From 6845de7fcd964a74fdfb2201d8b32fbfe1b76f54 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 3 Oct 2023 11:25:59 -0500 Subject: [PATCH] chore(shorebird_cli): remove `shorebird account downgrade` command (#1347) --- .../shorebird_cli/lib/src/command_runner.dart | 1 - .../lib/src/commands/account/account.dart | 2 - .../src/commands/account/account_command.dart | 19 -- .../account/downgrade_account_command.dart | 82 -------- .../lib/src/commands/commands.dart | 1 - .../downgrade_account_command_test.dart | 191 ------------------ .../lib/src/code_push_client.dart | 13 -- .../test/src/code_push_client_test.dart | 58 ------ .../cancel_subscription_response.dart | 24 --- .../cancel_subscription_response.g.dart | 31 --- .../lib/src/messages/messages.dart | 1 - .../cancel_subscription_response_test.dart | 16 -- .../check_for_patches_response_test.dart | 2 +- 13 files changed, 1 insertion(+), 440 deletions(-) delete mode 100644 packages/shorebird_cli/lib/src/commands/account/account.dart delete mode 100644 packages/shorebird_cli/lib/src/commands/account/account_command.dart delete mode 100644 packages/shorebird_cli/lib/src/commands/account/downgrade_account_command.dart delete mode 100644 packages/shorebird_cli/test/src/commands/account/downgrade_account_command_test.dart delete mode 100644 packages/shorebird_code_push_protocol/lib/src/messages/cancel_subscription/cancel_subscription_response.dart delete mode 100644 packages/shorebird_code_push_protocol/lib/src/messages/cancel_subscription/cancel_subscription_response.g.dart delete mode 100644 packages/shorebird_code_push_protocol/test/src/messages/cancel_subscription/cancel_subscription_response_test.dart diff --git a/packages/shorebird_cli/lib/src/command_runner.dart b/packages/shorebird_cli/lib/src/command_runner.dart index d43a1f4c..492d3b37 100644 --- a/packages/shorebird_cli/lib/src/command_runner.dart +++ b/packages/shorebird_cli/lib/src/command_runner.dart @@ -56,7 +56,6 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner { 'are building Flutter locally.', ); - addCommand(AccountCommand()); addCommand(AppsCommand()); addCommand(BuildCommand()); addCommand(CacheCommand()); diff --git a/packages/shorebird_cli/lib/src/commands/account/account.dart b/packages/shorebird_cli/lib/src/commands/account/account.dart deleted file mode 100644 index 1a61efa5..00000000 --- a/packages/shorebird_cli/lib/src/commands/account/account.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'account_command.dart'; -export 'downgrade_account_command.dart'; diff --git a/packages/shorebird_cli/lib/src/commands/account/account_command.dart b/packages/shorebird_cli/lib/src/commands/account/account_command.dart deleted file mode 100644 index be7fb46f..00000000 --- a/packages/shorebird_cli/lib/src/commands/account/account_command.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:shorebird_cli/src/command.dart'; -import 'package:shorebird_cli/src/commands/commands.dart'; - -/// {@template account_command} -/// `shorebird account` -/// Manage your Shorebird account. -/// {@endtemplate} -class AccountCommand extends ShorebirdCommand { - /// {@macro account_command} - AccountCommand() { - addSubcommand(DowngradeAccountCommand()); - } - - @override - String get name => 'account'; - - @override - String get description => 'Manage your Shorebird account.'; -} diff --git a/packages/shorebird_cli/lib/src/commands/account/downgrade_account_command.dart b/packages/shorebird_cli/lib/src/commands/account/downgrade_account_command.dart deleted file mode 100644 index 002c78f9..00000000 --- a/packages/shorebird_cli/lib/src/commands/account/downgrade_account_command.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'dart:async'; - -import 'package:intl/intl.dart'; -import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; -import 'package:shorebird_cli/src/command.dart'; -import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_validator.dart'; -import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; - -class DowngradeAccountCommand extends ShorebirdCommand { - DowngradeAccountCommand(); - - @override - String get name => 'downgrade'; - - @override - String get description => 'Downgrade your Shorebird account.'; - - @override - Future run() async { - try { - await shorebirdValidator.validatePreconditions( - checkUserIsAuthenticated: true, - ); - } on PreconditionFailedException catch (e) { - return e.exitCode.code; - } - - final User user; - try { - final currentUser = - await codePushClientWrapper.codePushClient.getCurrentUser(); - if (currentUser == null) { - throw Exception('Failed to retrieve user information.'); - } - user = currentUser; - } catch (error) { - logger.err(error.toString()); - return ExitCode.software.code; - } - - if (!user.hasActiveSubscription) { - logger.err('You do not have a "teams" subscription.'); - return ExitCode.software.code; - } - - final confirm = logger.confirm( - red.wrap( - '''This will downgrade your Shorebird plan to the "hobby" tier. Are you sure?''', - ), - ); - - if (!confirm) { - logger.info('Aborting.'); - return ExitCode.success.code; - } - - final progress = logger.progress('Downgrading your plan'); - - final DateTime cancellationDate; - try { - cancellationDate = - await codePushClientWrapper.codePushClient.cancelSubscription(); - } catch (error) { - progress.fail('Failed to downgrade plan. Error: $error'); - return ExitCode.software.code; - } - - final formattedDate = DateFormat.yMMMMd().format(cancellationDate); - progress.complete( - ''' -Your plan has been downgraded. - -Note: Your current plan will continue until $formattedDate, after which your account will be on the "hobby" tier. - -Apps on devices you've built with Shorebird will continue to function normally, but will be subject to the limits of the "hobby" tier.''', - ); - - return ExitCode.success.code; - } -} diff --git a/packages/shorebird_cli/lib/src/commands/commands.dart b/packages/shorebird_cli/lib/src/commands/commands.dart index f87a1721..98a95440 100644 --- a/packages/shorebird_cli/lib/src/commands/commands.dart +++ b/packages/shorebird_cli/lib/src/commands/commands.dart @@ -1,4 +1,3 @@ -export 'account/account.dart'; export 'apps/apps.dart'; export 'build/build_command.dart'; export 'cache/cache.dart'; diff --git a/packages/shorebird_cli/test/src/commands/account/downgrade_account_command_test.dart b/packages/shorebird_cli/test/src/commands/account/downgrade_account_command_test.dart deleted file mode 100644 index 0a75f98c..00000000 --- a/packages/shorebird_cli/test/src/commands/account/downgrade_account_command_test.dart +++ /dev/null @@ -1,191 +0,0 @@ -import 'package:mason_logger/mason_logger.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; -import 'package:shorebird_cli/src/commands/account/account.dart'; -import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_validator.dart'; -import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; -import 'package:test/test.dart'; - -import '../../mocks.dart'; - -void main() { - group(DowngradeAccountCommand, () { - const noSubscriptionUser = User(id: 1, email: 'tester1@shorebird.dev'); - const subscriptionUser = User( - id: 2, - email: 'tester2@shorebird.dev', - hasActiveSubscription: true, - ); - - late CodePushClientWrapper codePushClientWrapper; - late CodePushClient codePushClient; - late Logger logger; - late Progress progress; - late ShorebirdValidator shorebirdValidator; - late DowngradeAccountCommand command; - - R runWithOverrides(R Function() body) { - return runScoped( - body, - values: { - codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), - loggerRef.overrideWith(() => logger), - shorebirdValidatorRef.overrideWith(() => shorebirdValidator), - }, - ); - } - - setUp(() { - codePushClientWrapper = MockCodePushClientWrapper(); - codePushClient = MockCodePushClient(); - logger = MockLogger(); - progress = MockProgress(); - shorebirdValidator = MockShorebirdValidator(); - - when( - () => codePushClientWrapper.codePushClient, - ).thenReturn(codePushClient); - when(() => logger.progress(any())).thenReturn(progress); - when( - () => shorebirdValidator.validatePreconditions( - checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), - ), - ).thenAnswer((_) async {}); - - command = runWithOverrides(DowngradeAccountCommand.new); - }); - - test('has a description', () { - expect(command.description, isNotEmpty); - }); - - test('exits when validation fails', () async { - final exception = ValidationFailedException(); - when( - () => shorebirdValidator.validatePreconditions( - checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), - ), - ).thenThrow(exception); - await expectLater( - runWithOverrides(command.run), - completion(equals(exception.exitCode.code)), - ); - verify( - () => shorebirdValidator.validatePreconditions( - checkUserIsAuthenticated: true, - ), - ).called(1); - }); - - test('prints an error if fetch current user fails', () async { - when(() => codePushClient.getCurrentUser()).thenThrow( - Exception('an error occurred'), - ); - - final result = await runWithOverrides(command.run); - - expect(result, ExitCode.software.code); - verify( - () => logger.err(any(that: contains('an error occurred'))), - ).called(1); - }); - - test('prints an error if fetch current user returns null', () async { - when(() => codePushClient.getCurrentUser()).thenAnswer((_) async => null); - - final result = await runWithOverrides(command.run); - - expect(result, ExitCode.software.code); - verify( - () => logger.err( - any(that: contains('Failed to retrieve user information')), - ), - ).called(1); - }); - - test( - 'prints an error if the user does not have an active subscription', - () async { - when( - () => codePushClient.getCurrentUser(), - ).thenAnswer((_) async => noSubscriptionUser); - - final result = await runWithOverrides(command.run); - - expect(result, ExitCode.software.code); - verify( - () => logger.err( - any(that: contains('You do not have a "teams" subscription')), - ), - ).called(1); - }, - ); - - test('exits successfully if the user opts not to cancel', () async { - when( - () => codePushClient.getCurrentUser(), - ).thenAnswer((_) async => subscriptionUser); - when( - () => logger.confirm(any(that: contains('Are you sure?'))), - ).thenReturn(false); - - final result = await runWithOverrides(command.run); - - expect(result, ExitCode.success.code); - verify(() => logger.info('Aborting.')).called(1); - }); - - test('prints an error if call to cancel subscription fails', () async { - when( - () => codePushClient.getCurrentUser(), - ).thenAnswer((_) async => subscriptionUser); - when( - () => logger.confirm(any(that: contains('Are you sure?'))), - ).thenReturn(true); - when(() => codePushClient.cancelSubscription()).thenThrow( - Exception('an error occurred'), - ); - - final result = await runWithOverrides(command.run); - - expect(result, ExitCode.software.code); - verify( - () => progress.fail(any(that: contains('an error occurred'))), - ).called(1); - }); - - test('exits successfully on subscription cancellation', () async { - // Fri Apr 14 2023 07:00:00 GMT+0000 - const cancellationTimestamp = 1681455600; - when( - () => codePushClient.getCurrentUser(), - ).thenAnswer((_) async => subscriptionUser); - when( - () => logger.confirm(any(that: contains('Are you sure?'))), - ).thenReturn(true); - - when(() => codePushClient.cancelSubscription()).thenAnswer( - (_) async => DateTime.fromMillisecondsSinceEpoch( - cancellationTimestamp * 1000, - ), - ); - - final result = await runWithOverrides(command.run); - - expect(result, ExitCode.success.code); - - verify( - () => progress.complete( - any( - that: stringContainsInOrder([ - 'Your plan has been downgraded.', - '''Note: Your current plan will continue until April 14, 2023, after which your account will be on the "hobby" tier.''', - ]), - ), - ), - ).called(1); - }); - }); -} diff --git a/packages/shorebird_code_push_client/lib/src/code_push_client.dart b/packages/shorebird_code_push_client/lib/src/code_push_client.dart index e13148a3..b04996ff 100644 --- a/packages/shorebird_code_push_client/lib/src/code_push_client.dart +++ b/packages/shorebird_code_push_client/lib/src/code_push_client.dart @@ -498,19 +498,6 @@ class CodePushClient { } } - /// Cancels the current user's subscription. - Future cancelSubscription() async { - final response = await _httpClient.delete(Uri.parse('$_v1/subscriptions')); - - if (response.statusCode != HttpStatus.ok) { - throw _parseErrorResponse(response.statusCode, response.body); - } - - final json = jsonDecode(response.body) as Map; - final timestamp = json['expiration_date'] as int; - return DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); - } - /// Closes the client. void close() => _httpClient.close(); diff --git a/packages/shorebird_code_push_client/test/src/code_push_client_test.dart b/packages/shorebird_code_push_client/test/src/code_push_client_test.dart index 3e7cbf5c..7623eb26 100644 --- a/packages/shorebird_code_push_client/test/src/code_push_client_test.dart +++ b/packages/shorebird_code_push_client/test/src/code_push_client_test.dart @@ -2074,64 +2074,6 @@ void main() { }); }); - group('cancelSubscription', () { - late Uri uri; - - setUp(() { - uri = Uri.parse('${codePushClient.hostedUri}/api/v1/subscriptions'); - }); - - test('makes the correct request', () async { - codePushClient.cancelSubscription().ignore(); - final request = verify(() => httpClient.send(captureAny())) - .captured - .single as http.BaseRequest; - expect(request.method, equals('DELETE')); - expect(request.url, equals(v1('subscriptions'))); - expect(request.hasStandardHeaders, isTrue); - }); - - test('throws an exception if the http request fails', () { - when(() => httpClient.send(any())).thenAnswer( - (_) async => http.StreamedResponse( - const Stream.empty(), - HttpStatus.badRequest, - ), - ); - - expect( - codePushClient.cancelSubscription(), - throwsA( - isA().having( - (e) => e.message, - 'message', - CodePushClient.unknownErrorMessage, - ), - ), - ); - }); - - test('completes when request succeeds', () async { - const timestamp = 1681455600; - when(() => httpClient.send(any())).thenAnswer( - (_) async => http.StreamedResponse( - Stream.value( - utf8.encode(json.encode({'expiration_date': 1681455600})), - ), - HttpStatus.ok, - ), - ); - - final response = await codePushClient.cancelSubscription(); - - expect(response.millisecondsSinceEpoch, timestamp * 1000); - final request = verify(() => httpClient.send(captureAny())) - .captured - .single as http.BaseRequest; - expect(request.url, equals(uri)); - }); - }); - group('close', () { test('closes the underlying client', () { codePushClient.close(); diff --git a/packages/shorebird_code_push_protocol/lib/src/messages/cancel_subscription/cancel_subscription_response.dart b/packages/shorebird_code_push_protocol/lib/src/messages/cancel_subscription/cancel_subscription_response.dart deleted file mode 100644 index 787596d5..00000000 --- a/packages/shorebird_code_push_protocol/lib/src/messages/cancel_subscription/cancel_subscription_response.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; -import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart'; - -part 'cancel_subscription_response.g.dart'; - -/// {@template cancel_subscription_response} -/// The request body for DELETE /api/v1/subscriptions. -/// {@endtemplate} -@JsonSerializable() -class CancelSubscriptionResponse { - /// {@macro cancel_subscription_response} - CancelSubscriptionResponse({required this.expirationDate}); - - /// Converts a JSON object to a [CancelSubscriptionResponse]. - factory CancelSubscriptionResponse.fromJson(Json json) => - _$CancelSubscriptionResponseFromJson(json); - - /// Converts a [CancelSubscriptionResponse] to a JSON object. - Json toJson() => _$CancelSubscriptionResponseToJson(this); - - /// When this subscription will not longer be active. - @TimestampConverter() - final DateTime expirationDate; -} diff --git a/packages/shorebird_code_push_protocol/lib/src/messages/cancel_subscription/cancel_subscription_response.g.dart b/packages/shorebird_code_push_protocol/lib/src/messages/cancel_subscription/cancel_subscription_response.g.dart deleted file mode 100644 index 6f535595..00000000 --- a/packages/shorebird_code_push_protocol/lib/src/messages/cancel_subscription/cancel_subscription_response.g.dart +++ /dev/null @@ -1,31 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ignore_for_file: implicit_dynamic_parameter, require_trailing_commas, cast_nullable_to_non_nullable, lines_longer_than_80_chars - -part of 'cancel_subscription_response.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -CancelSubscriptionResponse _$CancelSubscriptionResponseFromJson( - Map json) => - $checkedCreate( - 'CancelSubscriptionResponse', - json, - ($checkedConvert) { - final val = CancelSubscriptionResponse( - expirationDate: $checkedConvert('expiration_date', - (v) => const TimestampConverter().fromJson(v as int)), - ); - return val; - }, - fieldKeyMap: const {'expirationDate': 'expiration_date'}, - ); - -Map _$CancelSubscriptionResponseToJson( - CancelSubscriptionResponse instance) => - { - 'expiration_date': - const TimestampConverter().toJson(instance.expirationDate), - }; diff --git a/packages/shorebird_code_push_protocol/lib/src/messages/messages.dart b/packages/shorebird_code_push_protocol/lib/src/messages/messages.dart index 7d582830..e27075bb 100644 --- a/packages/shorebird_code_push_protocol/lib/src/messages/messages.dart +++ b/packages/shorebird_code_push_protocol/lib/src/messages/messages.dart @@ -1,4 +1,3 @@ -export 'cancel_subscription/cancel_subscription_response.dart'; export 'check_for_patches/check_for_patches.dart'; export 'create_app/create_app.dart'; export 'create_app_collaborator/create_app_collaborator.dart'; diff --git a/packages/shorebird_code_push_protocol/test/src/messages/cancel_subscription/cancel_subscription_response_test.dart b/packages/shorebird_code_push_protocol/test/src/messages/cancel_subscription/cancel_subscription_response_test.dart deleted file mode 100644 index 8cc11d41..00000000 --- a/packages/shorebird_code_push_protocol/test/src/messages/cancel_subscription/cancel_subscription_response_test.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart'; -import 'package:test/test.dart'; - -void main() { - group(CancelSubscriptionResponse, () { - test('can be (de)serialized', () { - final response = CancelSubscriptionResponse( - expirationDate: DateTime.now(), - ); - expect( - CancelSubscriptionResponse.fromJson(response.toJson()).toJson(), - equals(response.toJson()), - ); - }); - }); -} diff --git a/packages/shorebird_code_push_protocol/test/src/messages/check_for_patches/check_for_patches_response_test.dart b/packages/shorebird_code_push_protocol/test/src/messages/check_for_patches/check_for_patches_response_test.dart index d7157e4f..52332781 100644 --- a/packages/shorebird_code_push_protocol/test/src/messages/check_for_patches/check_for_patches_response_test.dart +++ b/packages/shorebird_code_push_protocol/test/src/messages/check_for_patches/check_for_patches_response_test.dart @@ -2,7 +2,7 @@ import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart'; import 'package:test/test.dart'; void main() { - group(CancelSubscriptionResponse, () { + group(CheckForPatchesRequest, () { test('can be serialized to json without patch metadata', () { const response = CheckForPatchesResponse(patchAvailable: true); expect(response.toJson(), {'patch_available': true, 'patch': null});