From c40a227c2f4556c95fadf3c2e050bda6afd86cc3 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 21 Mar 2023 21:14:10 -0500 Subject: [PATCH] feat(shorebird_cli): app creation enhancements (#134) --- .../commands/apps/create_apps_command.dart | 39 ++++--------- .../src/commands/apps/list_apps_command.dart | 8 +-- .../lib/src/commands/init_command.dart | 29 ++++++++-- .../lib/src/shorebird_create_app_mixin.dart | 29 ++++++++++ .../apps/create_apps_command_test.dart | 37 ++++++++----- .../commands/apps/list_apps_command_test.dart | 9 ++- .../test/src/commands/init_command_test.dart | 55 ++++++++++++++++++- .../example/main.dart | 6 +- .../lib/src/code_push_client.dart | 15 +++-- .../test/src/code_push_client_test.dart | 29 +++++++--- .../lib/src/models/app.dart | 14 ++--- .../lib/src/models/app.g.dart | 18 ++---- .../lib/src/models/app_metadata.dart | 36 ++++++++++++ .../lib/src/models/app_metadata.g.dart | 39 +++++++++++++ .../lib/src/models/models.dart | 1 + .../test/src/models/app_metadata_test.dart | 19 +++++++ .../test/src/models/app_test.dart | 5 +- 17 files changed, 292 insertions(+), 96 deletions(-) create mode 100644 packages/shorebird_cli/lib/src/shorebird_create_app_mixin.dart create mode 100644 packages/shorebird_code_push_protocol/lib/src/models/app_metadata.dart create mode 100644 packages/shorebird_code_push_protocol/lib/src/models/app_metadata.g.dart create mode 100644 packages/shorebird_code_push_protocol/test/src/models/app_metadata_test.dart diff --git a/packages/shorebird_cli/lib/src/commands/apps/create_apps_command.dart b/packages/shorebird_cli/lib/src/commands/apps/create_apps_command.dart index d577e658..e06f8fcf 100644 --- a/packages/shorebird_cli/lib/src/commands/apps/create_apps_command.dart +++ b/packages/shorebird_cli/lib/src/commands/apps/create_apps_command.dart @@ -3,13 +3,16 @@ import 'dart:async'; import 'package:mason_logger/mason_logger.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; +import 'package:shorebird_cli/src/shorebird_create_app_mixin.dart'; +import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@template create_app_command} /// /// `shorebird apps create` /// Create a new app on Shorebird. /// {@endtemplate} -class CreateAppCommand extends ShorebirdCommand with ShorebirdConfigMixin { +class CreateAppCommand extends ShorebirdCommand + with ShorebirdConfigMixin, ShorebirdCreateAppMixin { /// {@macro create_app_command} CreateAppCommand({ required super.logger, @@ -17,10 +20,10 @@ class CreateAppCommand extends ShorebirdCommand with ShorebirdConfigMixin { super.auth, }) { argParser.addOption( - 'app-id', + 'app-name', help: ''' -The unique application identifier. -Defaults to the app_id in "shorebird.yaml".''', +The display name of your application. +Defaults to the name in "pubspec.yaml".''', ); } @@ -38,37 +41,17 @@ Defaults to the app_id in "shorebird.yaml".''', return ExitCode.noUser.code; } - final appIdArg = results['app-id'] as String?; - late final String appId; - - if (appIdArg == null) { - String? defaultAppId; - try { - defaultAppId = getShorebirdYaml()?.appId; - } catch (_) {} - - appId = logger.prompt( - '${lightGreen.wrap('?')} Enter the App ID', - defaultValue: defaultAppId, - ); - } else { - appId = appIdArg; - } - - final client = buildCodePushClient( - apiKey: session.apiKey, - hostedUri: hostedUri, - ); - + final appName = results['app-name'] as String?; + late final App app; try { - await client.createApp(appId: appId); + app = await createApp(appName: appName); } catch (error) { logger.err('$error'); return ExitCode.software.code; } logger.info( - '${lightGreen.wrap('Created new app: ${cyan.wrap(appId)}')}', + '''${lightGreen.wrap('Created ${cyan.wrap(app.displayName)} ${styleDim.wrap(cyan.wrap('(${app.id})'))}')}''', ); return ExitCode.success.code; diff --git a/packages/shorebird_cli/lib/src/commands/apps/list_apps_command.dart b/packages/shorebird_cli/lib/src/commands/apps/list_apps_command.dart index 7800c93a..52846b1b 100644 --- a/packages/shorebird_cli/lib/src/commands/apps/list_apps_command.dart +++ b/packages/shorebird_cli/lib/src/commands/apps/list_apps_command.dart @@ -40,7 +40,7 @@ class ListAppsCommand extends ShorebirdCommand with ShorebirdConfigMixin { hostedUri: hostedUri, ); - late final List apps; + late final List apps; try { apps = await client.getApps(); } catch (error) { @@ -61,12 +61,12 @@ class ListAppsCommand extends ShorebirdCommand with ShorebirdConfigMixin { } } -extension on App { +extension on AppMetadata { String prettyPrint() { final latestReleasePart = latestReleaseVersion != null ? 'v$latestReleaseVersion' : '(empty)'; final latestPatchPart = - latestPatchNumber != null ? '(patch #$latestPatchNumber)' : ''; - return '$appId: $latestReleasePart $latestPatchPart'; + latestPatchNumber != null ? ' (patch #$latestPatchNumber)' : ''; + return '$displayName: $latestReleasePart$latestPatchPart ($appId)'; } } diff --git a/packages/shorebird_cli/lib/src/commands/init_command.dart b/packages/shorebird_cli/lib/src/commands/init_command.dart index 28b3f2f2..c617af37 100644 --- a/packages/shorebird_cli/lib/src/commands/init_command.dart +++ b/packages/shorebird_cli/lib/src/commands/init_command.dart @@ -5,6 +5,8 @@ import 'package:path/path.dart' as p; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; +import 'package:shorebird_cli/src/shorebird_create_app_mixin.dart'; +import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:yaml/yaml.dart'; import 'package:yaml_edit/yaml_edit.dart'; @@ -13,9 +15,10 @@ import 'package:yaml_edit/yaml_edit.dart'; /// `shorebird init` /// Initialize Shorebird. /// {@endtemplate} -class InitCommand extends ShorebirdCommand with ShorebirdConfigMixin { +class InitCommand extends ShorebirdCommand + with ShorebirdConfigMixin, ShorebirdCreateAppMixin { /// {@macro init_command} - InitCommand({required super.logger}); + InitCommand({required super.logger, super.auth, super.buildCodePushClient}); @override String get description => 'Initialize Shorebird.'; @@ -25,6 +28,12 @@ class InitCommand extends ShorebirdCommand with ShorebirdConfigMixin { @override Future run() async { + final session = auth.currentSession; + if (session == null) { + logger.err('You must be logged in.'); + return ExitCode.noUser.code; + } + final progress = logger.progress('Initializing Shorebird'); try { if (!hasPubspecYaml) { @@ -36,14 +45,22 @@ class InitCommand extends ShorebirdCommand with ShorebirdConfigMixin { return ExitCode.software.code; } + late final App app; + try { + final pubspecYaml = getPubspecYaml()!; + app = await createApp(appName: pubspecYaml.name); + } catch (error) { + logger.err('$error'); + return ExitCode.software.code; + } + progress.update('Creating "shorebird.yaml"'); try { if (hasShorebirdYaml) { progress.update('"shorebird.yaml" already exists.'); } else { - final pubspecYaml = getPubspecYaml()!; - _addShorebirdYamlToProject(pubspecYaml.name); + _addShorebirdYamlToProject(app.id); progress.update('Generated a "shorebird.yaml".'); } } catch (error) { @@ -72,7 +89,6 @@ ${lightGreen.wrap('🐦 Shorebird initialized successfully!')} Reference the following commands to get started: -✨ To create a new app use: "${lightCyan.wrap('shorebird apps create')}". 🚙 To run your project use: "${lightCyan.wrap('shorebird run')}". 📦 To build your project use: "${lightCyan.wrap('shorebird build')}". 🚀 To publish an update use: "${lightCyan.wrap('shorebird publish')}". @@ -89,7 +105,8 @@ For more information about Shorebird, visit ${link(uri: Uri.parse('https://shore # This file is used to configure the Shorebird CLI. # Learn more at https://shorebird.dev -# This is the unique identifier for your app. +# This is the unique identifier assigned to your app. +# It is used by your app to request the correct patches from the Shorebird servers. app_id: $appId '''); diff --git a/packages/shorebird_cli/lib/src/shorebird_create_app_mixin.dart b/packages/shorebird_cli/lib/src/shorebird_create_app_mixin.dart new file mode 100644 index 00000000..72064261 --- /dev/null +++ b/packages/shorebird_cli/lib/src/shorebird_create_app_mixin.dart @@ -0,0 +1,29 @@ +import 'package:mason_logger/mason_logger.dart'; +import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; +import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; + +mixin ShorebirdCreateAppMixin on ShorebirdConfigMixin { + Future createApp({String? appName}) async { + late final String displayName; + if (appName == null) { + String? defaultAppName; + try { + defaultAppName = getPubspecYaml()?.name; + } catch (_) {} + + displayName = logger.prompt( + '${lightGreen.wrap('?')} How should we refer to this app?', + defaultValue: defaultAppName, + ); + } else { + displayName = appName; + } + + final client = buildCodePushClient( + apiKey: auth.currentSession!.apiKey, + hostedUri: hostedUri, + ); + + return client.createApp(displayName: displayName); + } +} diff --git a/packages/shorebird_cli/test/src/commands/apps/create_apps_command_test.dart b/packages/shorebird_cli/test/src/commands/apps/create_apps_command_test.dart index d6b13a35..49ac2d42 100644 --- a/packages/shorebird_cli/test/src/commands/apps/create_apps_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/apps/create_apps_command_test.dart @@ -18,7 +18,8 @@ class _MockLogger extends Mock implements Logger {} void main() { group('create', () { const apiKey = 'test-api-key'; - const appId = 'example'; + const appId = 'app-id'; + const displayName = 'Example App'; const session = Session(apiKey: apiKey); late ArgResults argResults; @@ -53,33 +54,43 @@ void main() { expect(result, ExitCode.noUser.code); }); - test('prompts for app-id when not provided', () async { - when(() => logger.prompt(any())).thenReturn(appId); + test('prompts for app name when not provided', () async { + when( + () => logger.prompt(any(), defaultValue: any(named: 'defaultValue')), + ).thenReturn(displayName); await command.run(); - verify(() => logger.prompt(any())).called(1); - verify(() => codePushClient.createApp(appId: appId)).called(1); + verify( + () => logger.prompt(any(), defaultValue: any(named: 'defaultValue')), + ).called(1); + verify( + () => codePushClient.createApp(displayName: displayName), + ).called(1); }); - test('uses provided app-id when provided', () async { - when(() => argResults['app-id']).thenReturn(appId); + test('uses provided app name when provided', () async { + when(() => argResults['app-name']).thenReturn(displayName); await command.run(); verifyNever(() => logger.prompt(any())); - verify(() => codePushClient.createApp(appId: appId)).called(1); + verify( + () => codePushClient.createApp(displayName: displayName), + ).called(1); }); test('returns success when app is created', () async { - when(() => argResults['app-id']).thenReturn(appId); + when(() => argResults['app-name']).thenReturn(displayName); when( - () => codePushClient.createApp(appId: appId), - ).thenAnswer((_) async {}); + () => codePushClient.createApp(displayName: displayName), + ).thenAnswer((_) async => const App(id: appId, displayName: displayName)); final result = await command.run(); expect(result, ExitCode.success.code); }); test('returns software error when app creation fails', () async { final error = Exception('oops'); - when(() => argResults['app-id']).thenReturn(appId); - when(() => codePushClient.createApp(appId: appId)).thenThrow(error); + when(() => argResults['app-name']).thenReturn(displayName); + when( + () => codePushClient.createApp(displayName: displayName), + ).thenThrow(error); final result = await command.run(); expect(result, ExitCode.software.code); verify(() => logger.err('$error')).called(1); diff --git a/packages/shorebird_cli/test/src/commands/apps/list_apps_command_test.dart b/packages/shorebird_cli/test/src/commands/apps/list_apps_command_test.dart index dc5a970f..2c64ade4 100644 --- a/packages/shorebird_cli/test/src/commands/apps/list_apps_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/apps/list_apps_command_test.dart @@ -59,8 +59,9 @@ void main() { test('returns ExitCode.success when apps are not empty', () async { final apps = [ - const App( - appId: 'shorebird-counter', + const AppMetadata( + appId: '30370f27-dbf1-4673-8b20-fb096e38dffa', + displayName: 'Shorebird Counter', latestReleaseVersion: '1.0.0', latestPatchNumber: 1, ), @@ -68,7 +69,9 @@ void main() { when(() => codePushClient.getApps()).thenAnswer((_) async => apps); expect(await command.run(), ExitCode.success.code); verify( - () => logger.info('shorebird-counter: v1.0.0 (patch #1)'), + () => logger.info( + '''Shorebird Counter: v1.0.0 (patch #1) (30370f27-dbf1-4673-8b20-fb096e38dffa)''', + ), ).called(1); }); }); diff --git a/packages/shorebird_cli/test/src/commands/init_command_test.dart b/packages/shorebird_cli/test/src/commands/init_command_test.dart index 900cf1d3..b1cc2194 100644 --- a/packages/shorebird_cli/test/src/commands/init_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/init_command_test.dart @@ -3,33 +3,66 @@ import 'dart:io'; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:path/path.dart' as p; +import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/auth/session.dart'; import 'package:shorebird_cli/src/commands/init_command.dart'; +import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; +class _MockAuth extends Mock implements Auth {} + +class _MockCodePushClient extends Mock implements CodePushClient {} + class _MockLogger extends Mock implements Logger {} class _MockProgress extends Mock implements Progress {} void main() { group('init', () { + const apiKey = 'test-api-key'; const version = '1.2.3'; const appId = 'test_app_id'; + const appName = 'test_app_name'; + const app = App(id: appId, displayName: appName); const pubspecYamlContent = ''' -name: $appId +name: $appName version: $version environment: sdk: ">=2.19.0 <3.0.0"'''; + const session = Session(apiKey: apiKey); + late Auth auth; + late CodePushClient codePushClient; late Logger logger; late Progress progress; late InitCommand command; setUp(() { + auth = _MockAuth(); + codePushClient = _MockCodePushClient(); logger = _MockLogger(); progress = _MockProgress(); - command = InitCommand(logger: logger); + command = InitCommand( + auth: auth, + buildCodePushClient: ({required String apiKey, Uri? hostedUri}) { + return codePushClient; + }, + logger: logger); + when(() => auth.currentSession).thenReturn(session); + when( + () => codePushClient.createApp(displayName: any(named: 'displayName')), + ).thenAnswer((_) async => app); when(() => logger.progress(any())).thenReturn(progress); + when( + () => logger.prompt(any(), defaultValue: any(named: 'defaultValue')), + ).thenReturn(appName); + }); + + test('returns no user error when not logged in', () async { + when(() => auth.currentSession).thenReturn(null); + final result = await command.run(); + expect(result, ExitCode.noUser.code); }); test('throws no input error when pubspec.yaml is not found.', () async { @@ -55,6 +88,24 @@ environment: expect(exitCode, ExitCode.software.code); }); + test('throws software error when error occurs creating app.', () async { + final error = Exception('oops'); + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync(pubspecYamlContent); + File(p.join(tempDir.path, 'shorebird.yaml')).createSync(); + when( + () => codePushClient.createApp(displayName: any(named: 'displayName')), + ).thenThrow(error); + final exitCode = await IOOverrides.runZoned( + command.run, + getCurrentDirectory: () => tempDir, + ); + verify(() => logger.err('$error')).called(1); + expect(exitCode, ExitCode.software.code); + }); + test('throws software error when shorebird.yaml is malformed.', () async { final tempDir = Directory.systemTemp.createTempSync(); File( diff --git a/packages/shorebird_code_push_client/example/main.dart b/packages/shorebird_code_push_client/example/main.dart index eaf2214f..7b6a2051 100644 --- a/packages/shorebird_code_push_client/example/main.dart +++ b/packages/shorebird_code_push_client/example/main.dart @@ -9,7 +9,9 @@ Future main() async { final engine = await client.downloadEngine('1837b5be5f'); // Create a new Shorebird application. - await client.createApp(appId: ''); + final app = await client.createApp( + displayName: '', // e.g. 'Shorebird Example' + ); // List all apps. final apps = await client.getApps(); @@ -18,7 +20,7 @@ Future main() async { await client.createPatch( artifactPath: '', // e.g. 'libapp.so' releaseVersion: '', // e.g. '1.0.0' - appId: '', // e.g. 'shorebird-example' + appId: app.id, // e.g. '30370f27-dbf1-4673-8b20-fb096e38dffa' channel: '', // e.g. 'stable' ); 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 71e22141..9fa1c2a5 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 @@ -46,17 +46,20 @@ class CodePushClient { Map get _apiKeyHeader => {'x-api-key': _apiKey}; - /// Create a new app with the provided [appId]. - Future createApp({required String appId}) async { + /// Create a new app with the provided [displayName]. + /// Returns the newly created app. + Future createApp({required String displayName}) async { final response = await _httpClient.post( Uri.parse('$hostedUri/api/v1/apps'), headers: _apiKeyHeader, - body: json.encode({'app_id': appId}), + body: json.encode({'display_name': displayName}), ); - if (response.statusCode != HttpStatus.created) { + if (response.statusCode != HttpStatus.ok) { throw _parseErrorResponse(response.body); } + final body = json.decode(response.body) as Map; + return App.fromJson(body); } /// Create a new patch. @@ -119,7 +122,7 @@ class CodePushClient { } /// List all apps for the current account. - Future> getApps() async { + Future> getApps() async { final response = await _httpClient.get( Uri.parse('$hostedUri/api/v1/apps'), headers: _apiKeyHeader, @@ -131,7 +134,7 @@ class CodePushClient { final apps = json.decode(response.body) as List; return apps - .map((app) => App.fromJson(app as Map)) + .map((app) => AppMetadata.fromJson(app as Map)) .toList(); } 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 b1cfab20..fb44261c 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 @@ -15,7 +15,8 @@ class _FakeBaseRequest extends Fake implements http.BaseRequest {} void main() { group('CodePushClient', () { const apiKey = 'api-key'; - const appId = 'shorebird-example'; + const appId = 'app-id'; + const displayName = 'shorebird-example'; const errorResponse = ErrorResponse( code: 'test_code', message: 'test message', @@ -66,7 +67,7 @@ void main() { ).thenAnswer((_) async => http.Response('', HttpStatus.badRequest)); expect( - codePushClient.createApp(appId: appId), + codePushClient.createApp(displayName: displayName), throwsA( isA().having( (e) => e.message, @@ -92,7 +93,7 @@ void main() { ); expect( - codePushClient.createApp(appId: appId), + codePushClient.createApp(displayName: displayName), throwsA( isA().having( (e) => e.message, @@ -110,9 +111,23 @@ void main() { headers: any(named: 'headers'), body: any(named: 'body'), ), - ).thenAnswer((_) async => http.Response('', HttpStatus.created)); + ).thenAnswer( + (_) async => http.Response( + json.encode(App(id: appId, displayName: displayName)), + HttpStatus.ok, + ), + ); - await codePushClient.createApp(appId: appId); + await expectLater( + codePushClient.createApp(displayName: displayName), + completion( + equals( + isA() + .having((a) => a.id, 'id', appId) + .having((a) => a.displayName, 'displayName', displayName), + ), + ), + ); final uri = verify( () => httpClient.post( @@ -393,8 +408,8 @@ void main() { test('completes when request succeeds (populated)', () async { final expected = [ - App(appId: 'shorebird-example'), - App(appId: 'shorebird-counter'), + AppMetadata(appId: '1', displayName: 'Shorebird Example'), + AppMetadata(appId: '2', displayName: 'Shorebird Clock'), ]; when( diff --git a/packages/shorebird_code_push_protocol/lib/src/models/app.dart b/packages/shorebird_code_push_protocol/lib/src/models/app.dart index 62ffd4ab..f4b7208e 100644 --- a/packages/shorebird_code_push_protocol/lib/src/models/app.dart +++ b/packages/shorebird_code_push_protocol/lib/src/models/app.dart @@ -9,9 +9,8 @@ part 'app.g.dart'; class App { /// {@macro app} const App({ - required this.appId, - this.latestReleaseVersion, - this.latestPatchNumber, + required this.id, + required this.displayName, }); /// Converts a Map to an [App] @@ -21,11 +20,8 @@ class App { Map toJson() => _$AppToJson(this); /// The ID of the app. - final String appId; + final String id; - /// The latest release version of the app. - final String? latestReleaseVersion; - - /// The latest patch number of the app. - final int? latestPatchNumber; + /// The display name of the app. + final String displayName; } diff --git a/packages/shorebird_code_push_protocol/lib/src/models/app.g.dart b/packages/shorebird_code_push_protocol/lib/src/models/app.g.dart index 987defd0..f4eb68dd 100644 --- a/packages/shorebird_code_push_protocol/lib/src/models/app.g.dart +++ b/packages/shorebird_code_push_protocol/lib/src/models/app.g.dart @@ -13,23 +13,15 @@ App _$AppFromJson(Map json) => $checkedCreate( json, ($checkedConvert) { final val = App( - appId: $checkedConvert('app_id', (v) => v as String), - latestReleaseVersion: - $checkedConvert('latest_release_version', (v) => v as String?), - latestPatchNumber: - $checkedConvert('latest_patch_number', (v) => v as int?), + id: $checkedConvert('id', (v) => v as String), + displayName: $checkedConvert('display_name', (v) => v as String), ); return val; }, - fieldKeyMap: const { - 'appId': 'app_id', - 'latestReleaseVersion': 'latest_release_version', - 'latestPatchNumber': 'latest_patch_number' - }, + fieldKeyMap: const {'displayName': 'display_name'}, ); Map _$AppToJson(App instance) => { - 'app_id': instance.appId, - 'latest_release_version': instance.latestReleaseVersion, - 'latest_patch_number': instance.latestPatchNumber, + 'id': instance.id, + 'display_name': instance.displayName, }; diff --git a/packages/shorebird_code_push_protocol/lib/src/models/app_metadata.dart b/packages/shorebird_code_push_protocol/lib/src/models/app_metadata.dart new file mode 100644 index 00000000..cf02ed9f --- /dev/null +++ b/packages/shorebird_code_push_protocol/lib/src/models/app_metadata.dart @@ -0,0 +1,36 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'app_metadata.g.dart'; + +/// {@template app_metadata} +/// A single app which contains zero or more releases. +/// {@endtemplate} +@JsonSerializable() +class AppMetadata { + /// {@macro app_metadata} + const AppMetadata({ + required this.appId, + required this.displayName, + this.latestReleaseVersion, + this.latestPatchNumber, + }); + + /// Converts a Map to an [AppMetadata] + factory AppMetadata.fromJson(Map json) => + _$AppMetadataFromJson(json); + + /// Converts a [AppMetadata] to a Map + Map toJson() => _$AppMetadataToJson(this); + + /// The ID of the app. + final String appId; + + /// The display name of the app. + final String displayName; + + /// The latest release version of the app. + final String? latestReleaseVersion; + + /// The latest patch number of the app. + final int? latestPatchNumber; +} diff --git a/packages/shorebird_code_push_protocol/lib/src/models/app_metadata.g.dart b/packages/shorebird_code_push_protocol/lib/src/models/app_metadata.g.dart new file mode 100644 index 00000000..b7f571f4 --- /dev/null +++ b/packages/shorebird_code_push_protocol/lib/src/models/app_metadata.g.dart @@ -0,0 +1,39 @@ +// 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 'app_metadata.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +AppMetadata _$AppMetadataFromJson(Map json) => $checkedCreate( + 'AppMetadata', + json, + ($checkedConvert) { + final val = AppMetadata( + appId: $checkedConvert('app_id', (v) => v as String), + displayName: $checkedConvert('display_name', (v) => v as String), + latestReleaseVersion: + $checkedConvert('latest_release_version', (v) => v as String?), + latestPatchNumber: + $checkedConvert('latest_patch_number', (v) => v as int?), + ); + return val; + }, + fieldKeyMap: const { + 'appId': 'app_id', + 'displayName': 'display_name', + 'latestReleaseVersion': 'latest_release_version', + 'latestPatchNumber': 'latest_patch_number' + }, + ); + +Map _$AppMetadataToJson(AppMetadata instance) => + { + 'app_id': instance.appId, + 'display_name': instance.displayName, + 'latest_release_version': instance.latestReleaseVersion, + 'latest_patch_number': instance.latestPatchNumber, + }; diff --git a/packages/shorebird_code_push_protocol/lib/src/models/models.dart b/packages/shorebird_code_push_protocol/lib/src/models/models.dart index be672ab6..1fb56cb4 100644 --- a/packages/shorebird_code_push_protocol/lib/src/models/models.dart +++ b/packages/shorebird_code_push_protocol/lib/src/models/models.dart @@ -1,4 +1,5 @@ export 'app.dart'; +export 'app_metadata.dart'; export 'error_response.dart'; export 'patch.dart'; export 'user.dart'; diff --git a/packages/shorebird_code_push_protocol/test/src/models/app_metadata_test.dart b/packages/shorebird_code_push_protocol/test/src/models/app_metadata_test.dart new file mode 100644 index 00000000..7dfaa2a0 --- /dev/null +++ b/packages/shorebird_code_push_protocol/test/src/models/app_metadata_test.dart @@ -0,0 +1,19 @@ +import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart'; +import 'package:test/test.dart'; + +void main() { + group('AppMetadata', () { + test('can be (de)serialized', () { + const appMetadata = AppMetadata( + appId: '30370f27-dbf1-4673-8b20-fb096e38dffa', + displayName: 'My App', + latestReleaseVersion: '1.0.0', + latestPatchNumber: 1, + ); + expect( + AppMetadata.fromJson(appMetadata.toJson()).toJson(), + equals(appMetadata.toJson()), + ); + }); + }); +} diff --git a/packages/shorebird_code_push_protocol/test/src/models/app_test.dart b/packages/shorebird_code_push_protocol/test/src/models/app_test.dart index a31c47bd..a1902a2c 100644 --- a/packages/shorebird_code_push_protocol/test/src/models/app_test.dart +++ b/packages/shorebird_code_push_protocol/test/src/models/app_test.dart @@ -5,9 +5,8 @@ void main() { group('App', () { test('can be (de)serialized', () { const app = App( - appId: 'my_app', - latestReleaseVersion: '1.0.0', - latestPatchNumber: 1, + id: '30370f27-dbf1-4673-8b20-fb096e38dffa', + displayName: 'My App', ); expect(App.fromJson(app.toJson()).toJson(), equals(app.toJson())); });