feat: allow user to choose organization in shorebird init (#2514)

This commit is contained in:
Bryan Oltman
2024-10-08 15:46:19 -04:00
committed by GitHub
parent 78faea7030
commit 238837f60d
10 changed files with 481 additions and 34 deletions
+1 -1
View File
@@ -586,4 +586,4 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.5.0-259.0.dev <4.0.0"
dart: ">=3.5.0 <4.0.0"
@@ -80,7 +80,10 @@ class CodePushClientWrapper {
final CodePushClient codePushClient;
Future<App> createApp({String? appName}) async {
Future<App> createApp({
required int organizationId,
String? appName,
}) async {
late final String displayName;
if (appName == null) {
String? defaultAppName;
@@ -96,7 +99,35 @@ class CodePushClientWrapper {
displayName = appName;
}
return codePushClient.createApp(displayName: displayName);
return codePushClient.createApp(
displayName: displayName,
organizationId: organizationId,
);
}
/// Returns the currently logged in user, or null if no user is logged in.
Future<PrivateUser?> getCurrentUser() async {
final progress = logger.progress('Fetching user');
try {
final user = await codePushClient.getCurrentUser();
progress.complete();
return user;
} catch (error) {
_handleErrorAndExit(error, progress: progress);
}
}
Future<List<OrganizationMembership>> getOrganizationMemberships() async {
final progress = logger.progress('Fetching organizations');
final List<OrganizationMembership> memberships;
try {
memberships = await codePushClient.getOrganizationMemberships();
progress.complete();
} catch (error) {
_handleErrorAndExit(error, progress: progress);
}
return memberships;
}
Future<List<AppMetadata>> getApps() async {
@@ -5,6 +5,7 @@ import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/executables/executables.dart';
import 'package:shorebird_cli/src/extensions/organization.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/platform/ios.dart';
import 'package:shorebird_cli/src/pubspec_editor.dart';
@@ -65,6 +66,26 @@ Please make sure you are running "shorebird init" from within your Flutter proje
return ExitCode.software.code;
}
final orgs = await codePushClientWrapper.getOrganizationMemberships();
if (orgs.isEmpty) {
logger.err(
'''You do not have any organizations. This should never happen. Please contact us on Discord or send us an email at contact@shorebird.dev.''',
);
return ExitCode.software.code;
}
final user = await codePushClientWrapper.getCurrentUser();
final Organization organization;
if (orgs.length > 1) {
organization = logger.chooseOne(
'Which organization should this app belong to?',
choices: orgs.map((o) => o.organization).toList(),
display: (o) => o.displayName(user: user!),
);
} else {
organization = orgs.first.organization;
}
final force = results['force'] == true;
Set<String>? androidFlavors;
@@ -130,6 +151,7 @@ Please make sure you are running "shorebird init" from within your Flutter proje
for (final flavor in newFlavors) {
final app = await codePushClientWrapper.createApp(
appName: '$deflavoredAppName ($flavor)',
organizationId: organization.id,
);
flavorsToAppIds[flavor] = app.id;
}
@@ -171,17 +193,24 @@ Please make sure you are running "shorebird init" from within your Flutter proje
if (hasNoFlavors) {
// No platforms have any flavors so we just create a single app
// and assign it as the default.
final app = await codePushClientWrapper.createApp(appName: displayName);
final app = await codePushClientWrapper.createApp(
appName: displayName,
organizationId: organization.id,
);
appId = app.id;
} else if (hasSomeFlavors) {
// Some platforms have flavors and some do not so we create an app
// for the default (no flavor) and then create an app per flavor.
final app = await codePushClientWrapper.createApp(appName: displayName);
final app = await codePushClientWrapper.createApp(
appName: displayName,
organizationId: organization.id,
);
appId = app.id;
final values = <String, String>{};
for (final flavor in productFlavors) {
final app = await codePushClientWrapper.createApp(
appName: '$displayName ($flavor)',
organizationId: organization.id,
);
values[flavor] = app.id;
}
@@ -193,6 +222,7 @@ Please make sure you are running "shorebird init" from within your Flutter proje
for (final flavor in productFlavors) {
final app = await codePushClientWrapper.createApp(
appName: '$displayName ($flavor)',
organizationId: organization.id,
);
values[flavor] = app.id;
}
@@ -0,0 +1,12 @@
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
/// {@template organization_display}
/// Returns the user-facing display name of an [Organization].
/// {@endtemplate}
extension OrganizationDisplay on Organization {
/// {@macro organization_display}
String displayName({required PrivateUser user}) => switch (organizationType) {
OrganizationType.team => name,
OrganizationType.personal => user.displayName ?? user.email,
};
}
@@ -212,44 +212,141 @@ void main() {
).thenAnswer((_) async => flutterVersion);
});
group('getCurrentUser', () {
group('when getCurrentUser request fails', () {
setUp(() {
when(
() => codePushClient.getCurrentUser(),
).thenThrow(Exception('something went wrong'));
});
test('exits with code 70', () async {
await expectLater(
() async => runWithOverrides(codePushClientWrapper.getCurrentUser),
exitsWithCode(ExitCode.software),
);
verify(() => progress.fail(any())).called(1);
});
});
group('when getCurrentUser request succeeds', () {
final user = PrivateUser.forTest();
setUp(() {
when(() => codePushClient.getCurrentUser()).thenAnswer(
(_) async => user,
);
});
test('returns current user', () async {
final result = await runWithOverrides(
codePushClientWrapper.getCurrentUser,
);
expect(result, user);
verify(() => progress.complete()).called(1);
});
});
});
group('app', () {
const organizationId = 123;
group('createApp', () {
test('prompts for displayName when not provided', () async {
const appName = 'test app';
const app = App(id: appId, displayName: 'Test App');
when(() => logger.prompt(any())).thenReturn(appName);
when(() => codePushClient.createApp(displayName: appName)).thenAnswer(
when(
() => codePushClient.createApp(
displayName: appName,
organizationId: any(named: 'organizationId'),
),
).thenAnswer(
(_) async => app,
);
await runWithOverrides(
() => codePushClientWrapper.createApp(),
() => codePushClientWrapper.createApp(
organizationId: organizationId,
),
);
verify(() => logger.prompt(any())).called(1);
verify(
() => codePushClient.createApp(displayName: appName),
() => codePushClient.createApp(
displayName: appName,
organizationId: organizationId,
),
).called(1);
});
test('does not prompt for displayName when not provided', () async {
const appName = 'test app';
const app = App(id: appId, displayName: 'Test App');
when(() => codePushClient.createApp(displayName: appName)).thenAnswer(
when(
() => codePushClient.createApp(
displayName: appName,
organizationId: any(named: 'organizationId'),
),
).thenAnswer(
(_) async => app,
);
await runWithOverrides(
() => codePushClientWrapper.createApp(appName: appName),
() => codePushClientWrapper.createApp(
appName: appName,
organizationId: organizationId,
),
);
verifyNever(() => logger.prompt(any()));
verify(
() => codePushClient.createApp(displayName: appName),
() => codePushClient.createApp(
displayName: appName,
organizationId: organizationId,
),
).called(1);
});
});
group('getOrganizationMemberships', () {
test('exits with code 70 when getting organization memberships fails',
() async {
const error = 'something went wrong';
when(() => codePushClient.getOrganizationMemberships())
.thenThrow(error);
await expectLater(
() async => runWithOverrides(
codePushClientWrapper.getOrganizationMemberships,
),
exitsWithCode(ExitCode.software),
);
verify(() => progress.fail(error)).called(1);
});
test('returns organization memberships on success', () async {
final expectedMemberships = [
OrganizationMembership(
organization: Organization.forTest(),
role: OrganizationRole.admin,
),
OrganizationMembership(
organization: Organization.forTest(),
role: OrganizationRole.member,
),
];
when(() => codePushClient.getOrganizationMemberships())
.thenAnswer((_) async => expectedMemberships);
final memberships = await runWithOverrides(
codePushClientWrapper.getOrganizationMemberships,
);
expect(memberships, equals(expectedMemberships));
verify(() => progress.complete()).called(1);
});
});
group('getApps', () {
test('exits with code 70 when getting apps fails', () async {
const error = 'something went wrong';
@@ -35,6 +35,8 @@ name: $appName
version: $version
environment:
sdk: ">=2.19.0 <3.0.0"''';
const organizationId = 123;
final currentUser = PrivateUser.forTest();
late ArgResults argResults;
late Doctor doctor;
@@ -92,8 +94,22 @@ environment:
shorebirdEnv = MockShorebirdEnv();
shorebirdValidator = MockShorebirdValidator();
when(() => codePushClientWrapper.getCurrentUser()).thenAnswer(
(_) async => currentUser,
);
when(() => codePushClientWrapper.getOrganizationMemberships()).thenAnswer(
(_) async => [
OrganizationMembership(
role: OrganizationRole.owner,
organization: Organization.forTest(id: organizationId),
),
],
);
when(
() => codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenAnswer((_) async => app);
when(
() => doctor.runValidators(any(), applyFixes: any(named: 'applyFixes')),
@@ -200,7 +216,10 @@ Please make sure you are running "shorebird init" from within your Flutter proje
() => logger.prompt(any(), defaultValue: any(named: 'defaultValue')),
);
verify(
() => codePushClientWrapper.createApp(appName: appName),
() => codePushClientWrapper.createApp(
appName: appName,
organizationId: organizationId,
),
).called(1);
});
@@ -211,7 +230,10 @@ Please make sure you are running "shorebird init" from within your Flutter proje
() => logger.prompt(any(), defaultValue: any(named: 'defaultValue')),
);
verify(
() => codePushClientWrapper.createApp(appName: appName),
() => codePushClientWrapper.createApp(
appName: appName,
organizationId: organizationId,
),
).called(1);
});
@@ -249,7 +271,10 @@ Please make sure you are running "shorebird init" from within your Flutter proje
test('throws software error when error occurs creating app.', () async {
final error = Exception('oops');
when(
() => codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenThrow(error);
final exitCode = await runWithOverrides(command.run);
verify(
@@ -259,6 +284,112 @@ Please make sure you are running "shorebird init" from within your Flutter proje
expect(exitCode, ExitCode.software.code);
});
group('when user has no organizations', () {
setUp(() {
when(
() => codePushClientWrapper.getOrganizationMemberships(),
).thenAnswer((_) async => []);
});
test('exits with software error code', () async {
final exitCode = await runWithOverrides(command.run);
expect(exitCode, equals(ExitCode.software.code));
verify(
() => logger.err(
'''You do not have any organizations. This should never happen. Please contact us on Discord or send us an email at contact@shorebird.dev.''',
),
).called(1);
});
});
group('when user has only one organization', () {
setUp(() {
when(() => codePushClientWrapper.getOrganizationMemberships())
.thenAnswer(
(_) async => [
OrganizationMembership(
role: OrganizationRole.owner,
organization: Organization.forTest(id: organizationId),
),
],
);
});
test('does not prompt for organization, uses that org id to create app',
() async {
final exitCode = await runWithOverrides(command.run);
expect(exitCode, equals(ExitCode.success.code));
verifyNever(
() => logger.chooseOne(
'Which organization should this app belong to?',
choices: any(named: 'choices'),
),
);
verify(
() => codePushClientWrapper.createApp(
appName: appName,
organizationId: organizationId,
),
).called(1);
});
});
group('when user has multiple organizations', () {
final org1 = Organization.forTest(
name: 'org1',
id: 1,
);
final org2 = Organization.forTest(
name: 'org2',
id: 2,
organizationType: OrganizationType.team,
);
setUp(() {
when(() => codePushClientWrapper.getOrganizationMemberships())
.thenAnswer(
(_) async => [
OrganizationMembership(
role: OrganizationRole.owner,
organization: org1,
),
OrganizationMembership(
role: OrganizationRole.owner,
organization: org2,
),
],
);
when(
() => logger.chooseOne<Organization>(
'Which organization should this app belong to?',
choices: any(named: 'choices'),
display: any(named: 'display'),
),
).thenReturn(org2);
});
test('prompts for organization and uses that org id to create app',
() async {
final exitCode = await runWithOverrides(command.run);
expect(exitCode, equals(ExitCode.success.code));
final capturedDisplay = verify(
() => logger.chooseOne<Organization>(
'Which organization should this app belong to?',
choices: [org1, org2],
display: captureAny(named: 'display'),
),
).captured.single as String Function(Organization);
expect(capturedDisplay(org1), equals(currentUser.email));
expect(capturedDisplay(org2), equals('org2'));
verify(
() => codePushClientWrapper.createApp(
appName: appName,
organizationId: org2.id,
),
).called(1);
});
});
group('on non MacOS', () {
setUp(() {
when(() => platform.isMacOS).thenReturn(false);
@@ -312,7 +443,10 @@ Please make sure you are running "shorebird init" from within your Flutter proje
).called(1);
verifyNever(() => logger.prompt(any()));
verify(
() => codePushClientWrapper.createApp(appName: displayName),
() => codePushClientWrapper.createApp(
appName: displayName,
organizationId: organizationId,
),
).called(1);
});
});
@@ -346,7 +480,10 @@ Please make sure you are running "shorebird init" from within your Flutter proje
const appIds = ['test-appId-1', 'test-appId-2'];
var index = 0;
when(
() => codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenAnswer((invocation) async {
final appName = invocation.namedArguments[#appName] as String?;
return App(id: appIds[index++], displayName: appName ?? '-');
@@ -373,8 +510,14 @@ flavors:
),
).called(1);
verifyInOrder([
() => codePushClientWrapper.createApp(appName: '$appName (internal)'),
() => codePushClientWrapper.createApp(appName: '$appName (stable)'),
() => codePushClientWrapper.createApp(
appName: '$appName (internal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (stable)',
organizationId: organizationId,
),
]);
});
});
@@ -410,7 +553,10 @@ flavors:
},
);
when(
() => codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenAnswer((invocation) async {
final appName = invocation.namedArguments[#appName] as String?;
return App(id: appIds[index++], displayName: appName ?? '--');
@@ -434,21 +580,27 @@ flavors:
verifyInOrder([
() => codePushClientWrapper.createApp(
appName: '$appName (development)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (developmentInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (production)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (productionInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (staging)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (stagingInternal)',
organizationId: organizationId,
),
]);
});
@@ -474,7 +626,10 @@ flavors:
},
);
when(
() => codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenAnswer((invocation) async {
final appName = invocation.namedArguments[#appName] as String?;
return App(id: appIds[index++], displayName: appName ?? '-');
@@ -501,21 +656,27 @@ flavors:
verifyInOrder([
() => codePushClientWrapper.createApp(
appName: '$appName (development)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (developmentInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (production)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (productionInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (staging)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (stagingInternal)',
organizationId: organizationId,
),
]);
});
@@ -543,7 +704,10 @@ flavors:
);
when(() => gradlew.productFlavors(any())).thenAnswer((_) async => {});
when(
() => codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenAnswer((invocation) async {
final appName = invocation.namedArguments[#appName] as String?;
return App(id: appIds[index++], displayName: appName ?? '--');
@@ -567,21 +731,27 @@ flavors:
verifyInOrder([
() => codePushClientWrapper.createApp(
appName: '$appName (development)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (developmentInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (production)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (productionInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (staging)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (stagingInternal)',
organizationId: organizationId,
),
]);
});
@@ -609,7 +779,10 @@ flavors:
},
);
when(
() => codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenAnswer((invocation) async {
final appName = invocation.namedArguments[#appName] as String?;
return App(id: appIds[index++], displayName: appName ?? '-');
@@ -633,21 +806,27 @@ flavors:
verifyInOrder([
() => codePushClientWrapper.createApp(
appName: '$appName (development)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (developmentInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (production)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (productionInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (staging)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (stagingInternal)',
organizationId: organizationId,
),
]);
});
@@ -675,7 +854,10 @@ flavors:
).thenAnswer((_) async => variants);
when(() => ios.flavors()).thenReturn(variants);
when(
() => codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenAnswer((invocation) async {
final appName = invocation.namedArguments[#appName] as String?;
return App(id: appIds[index++], displayName: appName ?? '-');
@@ -699,21 +881,27 @@ flavors:
verifyInOrder([
() => codePushClientWrapper.createApp(
appName: '$appName (development)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (developmentInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (production)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (productionInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (staging)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (stagingInternal)',
organizationId: organizationId,
),
]);
});
@@ -749,7 +937,10 @@ flavors:
).thenAnswer((_) async => androidVariants);
when(() => ios.flavors()).thenReturn(iosVariants);
when(
() => codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenAnswer((invocation) async {
final appName = invocation.namedArguments[#appName] as String?;
return App(id: appIds[index++], displayName: appName ?? '-');
@@ -773,27 +964,37 @@ flavors:
),
);
verifyInOrder([
() => codePushClientWrapper.createApp(appName: '$appName (dev)'),
() => codePushClientWrapper.createApp(
appName: '$appName (dev)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (devInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (production)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (productionInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (development)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (developmentInternal)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (staging)',
organizationId: organizationId,
),
() => codePushClientWrapper.createApp(
appName: '$appName (stagingInternal)',
organizationId: organizationId,
),
]);
});
@@ -842,8 +1043,10 @@ flavors:
),
);
when(
() =>
codePushClientWrapper.createApp(appName: any(named: 'appName')),
() => codePushClientWrapper.createApp(
appName: any(named: 'appName'),
organizationId: any(named: 'organizationId'),
),
).thenAnswer((invocation) async {
final appName = invocation.namedArguments[#appName] as String?;
return App(id: newAppIds[index++], displayName: appName ?? '-');
@@ -855,21 +1058,25 @@ flavors:
verifyNever(
() => codePushClientWrapper.createApp(
appName: '$appName (a)',
organizationId: organizationId,
),
);
verifyNever(
() => codePushClientWrapper.createApp(
appName: '$appName (b)',
organizationId: organizationId,
),
);
verify(
() => codePushClientWrapper.createApp(
appName: '$appName (c)',
organizationId: organizationId,
),
).called(1);
verify(
() => codePushClientWrapper.createApp(
appName: '$appName (d)',
organizationId: organizationId,
),
).called(1);
verify(
@@ -0,0 +1,43 @@
import 'package:shorebird_cli/src/extensions/organization.dart';
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
import 'package:test/test.dart';
void main() {
group('OrganizationDisplay', () {
group('displayName', () {
test('should return the name for a team organization', () {
final organization = Organization.forTest(
organizationType: OrganizationType.team,
name: 'My Organization',
);
expect(
organization.displayName(user: PrivateUser.forTest()),
equals('My Organization'),
);
});
group('for a personal organization', () {
group('when the user has a display name', () {
final user = PrivateUser.forTest(displayName: 'John Doe');
test("should return the user's display name", () {
final organization = Organization.forTest();
expect(organization.displayName(user: user), 'John Doe');
});
});
group('when the user does not have a display name', () {
final user = PrivateUser.forTest(email: 'test@test.com');
test("should return the user's email", () {
final organization = Organization.forTest();
expect(
organization.displayName(user: user),
equals('test@test.com'),
);
});
});
});
});
});
}
@@ -8,9 +8,13 @@ Future<void> main() async {
// List all apps.
final apps = await client.getApps();
// Get the list of organization memberships for the current user.
final memberships = await client.getOrganizationMemberships();
// Create a new Shorebird application.
final app = await client.createApp(
displayName: '<DISPLAY NAME>', // e.g. 'Shorebird Example'
organizationId: memberships.first.organization.id,
);
// Create a channel.
@@ -225,10 +225,18 @@ class CodePushClient {
/// Create a new app with the provided [displayName].
/// Returns the newly created app.
Future<App> createApp({required String displayName}) async {
Future<App> createApp({
required int organizationId,
required String displayName,
}) async {
final response = await _httpClient.post(
Uri.parse('$_v1/apps'),
body: json.encode({'display_name': displayName}),
body: json.encode(
CreateAppRequest(
organizationId: organizationId,
displayName: displayName,
).toJson(),
),
);
if (!response.isSuccess) {
@@ -19,6 +19,7 @@ void main() {
const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713';
const flutterVersion = '3.22.0';
const displayName = 'shorebird-example';
const organizationId = 1234;
const errorResponse = ErrorResponse(
code: 'test_code',
message: 'test message',
@@ -725,7 +726,12 @@ void main() {
group('createApp', () {
test('makes the correct request', () async {
codePushClient.createApp(displayName: displayName).ignore();
codePushClient
.createApp(
organizationId: organizationId,
displayName: displayName,
)
.ignore();
final request = verify(() => httpClient.send(captureAny()))
.captured
.single as http.BaseRequest;
@@ -743,7 +749,10 @@ void main() {
);
expect(
codePushClient.createApp(displayName: displayName),
codePushClient.createApp(
organizationId: organizationId,
displayName: displayName,
),
throwsA(
isA<CodePushException>().having(
(e) => e.message,
@@ -763,7 +772,10 @@ void main() {
);
expect(
codePushClient.createApp(displayName: displayName),
codePushClient.createApp(
organizationId: organizationId,
displayName: displayName,
),
throwsA(
isA<CodePushException>().having(
(e) => e.message,
@@ -787,7 +799,10 @@ void main() {
);
await expectLater(
codePushClient.createApp(displayName: displayName),
codePushClient.createApp(
organizationId: organizationId,
displayName: displayName,
),
completion(
equals(
isA<App>()