chore(shorebird_code_push_protocol): use Money object instead of int for dollar values (#820)
This commit is contained in:
@@ -42,8 +42,6 @@ class AccountUsageCommand extends ShorebirdCommand
|
||||
|
||||
extension on GetUsageResponse {
|
||||
String prettyPrint() {
|
||||
final currencyFormatter = NumberFormat('#,##0.00', 'en_US');
|
||||
|
||||
const cellStyle = CellStyle(
|
||||
paddingLeft: 1,
|
||||
paddingRight: 1,
|
||||
@@ -99,7 +97,7 @@ ${Table(
|
||||
${styleBold.wrap('${lightCyan.wrap(remainingPatchInstalls)} patch installs remaining in the current billing period.')}
|
||||
|
||||
Current Billing Period: ${lightCyan.wrap(DateFormat.yMMMd().format(currentPeriodStart))} - ${lightCyan.wrap(DateFormat.yMMMd().format(currentPeriodEnd))}
|
||||
Month-to-date cost: ${lightCyan.wrap('\$${currencyFormatter.format(currentPeriodCost / 100.0)}')}
|
||||
Month-to-date cost: ${lightCyan.wrap(currentPeriodCost.toString())}
|
||||
|
||||
${styleBold.wrap('*Usage data is not reported in real-time and may be delayed by up to 48 hours.')}''';
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ Defaults to the app_id in "shorebird.yaml".''',
|
||||
help: 'Release without confirmation if there are no errors.',
|
||||
negatable: false,
|
||||
);
|
||||
;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:shorebird_cli/src/shorebird_environment.dart';
|
||||
import 'package:shorebird_cli/src/process.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_environment.dart';
|
||||
import 'package:shorebird_cli/src/validators/validators.dart';
|
||||
import 'package:version/version.dart';
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ dependencies:
|
||||
json_annotation: ^4.8.0
|
||||
mason_logger: ^0.2.4
|
||||
meta: ^1.9.0
|
||||
money2: ^3.4.1
|
||||
path: ^1.8.3
|
||||
platform: ^3.1.0
|
||||
propertylistserialization: ^1.3.0
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:money2/money2.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:scoped/scoped.dart';
|
||||
@@ -1679,9 +1680,9 @@ Please bump your version number and try again.''',
|
||||
|
||||
test('returns usage when succeeds', () async {
|
||||
final usage = GetUsageResponse(
|
||||
plan: const ShorebirdPlan(
|
||||
plan: ShorebirdPlan(
|
||||
name: 'Hobby',
|
||||
monthlyCost: 0,
|
||||
monthlyCost: Money.fromIntWithCurrency(0, usd),
|
||||
patchInstallLimit: 1000,
|
||||
maxTeamSize: 1,
|
||||
),
|
||||
@@ -1693,7 +1694,7 @@ Please bump your version number and try again.''',
|
||||
),
|
||||
],
|
||||
patchInstallLimit: 20000,
|
||||
currentPeriodCost: 0,
|
||||
currentPeriodCost: Money.fromIntWithCurrency(0, usd),
|
||||
currentPeriodStart: DateTime(2023),
|
||||
currentPeriodEnd: DateTime(2023, 2),
|
||||
);
|
||||
|
||||
@@ -6,8 +6,8 @@ import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/auth/auth.dart' hide auth;
|
||||
import 'package:shorebird_cli/src/command_runner.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart' hide logger;
|
||||
import 'package:shorebird_cli/src/shorebird_environment.dart';
|
||||
import 'package:shorebird_cli/src/process.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_environment.dart';
|
||||
import 'package:shorebird_cli/src/version.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:money2/money2.dart';
|
||||
import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/auth/auth.dart';
|
||||
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
|
||||
@@ -68,9 +69,9 @@ void main() {
|
||||
|
||||
test('exits with code 0 when usage is fetched.', () async {
|
||||
final usage = GetUsageResponse(
|
||||
plan: const ShorebirdPlan(
|
||||
plan: ShorebirdPlan(
|
||||
name: 'Team',
|
||||
monthlyCost: 2000,
|
||||
monthlyCost: Money.fromIntWithCurrency(2000, usd),
|
||||
patchInstallLimit: 1000,
|
||||
maxTeamSize: 1,
|
||||
),
|
||||
@@ -87,7 +88,7 @@ void main() {
|
||||
),
|
||||
],
|
||||
patchInstallLimit: 20000,
|
||||
currentPeriodCost: 2000,
|
||||
currentPeriodCost: Money.fromIntWithCurrency(2000, usd),
|
||||
currentPeriodStart: DateTime(2023),
|
||||
currentPeriodEnd: DateTime(2023, 2),
|
||||
);
|
||||
@@ -129,9 +130,9 @@ ${styleBold.wrap('*Usage data is not reported in real-time and may be delayed by
|
||||
|
||||
test('exits with code 0 when usage is fetched (unlimited).', () async {
|
||||
final usage = GetUsageResponse(
|
||||
plan: const ShorebirdPlan(
|
||||
plan: ShorebirdPlan(
|
||||
name: 'Hobby',
|
||||
monthlyCost: 0,
|
||||
monthlyCost: Money.fromIntWithCurrency(0, usd),
|
||||
patchInstallLimit: 1000,
|
||||
maxTeamSize: 1,
|
||||
),
|
||||
@@ -147,7 +148,7 @@ ${styleBold.wrap('*Usage data is not reported in real-time and may be delayed by
|
||||
patchInstallCount: 42,
|
||||
),
|
||||
],
|
||||
currentPeriodCost: 0,
|
||||
currentPeriodCost: Money.fromIntWithCurrency(0, usd),
|
||||
currentPeriodStart: DateTime(2023),
|
||||
currentPeriodEnd: DateTime(2023, 2),
|
||||
);
|
||||
|
||||
@@ -28,7 +28,6 @@ void main() {
|
||||
startProcess = _MockProcess();
|
||||
shorebirdProcess = ShorebirdProcess(
|
||||
processWrapper: processWrapper,
|
||||
engineConfig: const EngineConfig.empty(),
|
||||
);
|
||||
|
||||
when(
|
||||
|
||||
@@ -5,8 +5,8 @@ import 'package:path/path.dart' as p;
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/platform.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_environment.dart';
|
||||
import 'package:shorebird_cli/src/process.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_environment.dart';
|
||||
import 'package:shorebird_cli/src/validators/validators.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ environment:
|
||||
|
||||
dependencies:
|
||||
http: ^1.0.0
|
||||
money2: ^3.4.1
|
||||
shorebird_code_push_protocol:
|
||||
path: ../shorebird_code_push_protocol
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:money2/money2.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
import 'package:test/test.dart';
|
||||
@@ -1930,9 +1931,9 @@ void main() {
|
||||
|
||||
test('completes when request succeeds', () async {
|
||||
final expected = GetUsageResponse(
|
||||
plan: const ShorebirdPlan(
|
||||
plan: ShorebirdPlan(
|
||||
name: 'Hobby',
|
||||
monthlyCost: 0,
|
||||
monthlyCost: Money.fromIntWithCurrency(0, usd),
|
||||
patchInstallLimit: 1000,
|
||||
maxTeamSize: 1,
|
||||
),
|
||||
@@ -1944,7 +1945,7 @@ void main() {
|
||||
)
|
||||
],
|
||||
patchInstallLimit: 1337,
|
||||
currentPeriodCost: 0,
|
||||
currentPeriodCost: Money.fromIntWithCurrency(0, usd),
|
||||
currentPeriodStart: DateTime(2023),
|
||||
currentPeriodEnd: DateTime(2023, 2),
|
||||
);
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export 'money_converter.dart';
|
||||
export 'timestamp_converter.dart';
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:money2/money2.dart';
|
||||
|
||||
/// The US dollar.
|
||||
Currency get usd => Currency.create('USD', 2);
|
||||
|
||||
/// {@template money_converter}
|
||||
/// Converts between [Money] and [String].
|
||||
/// {@endtemplate}
|
||||
class MoneyConverter implements JsonConverter<Money, String> {
|
||||
/// {@macro money_converter}
|
||||
const MoneyConverter();
|
||||
|
||||
@override
|
||||
Money fromJson(String cents) => Money.parseWithCurrency(cents, usd);
|
||||
|
||||
@override
|
||||
String toJson(Money money) => money.minorUnits.toString();
|
||||
}
|
||||
+3
-1
@@ -1,4 +1,5 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:money2/money2.dart';
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
|
||||
part 'get_usage_response.g.dart';
|
||||
@@ -39,7 +40,8 @@ class GetUsageResponse {
|
||||
|
||||
/// The total cost so far for the current billing period. Includes base
|
||||
/// monthly cost and any overages.
|
||||
final int currentPeriodCost;
|
||||
@MoneyConverter()
|
||||
final Money currentPeriodCost;
|
||||
|
||||
/// The upper limit of patch installs for the current billing period.
|
||||
/// If `null`, there is no limit.
|
||||
|
||||
+4
-3
@@ -25,8 +25,8 @@ GetUsageResponse _$GetUsageResponseFromJson(Map<String, dynamic> json) =>
|
||||
'current_period_start', (v) => DateTime.parse(v as String)),
|
||||
currentPeriodEnd: $checkedConvert(
|
||||
'current_period_end', (v) => DateTime.parse(v as String)),
|
||||
currentPeriodCost:
|
||||
$checkedConvert('current_period_cost', (v) => v as int),
|
||||
currentPeriodCost: $checkedConvert('current_period_cost',
|
||||
(v) => const MoneyConverter().fromJson(v as String)),
|
||||
patchInstallLimit:
|
||||
$checkedConvert('patch_install_limit', (v) => v as int?),
|
||||
);
|
||||
@@ -46,7 +46,8 @@ Map<String, dynamic> _$GetUsageResponseToJson(GetUsageResponse instance) =>
|
||||
'apps': instance.apps.map((e) => e.toJson()).toList(),
|
||||
'current_period_start': instance.currentPeriodStart.toIso8601String(),
|
||||
'current_period_end': instance.currentPeriodEnd.toIso8601String(),
|
||||
'current_period_cost': instance.currentPeriodCost,
|
||||
'current_period_cost':
|
||||
const MoneyConverter().toJson(instance.currentPeriodCost),
|
||||
'patch_install_limit': instance.patchInstallLimit,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:money2/money2.dart';
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
|
||||
part 'shorebird_plan.g.dart';
|
||||
@@ -25,8 +26,9 @@ class ShorebirdPlan {
|
||||
/// The name of the plan.
|
||||
final String name;
|
||||
|
||||
/// Billing rate, in cents.
|
||||
final int monthlyCost;
|
||||
/// Monthly billing rate.
|
||||
@MoneyConverter()
|
||||
final Money monthlyCost;
|
||||
|
||||
/// The number of patch installs allowed per billing period. This will be null
|
||||
/// for accounts with unlimited patch installs.
|
||||
|
||||
@@ -15,7 +15,8 @@ ShorebirdPlan _$ShorebirdPlanFromJson(Map<String, dynamic> json) =>
|
||||
($checkedConvert) {
|
||||
final val = ShorebirdPlan(
|
||||
name: $checkedConvert('name', (v) => v as String),
|
||||
monthlyCost: $checkedConvert('monthly_cost', (v) => v as int),
|
||||
monthlyCost: $checkedConvert('monthly_cost',
|
||||
(v) => const MoneyConverter().fromJson(v as String)),
|
||||
patchInstallLimit:
|
||||
$checkedConvert('patch_install_limit', (v) => v as int?),
|
||||
maxTeamSize: $checkedConvert('max_team_size', (v) => v as int?),
|
||||
@@ -32,7 +33,7 @@ ShorebirdPlan _$ShorebirdPlanFromJson(Map<String, dynamic> json) =>
|
||||
Map<String, dynamic> _$ShorebirdPlanToJson(ShorebirdPlan instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'monthly_cost': instance.monthlyCost,
|
||||
'monthly_cost': const MoneyConverter().toJson(instance.monthlyCost),
|
||||
'patch_install_limit': instance.patchInstallLimit,
|
||||
'max_team_size': instance.maxTeamSize,
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ environment:
|
||||
|
||||
dependencies:
|
||||
json_annotation: ^4.8.0
|
||||
money2: ^3.4.1
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.0.0
|
||||
|
||||
+4
-3
@@ -1,3 +1,4 @@
|
||||
import 'package:money2/money2.dart';
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
@@ -5,9 +6,9 @@ void main() {
|
||||
group(GetUsageResponse, () {
|
||||
test('can be (de)serialized', () {
|
||||
final response = GetUsageResponse(
|
||||
plan: const ShorebirdPlan(
|
||||
plan: ShorebirdPlan(
|
||||
name: 'Hobby',
|
||||
monthlyCost: 0,
|
||||
monthlyCost: Money.fromIntWithCurrency(0, usd),
|
||||
patchInstallLimit: 1000,
|
||||
maxTeamSize: 1,
|
||||
),
|
||||
@@ -15,7 +16,7 @@ void main() {
|
||||
const AppUsage(id: 'app-id', name: 'My app', patchInstallCount: 1337),
|
||||
],
|
||||
patchInstallLimit: 42,
|
||||
currentPeriodCost: 0,
|
||||
currentPeriodCost: Money.fromIntWithCurrency(0, usd),
|
||||
currentPeriodStart: DateTime(2021),
|
||||
currentPeriodEnd: DateTime(2021, 1, 2),
|
||||
);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'package:money2/money2.dart';
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group(ShorebirdPlan, () {
|
||||
test('can be (de)serialized', () {
|
||||
const plan = ShorebirdPlan(
|
||||
final plan = ShorebirdPlan(
|
||||
name: 'Hobby',
|
||||
monthlyCost: 0,
|
||||
monthlyCost: Money.fromIntWithCurrency(0, usd),
|
||||
patchInstallLimit: 1000,
|
||||
maxTeamSize: 1,
|
||||
);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import 'package:money2/money2.dart';
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group(Subscription, () {
|
||||
const plan = ShorebirdPlan(
|
||||
final plan = ShorebirdPlan(
|
||||
name: 'Hobby',
|
||||
monthlyCost: 0,
|
||||
monthlyCost: Money.fromIntWithCurrency(0, usd),
|
||||
patchInstallLimit: 1000,
|
||||
maxTeamSize: 1,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user