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(api): update endpoint config to use ApiOutputs instead of AWSApiConfig type #5193

Merged
merged 2 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'package:amplify_core/amplify_core.dart';

/// {@template amplify_core.amplify_outputs.data_outputs}
/// The Rest API and GraphQL category Outputs.
/// {@endtemplate}
abstract interface class ApiOutputs {
String get awsRegion;
String get url;
String? get apiKey;
APIAuthorizationType get authorizationType;
ApiType get apiType;
}

enum ApiType {
rest,
graphQL,
}
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/api_outputs.dart';

part 'data_outputs.g.dart';

Expand All @@ -10,7 +11,8 @@ part 'data_outputs.g.dart';
/// {@endtemplate}
@zAmplifyOutputsSerializable
class DataOutputs
with AWSEquatable<DataOutputs>, AWSSerializable, AWSDebuggable {
with AWSEquatable<DataOutputs>, AWSSerializable, AWSDebuggable
implements ApiOutputs {
/// {@macro amplify_core.amplify_outputs.data_outputs}
const DataOutputs({
required this.awsRegion,
Expand All @@ -24,12 +26,15 @@ class DataOutputs
_$DataOutputsFromJson(json);

/// The AWS region of Amazon AppSync resources.
@override
final String awsRegion;

/// The AppSync endpoint URL.
@override
final String url;

/// The AppSync API Key.
@override
final String? apiKey;

/// The default authorization type for AWS AppSync.
Expand All @@ -38,6 +43,13 @@ class DataOutputs
/// List of supported authorization types for AWS AppSync.
final List<APIAuthorizationType> authorizationTypes;

@override
ApiType get apiType => ApiType.graphQL;
Copy link
Member

Choose a reason for hiding this comment

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

Question: Does this get included in the json serialization/de-serialization? I don't see the generated file changed, but that could just be caused by codegen not being run

Copy link
Member Author

Choose a reason for hiding this comment

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

code gen does not change the generated codes, I think this is because they are getters.


@override
APIAuthorizationType<AmplifyAuthProvider> get authorizationType =>
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: missing doc comment

defaultAuthorizationType;

@override
List<Object?> get props => [
awsRegion,
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/api_outputs.dart';

part 'rest_api_outputs.g.dart';

Expand All @@ -13,7 +14,8 @@ part 'rest_api_outputs.g.dart';
/// {@endtemplate}
@zAmplifyOutputsSerializable
class RestApiOutputs
with AWSEquatable<RestApiOutputs>, AWSSerializable, AWSDebuggable {
with AWSEquatable<RestApiOutputs>, AWSSerializable, AWSDebuggable
implements ApiOutputs {
/// {@macro amplify_core.amplify_outputs.rest_api_outputs}
const RestApiOutputs({
required this.awsRegion,
Expand All @@ -25,18 +27,25 @@ class RestApiOutputs
factory RestApiOutputs.fromJson(Map<String, Object?> json) =>
_$RestApiOutputsFromJson(json);

/// The AWS region of Amazon AWS Gateway resources.
/// The AWS region of Amazon API Gateway resources.
@override
final String awsRegion;

/// The AWS Gateway endpoint URL.
/// The Amazon API Gateway endpoint URL.
@override
final String url;

/// The AppSync API Key.
/// The Amazon API Gateway API Key.
@override
final String? apiKey;

/// The authorization type.
@override
final APIAuthorizationType authorizationType;

@override
ApiType get apiType => ApiType.rest;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this is missing its docs comment. Same with the DataOutputs.apiType


@override
List<Object?> get props => [
awsRegion,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'dart:convert';

import 'package:amplify_core/amplify_core.dart';
Expand Down
48 changes: 19 additions & 29 deletions packages/api/amplify_api_dart/lib/src/api_plugin_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import 'package:amplify_api_dart/src/util/amplify_api_config.dart';
import 'package:amplify_api_dart/src/util/amplify_authorization_rest_client.dart';
import 'package:amplify_core/amplify_core.dart';
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/api_outputs.dart';
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/data/data_outputs.dart';
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/rest_api/rest_api_outputs.dart';
Expand Down Expand Up @@ -162,7 +164,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
/// Use [apiName] if there are multiple endpoints of the same type.
@visibleForTesting
AWSHttpClient getHttpClient(
EndpointType type, {
ApiType type, {
String? apiName,
APIAuthorizationType? authorizationMode,
}) {
Expand All @@ -181,8 +183,8 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
);
}

EndpointConfig _getEndpointConfig(EndpointType type, String? apiName) {
if (type == EndpointType.graphQL) {
EndpointConfig _getEndpointConfig(ApiType type, String? apiName) {
if (type == ApiType.graphQL) {
if (_dataConfig == null) {
throw ConfigurationError(
'No GraphQL API endpoint found.',
Expand Down Expand Up @@ -210,16 +212,10 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
}
return EndpointConfig(
apiName,
AWSApiConfig(
region: config.awsRegion,
endpoint: config.url,
endpointType: EndpointType.graphQL,
authorizationType: config.defaultAuthorizationType,
apiKey: config.apiKey,
),
config,
);
}
if (type == EndpointType.rest) {
if (type == ApiType.rest) {
if (_restConfig == null) {
throw ConfigurationError(
'No REST API endpoint found.',
Expand Down Expand Up @@ -247,13 +243,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
}
return EndpointConfig(
apiName,
AWSApiConfig(
region: config.awsRegion,
endpoint: config.url,
endpointType: EndpointType.rest,
authorizationType: config.authorizationType,
apiKey: config.apiKey,
),
config,
);
}
throw ConfigurationError(
Expand All @@ -263,7 +253,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {

WebSocketBloc _webSocketBloc({String? apiName}) {
final endpoint = _getEndpointConfig(
EndpointType.graphQL,
ApiType.graphQL,
apiName,
);

Expand Down Expand Up @@ -292,7 +282,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {

Uri _getGraphQLUri(String? apiName) {
final endpoint = _getEndpointConfig(
EndpointType.graphQL,
ApiType.graphQL,
apiName,
);
return endpoint.getUri();
Expand All @@ -304,7 +294,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
Map<String, dynamic>? queryParameters,
) {
final endpoint = _getEndpointConfig(
EndpointType.rest,
ApiType.rest,
apiName,
);
return endpoint.getUri(path: path, queryParameters: queryParameters);
Expand All @@ -317,7 +307,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
@override
GraphQLOperation<T> query<T>({required GraphQLRequest<T> request}) {
final graphQLClient = getHttpClient(
EndpointType.graphQL,
ApiType.graphQL,
apiName: request.apiName,
authorizationMode: request.authorizationMode,
);
Expand All @@ -333,7 +323,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
@override
GraphQLOperation<T> mutate<T>({required GraphQLRequest<T> request}) {
final graphQLClient = getHttpClient(
EndpointType.graphQL,
ApiType.graphQL,
apiName: request.apiName,
authorizationMode: request.authorizationMode,
);
Expand Down Expand Up @@ -366,7 +356,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
String? apiName,
}) {
final uri = _getRestUri(path, apiName, queryParameters);
final client = getHttpClient(EndpointType.rest, apiName: apiName);
final client = getHttpClient(ApiType.rest, apiName: apiName);
return RestOperation.fromHttpOperation(
AWSStreamedHttpRequest.delete(
uri,
Expand All @@ -384,7 +374,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
String? apiName,
}) {
final uri = _getRestUri(path, apiName, queryParameters);
final client = getHttpClient(EndpointType.rest, apiName: apiName);
final client = getHttpClient(ApiType.rest, apiName: apiName);
return RestOperation.fromHttpOperation(
AWSHttpRequest.get(
uri,
Expand All @@ -401,7 +391,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
String? apiName,
}) {
final uri = _getRestUri(path, apiName, queryParameters);
final client = getHttpClient(EndpointType.rest, apiName: apiName);
final client = getHttpClient(ApiType.rest, apiName: apiName);
return RestOperation.fromHttpOperation(
AWSHttpRequest.head(
uri,
Expand All @@ -419,7 +409,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
String? apiName,
}) {
final uri = _getRestUri(path, apiName, queryParameters);
final client = getHttpClient(EndpointType.rest, apiName: apiName);
final client = getHttpClient(ApiType.rest, apiName: apiName);
return RestOperation.fromHttpOperation(
AWSStreamedHttpRequest.patch(
uri,
Expand All @@ -438,7 +428,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
String? apiName,
}) {
final uri = _getRestUri(path, apiName, queryParameters);
final client = getHttpClient(EndpointType.rest, apiName: apiName);
final client = getHttpClient(ApiType.rest, apiName: apiName);
return RestOperation.fromHttpOperation(
AWSStreamedHttpRequest.post(
uri,
Expand All @@ -457,7 +447,7 @@ class AmplifyAPIDart extends APIPluginInterface with AWSDebuggable {
String? apiName,
}) {
final uri = _getRestUri(path, apiName, queryParameters);
final client = getHttpClient(EndpointType.rest, apiName: apiName);
final client = getHttpClient(ApiType.rest, apiName: apiName);
return RestOperation.fromHttpOperation(
AWSStreamedHttpRequest.put(
uri,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import 'dart:async';

import 'package:amplify_api_dart/src/graphql/providers/app_sync_api_key_auth_provider.dart';
import 'package:amplify_core/amplify_core.dart';
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/api_outputs.dart';
import 'package:meta/meta.dart';

/// Transforms an HTTP request according to auth providers that match the endpoint
/// configuration.
@internal
Future<AWSBaseHttpRequest> authorizeHttpRequest(
AWSBaseHttpRequest request, {
required AWSApiConfig endpointConfig,
required ApiOutputs endpointConfig,
required AmplifyAuthProviderRepository authProviderRepo,
APIAuthorizationType? authorizationMode,
}) async {
Expand Down Expand Up @@ -49,7 +51,7 @@ Future<AWSBaseHttpRequest> authorizeHttpRequest(
.getAuthProvider(APIAuthorizationType.iam.authProviderToken),
authType,
);
final isGraphQL = endpointConfig.endpointType == EndpointType.graphQL;
final isGraphQL = endpointConfig.apiType == ApiType.graphQL;
final service = isGraphQL
? AWSService.appSync
: AWSService.apiGatewayManagementApi; // resolves to "execute-api"
Expand All @@ -60,7 +62,7 @@ Future<AWSBaseHttpRequest> authorizeHttpRequest(
final authorizedRequest = await authProvider.authorizeRequest(
request,
options: IamAuthProviderOptions(
region: endpointConfig.region,
region: endpointConfig.awsRegion,
service: service,
serviceConfiguration: serviceConfiguration,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import 'dart:convert';
import 'package:amplify_api_dart/src/decorators/authorize_http_request.dart';
import 'package:amplify_api_dart/src/graphql/web_socket/types/web_socket_types.dart';
import 'package:amplify_core/amplify_core.dart';
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/api_outputs.dart';
import 'package:meta/meta.dart';

const _appSyncHostPortion = 'appsync-api';
Expand All @@ -31,7 +33,7 @@ const _emptyBody = <String, dynamic>{};
///
/// See https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#handshake-details-to-establish-the-websocket-connection=
Future<Uri> generateConnectionUri(
AWSApiConfig config,
ApiOutputs config,
AmplifyAuthProviderRepository authRepo,
) async {
// First, generate auth query parameters.
Expand All @@ -48,7 +50,7 @@ Future<Uri> generateConnectionUri(
'payload': base64.encode(utf8.encode(json.encode(_emptyBody))),
};
// Conditionally format the URI for a) AppSync domain b) custom domain.
var endpointUriHost = Uri.parse(config.endpoint).host;
var endpointUriHost = Uri.parse(config.url).host;
String path;
if (endpointUriHost.contains(_appSyncHostPortion) &&
endpointUriHost.endsWith(_appSyncHostSuffix)) {
Expand Down Expand Up @@ -78,7 +80,7 @@ Future<Uri> generateConnectionUri(
/// See https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#subscription-registration-message
Future<WebSocketSubscriptionRegistrationMessage>
generateSubscriptionRegistrationMessage<T>(
AWSApiConfig config, {
ApiOutputs config, {
required String id,
required AmplifyAuthProviderRepository authRepo,
required GraphQLRequest<T> request,
Expand Down Expand Up @@ -113,21 +115,21 @@ Future<WebSocketSubscriptionRegistrationMessage>
/// the HTTP request are reformatted and returned. This logic applies for all auth
/// modes as determined by [authRepo] parameter.
Future<Map<String, String>> _generateAuthorizationHeaders(
AWSApiConfig config, {
ApiOutputs config, {
required bool isConnectionInit,
required AmplifyAuthProviderRepository authRepo,
required Map<String, dynamic> body,
APIAuthorizationType? authorizationMode,
Map<String, String>? customHeaders,
}) async {
final endpointHost = Uri.parse(config.endpoint).host;
final endpointHost = Uri.parse(config.url).host;
// Create canonical HTTP request to authorize but never send.
//
// The canonical request URL is a little different depending on if authorizing
// connection URI or start message (subscription registration).
final maybeConnect = isConnectionInit ? '/connect' : '';
final canonicalHttpRequest = AWSStreamedHttpRequest.post(
Uri.parse('${config.endpoint}$maybeConnect'),
Uri.parse('${config.url}$maybeConnect'),
headers: {
...?customHeaders,
..._requiredHeaders,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import 'package:amplify_api_dart/src/graphql/web_socket/types/connectivity_platf
import 'package:amplify_api_dart/src/graphql/web_socket/types/subscriptions_event.dart';
import 'package:amplify_api_dart/src/graphql/web_socket/types/web_socket_types.dart';
import 'package:amplify_core/amplify_core.dart' hide SubscriptionEvent;
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/api_outputs.dart';
Comment on lines +15 to +16
Copy link
Contributor

Choose a reason for hiding this comment

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

Q: Needing this ignore seems odd, are we sure this is the best way to import/export this 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.

I think this is the way we do in the monorepo when we do not want to export files/types from one package but still want to use them in other packages.

import 'package:async/async.dart';
import 'package:meta/meta.dart';
import 'package:stream_transform/stream_transform.dart';
Expand All @@ -26,7 +28,7 @@ part '../types/web_socket_event.dart';
class WebSocketBloc with AWSDebuggable, AmplifyLoggerMixin {
/// {@macro api.web_socket_bloc}
WebSocketBloc({
required AWSApiConfig config,
required ApiOutputs config,
required AmplifyAuthProviderRepository authProviderRepo,
required WebSocketService wsService,
required GraphQLSubscriptionOptions subscriptionOptions,
Expand Down
Loading
Loading