diff --git a/packages/shorebird_cli/lib/src/commands/collaborators/add_collaborators_command.dart b/packages/shorebird_cli/lib/src/commands/collaborators/add_collaborators_command.dart index ce646d82..0936ec44 100644 --- a/packages/shorebird_cli/lib/src/commands/collaborators/add_collaborators_command.dart +++ b/packages/shorebird_cli/lib/src/commands/collaborators/add_collaborators_command.dart @@ -7,6 +7,7 @@ import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; import 'package:shorebird_cli/src/shorebird_environment.dart'; import 'package:shorebird_cli/src/shorebird_validation_mixin.dart'; +import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@template add_collaborators_command} /// `shorebird collaborators add` @@ -86,6 +87,12 @@ ${styleBold.wrap(lightGreen.wrap('🚀 Ready to add a new collaborator!'))} try { await client.createCollaborator(appId: appId, email: collaborator); progress.complete(); + } on CodePushForbiddenException { + progress.fail(); + logger.err( + 'You do not have permission to add collaborators to this app.', + ); + return ExitCode.software.code; } catch (error) { progress.fail(); logger.err('$error'); diff --git a/packages/shorebird_cli/test/src/commands/collaborators/add_collaborators_command_test.dart b/packages/shorebird_cli/test/src/commands/collaborators/add_collaborators_command_test.dart index d0e7e2da..a0524149 100644 --- a/packages/shorebird_cli/test/src/commands/collaborators/add_collaborators_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/collaborators/add_collaborators_command_test.dart @@ -104,6 +104,26 @@ void main() { ); }); + test( + '''exits with code 70 if user does not have permission to add collaborators''', + () async { + final error = CodePushForbiddenException( + message: 'oops something went wrong', + ); + when( + () => codePushClient.createCollaborator( + appId: any(named: 'appId'), + email: any(named: 'email'), + ), + ).thenThrow(error); + expect(await runWithOverrides(command.run), ExitCode.software.code); + verify( + () => logger.err( + 'You do not have permission to add collaborators to this app.', + ), + ).called(1); + }); + test( 'returns ExitCode.software ' 'when adding a collaborator fails', () async { 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 56b7bd56..9ccf2795 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 @@ -22,6 +22,14 @@ class CodePushException implements Exception { String toString() => '$message${details != null ? '\n$details' : ''}'; } +/// {@template code_push_forbidden_exception} +/// Exception thrown when a 403 response is received. +/// {@endtemplate} +class CodePushForbiddenException extends CodePushException { + /// {@macro code_push_forbidden_exception} + CodePushForbiddenException({required super.message, super.details}); +} + /// {@template code_push_conflict_exception} /// Exception thrown when a 409 response is received. /// {@endtemplate} @@ -494,6 +502,7 @@ class CodePushClient { HttpStatus.conflict => CodePushConflictException.new, HttpStatus.notFound => CodePushNotFoundException.new, HttpStatus.upgradeRequired => CodePushUpgradeRequiredException.new, + HttpStatus.forbidden => CodePushForbiddenException.new, _ => CodePushException.new, }; 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 b311fc1c..09fdc4f6 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 @@ -108,6 +108,21 @@ void main() { ); }); + test('throws a permission exception if the http response code is 403', + () async { + when(() => httpClient.send(any())).thenAnswer( + (_) async => http.StreamedResponse( + Stream.value(utf8.encode(json.encode(errorResponse.toJson()))), + HttpStatus.forbidden, + ), + ); + + expect( + codePushClient.createCollaborator(appId: appId, email: email), + throwsA(isA()), + ); + }); + test('throws an exception if the http request fails', () async { when(() => httpClient.send(any())).thenAnswer( (_) async => http.StreamedResponse(