Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update configure apis to use AmplifyOutputs instead of AmplifyConfig #5017

Merged
merged 6 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 45 additions & 19 deletions packages/amplify_core/lib/src/amplify_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,24 +111,12 @@ abstract class AmplifyClass {
'Check if Amplify is already configured using Amplify.isConfigured.',
);
}

late AmplifyConfig amplifyConfig;
late final AmplifyOutputs amplifyOutputs;
try {
try {
final json = jsonDecode(configuration) as Map;
amplifyConfig = AmplifyConfig.fromJson(json.cast());
} on Object catch (e) {
throw ConfigurationError(
'The provided configuration is not a valid json. '
'Check underlyingException.',
recoverySuggestion:
'Inspect your amplifyconfiguration.dart and ensure that '
'the string is proper json',
underlyingException: e,
);
}
await _configurePlugins(amplifyConfig);
_configCompleter.complete(amplifyConfig.toAmplifyOutputs());
final json = _decodeJson(configuration);
amplifyOutputs = _parseJsonConfig(json);
await _configurePlugins(amplifyOutputs);
_configCompleter.complete(amplifyOutputs);
} on ConfigurationError catch (e, st) {
// Complete with the configuration error and reset the completer so
// that 1) `configure` can be called again and 2) listeners registered
Expand All @@ -146,14 +134,52 @@ abstract class AmplifyClass {
// handled by the developer, but since they are unrelated to
// configuration, listeners to `Amplify.asyncConfig` should be allowed to
// proceed with the validated configuration.
_configCompleter.complete(amplifyConfig.toAmplifyOutputs());
_configCompleter.complete(amplifyOutputs);
_configCompleter = Completer();
rethrow;
}
}

AmplifyOutputs _parseJsonConfig(Map<String, Object?> json) {
try {
return AmplifyOutputs.fromJson(json);
} on Object {
try {
final amplifyConfig = AmplifyConfig.fromJson(json);
return amplifyConfig.toAmplifyOutputs();
} on Object catch (e) {
throw ConfigurationError(
'The provided configuration can not be decoded to AmplifyOutputs '
'or AmplifyConfig. '
'Check underlyingException.',
recoverySuggestion:
'If using Amplify Gen 2 ensure that the json string '
'can be decoded to AmplifyOutputs type. '
'If using Amplify Gen 1 ensure that the json '
'string can be decoded to AmplifyConfig type.',
underlyingException: e,
);
}
}
}

Map<String, Object?> _decodeJson(String configuration) {
try {
return jsonDecode(configuration) as Map<String, Object?>;
} on Object catch (e) {
throw ConfigurationError(
'The provided configuration is not a valid json. '
'Check underlyingException.',
recoverySuggestion:
'Inspect your amplify_outputs.dart or amplifyconfiguration.dart '
'and ensure that the string is proper json',
underlyingException: e,
);
}
}

/// Configures all plugins in topologically-sorted order.
Future<void> _configurePlugins(AmplifyConfig config) async {
Future<void> _configurePlugins(AmplifyOutputs config) async {
await Future.wait(_addPluginFutures);
_addPluginFutures.clear();
final categories = <Category, AmplifyCategory>{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class AmplifyOutputs

/// {@macro amplify_core.amplify_outputs.rest_api_outputs}
@internal
@JsonKey(includeToJson: false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: was there a particular reason to make this change?

I think this change is fine, but am curious about what the motivation was. I had considered mvoing this change when I added rest API but decided not to because I thought it might be weird if AmplifyOutputs.fromJson(outputs.toJson()) would not result in the original outputs. Since AmplifyOutputs is private I don't think it really matters though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in packages/amplify_datastore/lib/amplify_datastore.dart we call the .toJson() method before calling NativeAmplifyBridge.configure(). having the rest_api included in toJson causes the config to become invalid Gen 2 config.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do Amplify iOS and/or Android consider the config invalid when there are extra key/value pairs? That seems potentially problematic since new key/value pairs would generally be considered non breaking.

final Map<String, RestApiOutputs>? restApi;

/// {@macro amplify_core.amplify_outputs.notifications_outputs}
Expand Down Expand Up @@ -119,6 +120,6 @@ Object? _dataToJson(Map<String, DataOutputs>? outputs) {
' Amplify Outputs does not support multiple GraphQL endpoints.',
);
}
final data = outputs[_dataPluginName];
final data = outputs.values.firstOrNull;
return data?.toJson();
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions packages/amplify_core/lib/src/config/auth/cognito/oauth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// SPDX-License-Identifier: Apache-2.0

import 'package:amplify_core/amplify_core.dart';
import 'package:amplify_core/src/config/amplify_outputs/auth/auth_outputs.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart';

part 'oauth.g.dart';

Expand All @@ -27,6 +29,46 @@ class CognitoOAuthConfig
this.tokenUriQueryParameters,
});

@internal
factory CognitoOAuthConfig.fromAuthOutputs(AuthOutputs authOutputs) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P.S. this used in AuthConfiguration.fromAmplifyOutputs AuthConfiguration is an internal type used in Auth State Machine and is differnt from AuthConfig.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a follow up task to remove use of CognitoOAuthConfig? Maybe if AuthConfiguration used OAuthOutputs instead of CognitoOAuthConfig the changes in the state machine would be minimal?

if (authOutputs.userPoolClientId == null) {
throw ConfigurationError('Invalid config: no User Pool Client Id found.');
}
if (authOutputs.oauth == null) {
throw ConfigurationError('Invalid config: no oAuth configuration found.');
}

final appClientId = authOutputs.userPoolClientId!;
final appClientSecret = authOutputs.appClientSecret;
final scopes = authOutputs.oauth!.scopes;
final signInUri = authOutputs.oauth!.signInUri;
final signOutUri = authOutputs.oauth!.signOutUri;
final signInUriQueryParameters =
authOutputs.oauth!.signInUriQueryParameters;
final signOutUriQueryParameters =
authOutputs.oauth!.signOutUriQueryParameters;
final signInRedirectUri = authOutputs.oauth!.redirectSignInUri.join(',');
final signOutRedirectUri = authOutputs.oauth!.redirectSignOutUri.join(',');
final webDomain = authOutputs.oauth!.domain;
final tokenUri = authOutputs.oauth!.tokenUri;
final tokenUriQueryParameters = authOutputs.oauth!.tokenUriQueryParameters;

return CognitoOAuthConfig(
appClientId: appClientId,
appClientSecret: appClientSecret,
scopes: scopes,
signInUri: signInUri,
signOutUri: signOutUri,
signInRedirectUri: signInRedirectUri,
signOutRedirectUri: signOutRedirectUri,
webDomain: webDomain,
signInUriQueryParameters: signInUriQueryParameters,
signOutUriQueryParameters: signOutUriQueryParameters,
tokenUri: tokenUri,
tokenUriQueryParameters: tokenUriQueryParameters,
);
}

factory CognitoOAuthConfig.fromJson(Map<String, Object?> json) =>
_$CognitoOAuthConfigFromJson(json);

Expand Down
25 changes: 25 additions & 0 deletions packages/amplify_core/lib/src/config/auth/cognito/user_pool.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// SPDX-License-Identifier: Apache-2.0

import 'package:amplify_core/amplify_core.dart';
import 'package:amplify_core/src/config/amplify_outputs/auth/auth_outputs.dart';
import 'package:meta/meta.dart';

part 'user_pool.g.dart';

Expand All @@ -17,6 +19,29 @@ class CognitoUserPoolConfig
this.endpoint,
});

@internal
factory CognitoUserPoolConfig.fromAuthOutputs(AuthOutputs authOutputs) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P.S. this used in AuthConfiguration.fromAmplifyOutputs. AuthConfiguration is an internal type used in Auth State Machine and is differnt from AuthConfig.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above. Do we have a follow up task to remove use of this?

if (authOutputs.userPoolId == null) {
throw ConfigurationError(
'Invalid Cognito User Pool config: No User Pool Id found',
);
}
if (authOutputs.userPoolClientId == null) {
throw ConfigurationError(
'Invalid Cognito User Pool config: No User Pool Client Id found',
);
}
return CognitoUserPoolConfig(
poolId: authOutputs.userPoolId!,
appClientId: authOutputs.userPoolClientId!,
appClientSecret: authOutputs.appClientSecret,
region: authOutputs.awsRegion,
hostedUI: authOutputs.oauth == null
? null
: CognitoOAuthConfig.fromAuthOutputs(authOutputs),
);
}

factory CognitoUserPoolConfig.fromJson(Map<String, Object?> json) =>
_$CognitoUserPoolConfigFromJson(json);
final String poolId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ abstract /* base */ class AmplifyPluginInterface {

/// Configures the plugin using the registered [config].
Future<void> configure({
AmplifyConfig? config,
AmplifyOutputs? config,
required AmplifyAuthProviderRepository authProviderRepo,
}) async {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import 'package:amplify_core/amplify_core.dart';
import 'package:amplify_core/src/config/amplify_outputs/notifications/notifications_outputs.dart';

/// {@template amplify_core.push.service_provider_client}
/// A base class for new service providers to implement and add functionality
Expand All @@ -11,7 +12,7 @@ import 'package:amplify_core/amplify_core.dart';
abstract class ServiceProviderClient {
/// Initialize this client, used by the plugin during configuration.
Future<void> init({
required NotificationsPinpointPluginConfig config,
required NotificationsOutputs config,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Is this an internal type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it is not exporeted.

required AmplifyAuthProviderRepository authProviderRepo,
});

Expand Down
8 changes: 4 additions & 4 deletions packages/amplify_core/test/amplify_class_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void main() {
class ConfigErrorPlugin extends AnalyticsPluginInterface {
@override
Future<void> configure({
AmplifyConfig? config,
AmplifyOutputs? config,
required AmplifyAuthProviderRepository authProviderRepo,
}) {
throw ConfigurationError('Could not configure');
Expand All @@ -82,7 +82,7 @@ class ConfigErrorPlugin extends AnalyticsPluginInterface {
class NonConfigErrorPlugin extends AnalyticsPluginInterface {
@override
Future<void> configure({
AmplifyConfig? config,
AmplifyOutputs? config,
required AmplifyAuthProviderRepository authProviderRepo,
}) {
throw const UnknownException('Could not configure');
Expand All @@ -92,7 +92,7 @@ class NonConfigErrorPlugin extends AnalyticsPluginInterface {
class SuccessPlugin extends AnalyticsPluginInterface {
@override
Future<void> configure({
AmplifyConfig? config,
AmplifyOutputs? config,
required AmplifyAuthProviderRepository authProviderRepo,
}) async {
return;
Expand All @@ -107,7 +107,7 @@ class AsyncAddPlugin extends AnalyticsPluginInterface {

@override
Future<void> configure({
AmplifyConfig? config,
AmplifyOutputs? config,
required AmplifyAuthProviderRepository authProviderRepo,
}) async {
_configureCompleter.complete();
Expand Down
6 changes: 3 additions & 3 deletions packages/amplify_core/test/plugin/auth_providers_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class AuthPlugin extends Fake implements AuthPluginInterface {

@override
Future<void> configure({
AmplifyConfig? config,
AmplifyOutputs? config,
required AmplifyAuthProviderRepository authProviderRepo,
}) async {
_authProvider = authProviderRepo.getAuthProvider(
Expand Down Expand Up @@ -72,7 +72,7 @@ class ApiPlugin extends Fake implements APIPluginInterface {

@override
Future<void> configure({
AmplifyConfig? config,
AmplifyOutputs? config,
required AmplifyAuthProviderRepository authProviderRepo,
}) async {
_authProvider = authProviderRepo.getAuthProvider(
Expand All @@ -83,7 +83,7 @@ class ApiPlugin extends Fake implements APIPluginInterface {

void main() {
group('AuthProviderRepository', () {
test('allows plugin interdependenices', () async {
test('allows plugin interdependencies', () async {
final apiPlugin = ApiPlugin();
final authPlugin = AuthPlugin();

Expand Down
8 changes: 4 additions & 4 deletions packages/amplify_datastore/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ android {
}

dependencies {
implementation 'com.amplifyframework:aws-auth-cognito:2.15.0'
implementation "com.amplifyframework:aws-api:2.15.0"
implementation "com.amplifyframework:aws-datastore:2.15.0"
implementation "com.amplifyframework:aws-api-appsync:2.15.0"
implementation 'com.amplifyframework:aws-auth-cognito:2.19.1'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Should this go to main in a separate PR?

implementation "com.amplifyframework:aws-api:2.19.1"
implementation "com.amplifyframework:aws-datastore:2.19.1"
implementation "com.amplifyframework:aws-api-appsync:2.19.1"
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ import com.amazonaws.amplify.amplify_datastore.util.cast
import com.amazonaws.amplify.amplify_datastore.util.safeCastToList
import com.amazonaws.amplify.amplify_datastore.util.safeCastToMap
import com.amplifyframework.AmplifyException
import com.amplifyframework.annotations.AmplifyFlutterApi
import com.amplifyframework.api.aws.AWSApiPlugin
import com.amplifyframework.api.aws.AuthModeStrategyType
import com.amplifyframework.api.aws.AuthorizationType
import com.amplifyframework.auth.AuthUser
import com.amplifyframework.core.Amplify
import com.amplifyframework.core.AmplifyConfiguration
import com.amplifyframework.core.async.Cancelable
import com.amplifyframework.core.configuration.AmplifyOutputs
import com.amplifyframework.core.model.CustomTypeSchema
import com.amplifyframework.core.model.Model
import com.amplifyframework.core.model.ModelSchema
Expand Down Expand Up @@ -937,11 +939,9 @@ class AmplifyDataStorePlugin :
) {
coroutineScope.launch(dispatcher) {
try {
val configuration = AmplifyConfiguration.builder(JSONObject(config))
.addPlatform(UserAgent.Platform.FLUTTER, "$version /datastore")
.devMenuEnabled(false)
.build()
Amplify.configure(configuration, context)
@OptIn(AmplifyFlutterApi::class)
Amplify.addUserAgentPlatform(UserAgent.Platform.FLUTTER, "$version /datastore")
Amplify.configure(AmplifyOutputs(config), context)
withContext(Dispatchers.Main) {
callback(kotlin.Result.success(Unit))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,9 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin, NativeAmplify
nil
)
}
let amplifyConfiguration = try JSONDecoder().decode(AmplifyConfiguration.self,
from: data)
// TODO: Migrate to Async Swift v2
// AmplifyAWSServiceConfiguration.addUserAgentPlatform(.flutter, version: "\(version) /datastore")
try Amplify.configure(amplifyConfiguration)
try Amplify.configure(with : .data(data))
return completion(.success(()))
} catch let error as ConfigurationError {
switch error {
Expand Down
10 changes: 4 additions & 6 deletions packages/amplify_datastore/lib/amplify_datastore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class AmplifyDataStore extends DataStorePluginInterface

@override
Future<void> configure({
AmplifyConfig? config,
AmplifyOutputs? config,
required AmplifyAuthProviderRepository authProviderRepo,
}) async {
if (config == null) {
Expand Down Expand Up @@ -100,15 +100,13 @@ class AmplifyDataStore extends DataStorePluginInterface
}

final apiPlugin = Amplify.API.plugins.firstOrNull;
final gqlConfig = config.api?.awsPlugin?.all.values.firstWhereOrNull(
(config) => config.endpointType == EndpointType.graphQL,
);
final gqlConfig = config.data;
if (apiPlugin != null && gqlConfig != null) {
// ignore: invalid_use_of_protected_member
final authProviders = apiPlugin.authProviders;
Map<String, String> endpoints = {};
config.api?.awsPlugin?.all.entries.forEach((e) {
endpoints[e.key] = e.value.authorizationType.name;
gqlConfig.entries.forEach((e) {
endpoints[e.key] = e.value.defaultAuthorizationType.name;
});
final nativePlugin = NativeAmplifyApi(authProviders);
NativeApiPlugin.setup(nativePlugin);
Expand Down
Loading
Loading