From 4156bfb7cac8dcc6dc2e2de61a20c52e26286a12 Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Fri, 17 May 2024 13:19:04 -0400 Subject: [PATCH] fix(API): pass authMode used for lazy loading functionality (#3690) * fix(API): pass authMode used for lazy loading functionality * revert GraphQLFilter change --- .../Core/AppSyncListDecoder.swift | 2 + .../Core/AppSyncListPayload.swift | 3 ++ .../Core/AppSyncListProvider.swift | 29 +++++++++++---- .../Core/AppSyncListResponse.swift | 9 ++++- .../Core/AppSyncModelDecoder.swift | 8 +++- .../Core/AppSyncModelMetadata.swift | 22 ++++++++--- .../Core/AppSyncModelProvider.swift | 7 +++- .../GraphQLResponseDecoder+DecodeData.swift | 8 +++- .../Utils/GraphQLRequest+toListQuery.swift | 12 ++++-- .../Core/AppSyncListDecoderTests.swift | 7 ++-- .../Core/AppSyncListPayloadTests.swift | 7 +++- .../AppSyncListProviderPaginationTests.swift | 13 +++++-- .../Core/AppSyncListProviderTests.swift | 37 +++++++++++++------ .../Core/AppSyncListResponseTests.swift | 8 ++-- .../Core/AppSyncModelMetadataTests.swift | 24 +++++++----- .../GraphQLRequestToListQueryTests.swift | 12 ++++-- .../Auth/AWSAuthorizationType.swift | 2 + 17 files changed, 153 insertions(+), 57 deletions(-) diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListDecoder.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListDecoder.swift index c6050bb2d7..e75add220b 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListDecoder.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListDecoder.swift @@ -7,6 +7,7 @@ import Foundation import Amplify +import AWSPluginsCore /// This decoder is registered and used to detect various data payloads objects to store /// inside an AppSyncListProvider when decoding to the Lazy `List` type as a "not yet loaded" List. If the data payload @@ -18,6 +19,7 @@ public struct AppSyncListDecoder: ModelListDecoder { let appSyncAssociatedIdentifiers: [String] let appSyncAssociatedFields: [String] let apiName: String? + let authMode: AWSAuthorizationType? } /// Used by the custom decoder implemented in the `List` type to detect if the payload can be diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListPayload.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListPayload.swift index 6205e6c448..033c718d86 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListPayload.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListPayload.swift @@ -16,11 +16,14 @@ public struct AppSyncListPayload: Codable { let graphQLData: JSONValue let apiName: String? + let authMode: AWSAuthorizationType? public init(graphQLData: JSONValue, apiName: String?, + authMode: AWSAuthorizationType?, variables: [String: JSONValue]?) { self.apiName = apiName + self.authMode = authMode self.variables = variables self.graphQLData = graphQLData } diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListProvider.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListProvider.swift index 2edf147c1b..4f1eaab6f0 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListProvider.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListProvider.swift @@ -14,6 +14,9 @@ public class AppSyncListProvider: ModelListProvider { /// The API friendly name used to reference the API to call let apiName: String? + /// The auth mode used for this API call + let authMode: AWSAuthorizationType? + /// The limit for each page size var limit: Int? = 1_000 @@ -42,7 +45,8 @@ public class AppSyncListProvider: ModelListProvider { convenience init(payload: AppSyncListPayload) throws { let listResponse = try AppSyncListResponse.initWithMetadata(type: Element.self, graphQLData: payload.graphQLData, - apiName: payload.apiName) + apiName: payload.apiName, + authMode: payload.authMode) self.init(elements: listResponse.items, nextToken: listResponse.nextToken, @@ -59,27 +63,34 @@ public class AppSyncListProvider: ModelListProvider { convenience init(metadata: AppSyncListDecoder.Metadata) { self.init(associatedIdentifiers: metadata.appSyncAssociatedIdentifiers, associatedFields: metadata.appSyncAssociatedFields, - apiName: metadata.apiName) + apiName: metadata.apiName, + authMode: metadata.authMode) } // Internal initializer for testing init(elements: [Element], nextToken: String? = nil, apiName: String? = nil, + authMode: AWSAuthorizationType? = nil, limit: Int? = nil, filter: [String: Any]? = nil) { self.loadedState = .loaded(elements: elements, nextToken: nextToken, filter: filter) self.apiName = apiName + self.authMode = authMode self.limit = limit } // Internal initializer for testing - init(associatedIdentifiers: [String], associatedFields: [String], apiName: String? = nil) { + init(associatedIdentifiers: [String], + associatedFields: [String], + apiName: String? = nil, + authMode: AWSAuthorizationType? = nil) { self.loadedState = .notLoaded(associatedIdentifiers: associatedIdentifiers, associatedFields: associatedFields) self.apiName = apiName + self.authMode = authMode } // MARK: APIs @@ -128,14 +139,16 @@ public class AppSyncListProvider: ModelListProvider { modelSchema: Element.schema, filter: filter, limit: limit, - apiName: apiName) + apiName: apiName, + authMode: authMode) do { let graphQLResponse = try await Amplify.API.query(request: request) switch graphQLResponse { case .success(let graphQLData): guard let listResponse = try? AppSyncListResponse.initWithMetadata(type: Element.self, graphQLData: graphQLData, - apiName: self.apiName) else { + apiName: self.apiName, + authMode: self.authMode) else { throw CoreError.listOperation(""" The AppSync response return successfully, but could not decode to AWSAppSyncListResponse from: \(graphQLData) @@ -196,7 +209,8 @@ public class AppSyncListProvider: ModelListProvider { filter: filter, limit: limit, nextToken: nextToken, - apiName: apiName) + apiName: apiName, + authMode: authMode) do { let graphQLResponse = try await Amplify.API.query(request: request) switch graphQLResponse { @@ -225,7 +239,8 @@ public class AppSyncListProvider: ModelListProvider { let metadata = AppSyncListDecoder.Metadata.init( appSyncAssociatedIdentifiers: associatedIdentifiers, appSyncAssociatedFields: associatedFields, - apiName: apiName) + apiName: apiName, + authMode: authMode) var container = encoder.singleValueContainer() try container.encode(metadata) case .loaded(let elements, _, _): diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListResponse.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListResponse.swift index 7f83ab4ce7..a30821853d 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListResponse.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListResponse.swift @@ -7,6 +7,7 @@ import Amplify import Foundation +import AWSPluginsCore /// Resembles the AppSync's GraphQL response for a list operation. struct AppSyncListResponse: Codable { @@ -25,10 +26,14 @@ extension AppSyncListResponse { /// model metadata on its array associations. static func initWithMetadata(type: Element.Type, graphQLData: JSONValue, - apiName: String?) throws -> AppSyncListResponse { + apiName: String?, + authMode: AWSAuthorizationType?) throws -> AppSyncListResponse { var elements = [Element]() if case let .array(jsonArray) = graphQLData["items"] { - let jsonArrayWithMetadata = AppSyncModelMetadataUtils.addMetadata(toModelArray: jsonArray, apiName: apiName) + let jsonArrayWithMetadata = AppSyncModelMetadataUtils.addMetadata( + toModelArray: jsonArray, + apiName: apiName, + authMode: authMode) let encoder = JSONEncoder() encoder.dateEncodingStrategy = ModelDateFormatting.encodingStrategy diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelDecoder.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelDecoder.swift index b70728b052..dab2e44756 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelDecoder.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelDecoder.swift @@ -7,6 +7,7 @@ import Foundation import Amplify +import AWSPluginsCore /// This decoder is registered and used to detect various data payloads to store /// inside an `AppSyncModelProvider` when decoding to the `LazyReference` as a "not yet loaded" Reference. If the data payload @@ -17,11 +18,16 @@ public struct AppSyncModelDecoder: ModelProviderDecoder { struct Metadata: Codable { let identifiers: [LazyReferenceIdentifier] let apiName: String? + let authMode: AWSAuthorizationType? let source: String - init(identifiers: [LazyReferenceIdentifier], apiName: String?, source: String = ModelProviderRegistry.DecoderSource.appSync) { + init(identifiers: [LazyReferenceIdentifier], + apiName: String?, + authMode: AWSAuthorizationType?, + source: String = ModelProviderRegistry.DecoderSource.appSync) { self.identifiers = identifiers self.apiName = apiName + self.authMode = authMode self.source = source } } diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelMetadata.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelMetadata.swift index d03f8623ac..20f0b88dd0 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelMetadata.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelMetadata.swift @@ -7,6 +7,7 @@ import Amplify import Foundation +import AWSPluginsCore /// Holds the methods to traverse and maniupulate the response data object by injecting public struct AppSyncModelMetadataUtils { @@ -25,15 +26,17 @@ public struct AppSyncModelMetadataUtils { } static func addMetadata(toModelArray graphQLDataArray: [JSONValue], - apiName: String?) -> [JSONValue] { + apiName: String?, + authMode: AWSAuthorizationType?) -> [JSONValue] { return graphQLDataArray.map { (graphQLData) -> JSONValue in - addMetadata(toModel: graphQLData, apiName: apiName) + addMetadata(toModel: graphQLData, apiName: apiName, authMode: authMode) } } static func addMetadata( toModel graphQLData: JSONValue, apiName: String?, + authMode: AWSAuthorizationType?, source: String = ModelProviderRegistry.DecoderSource.appSync) -> JSONValue { guard case var .object(modelJSON) = graphQLData else { @@ -89,6 +92,7 @@ public struct AppSyncModelMetadataUtils { if let modelIdentifierMetadata = createModelIdentifierMetadata(associatedModelType, modelObject: modelObject, apiName: apiName, + authMode: authMode, source: source) { if let serializedMetadata = try? encoder.encode(modelIdentifierMetadata), let metadataJSON = try? decoder.decode(JSONValue.self, from: serializedMetadata) { @@ -104,7 +108,9 @@ public struct AppSyncModelMetadataUtils { // add metadata to its fields, to create not loaded LazyReference objects // only if the model type allowsfor lazy loading functionality else if associatedModelType.rootPath != nil { - let nestedModelWithMetadata = addMetadata(toModel: nestedModelJSON, apiName: apiName) + let nestedModelWithMetadata = addMetadata(toModel: nestedModelJSON, + apiName: apiName, + authMode: authMode) modelJSON.updateValue(nestedModelWithMetadata, forKey: modelField.name) } // otherwise do nothing to the data. @@ -120,7 +126,8 @@ public struct AppSyncModelMetadataUtils { if modelJSON[modelField.name] == nil { let appSyncModelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: identifiers, appSyncAssociatedFields: modelField.associatedFieldNames, - apiName: apiName) + apiName: apiName, + authMode: authMode) if let serializedMetadata = try? encoder.encode(appSyncModelMetadata), let metadataJSON = try? decoder.decode(JSONValue.self, from: serializedMetadata) { log.verbose("Adding [\(modelField.name): \(metadataJSON)]") @@ -141,13 +148,16 @@ public struct AppSyncModelMetadataUtils { associatedModelType.rootPath != nil { for (index, item) in graphQLDataArray.enumerated() { - let modelJSON = AppSyncModelMetadataUtils.addMetadata(toModel: item, apiName: apiName) + let modelJSON = AppSyncModelMetadataUtils.addMetadata(toModel: item, + apiName: apiName, + authMode: authMode) graphQLDataArray[index] = modelJSON } graphQLDataObject["items"] = JSONValue.array(graphQLDataArray) let payload = AppSyncListPayload(graphQLData: JSONValue.object(graphQLDataObject), apiName: apiName, + authMode: authMode, variables: nil) if let serializedPayload = try? encoder.encode(payload), @@ -171,6 +181,7 @@ public struct AppSyncModelMetadataUtils { static func createModelIdentifierMetadata(_ associatedModel: Model.Type, modelObject: [String: JSONValue], apiName: String?, + authMode: AWSAuthorizationType?, source: String) -> AppSyncModelDecoder.Metadata? { let primarykeys = associatedModel.schema.primaryKey var identifiers = [LazyReferenceIdentifier]() @@ -188,6 +199,7 @@ public struct AppSyncModelMetadataUtils { return AppSyncModelDecoder.Metadata( identifiers: identifiers, apiName: apiName, + authMode: authMode, source: source) } else { return nil diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelProvider.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelProvider.swift index 680c5afd14..cdcaa16d88 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelProvider.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncModelProvider.swift @@ -12,6 +12,7 @@ import AWSPluginsCore public class AppSyncModelProvider: ModelProvider { let apiName: String? + let authMode: AWSAuthorizationType? let source: String var loadedState: ModelProviderState @@ -20,12 +21,14 @@ public class AppSyncModelProvider: ModelProvider { self.loadedState = .notLoaded(identifiers: metadata.identifiers) self.apiName = metadata.apiName self.source = metadata.source + self.authMode = metadata.authMode } // Creates a "loaded" provider init(model: ModelType?) { self.loadedState = .loaded(model: model) self.apiName = nil + self.authMode = nil self.source = ModelProviderRegistry.DecoderSource.appSync } @@ -41,7 +44,8 @@ public class AppSyncModelProvider: ModelProvider { } let request = GraphQLRequest.getRequest(ModelType.self, byIdentifiers: identifiers, - apiName: apiName) + apiName: apiName, + authMode: authMode) log.verbose("Loading \(ModelType.modelName) with \(identifiers)") let graphQLResponse = try await Amplify.API.query(request: request) switch graphQLResponse { @@ -67,6 +71,7 @@ public class AppSyncModelProvider: ModelProvider { let metadata = AppSyncModelDecoder.Metadata( identifiers: identifiers ?? [], apiName: apiName, + authMode: authMode, source: source) try metadata.encode(to: encoder) diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Decode/GraphQLResponseDecoder+DecodeData.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Decode/GraphQLResponseDecoder+DecodeData.swift index ea37cc8343..425a0c46c5 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Decode/GraphQLResponseDecoder+DecodeData.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Decode/GraphQLResponseDecoder+DecodeData.swift @@ -52,22 +52,26 @@ extension GraphQLResponseDecoder { modelJSON = AppSyncModelMetadataUtils.addMetadata( toModel: item, apiName: request.apiName, + authMode: request.authMode as? AWSAuthorizationType, source: ModelProviderRegistry.DecoderSource.dataStore) } else { modelJSON = AppSyncModelMetadataUtils.addMetadata( toModel: item, - apiName: request.apiName) + apiName: request.apiName, + authMode: request.authMode as? AWSAuthorizationType) } graphQLDataArray[index] = modelJSON } graphQLDataObject["items"] = JSONValue.array(graphQLDataArray) let payload = AppSyncListPayload(graphQLData: JSONValue.object(graphQLDataObject), apiName: request.apiName, + authMode: request.authMode as? AWSAuthorizationType, variables: try getVariablesJSON()) serializedJSON = try encoder.encode(payload) } else if AppSyncModelMetadataUtils.shouldAddMetadata(toModel: graphQLData) { // 4 let modelJSON = AppSyncModelMetadataUtils.addMetadata(toModel: graphQLData, - apiName: request.apiName) + apiName: request.apiName, + authMode: request.authMode as? AWSAuthorizationType) serializedJSON = try encoder.encode(modelJSON) } else { // 5 serializedJSON = try encoder.encode(graphQLData) diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Utils/GraphQLRequest+toListQuery.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Utils/GraphQLRequest+toListQuery.swift index 130e3d4167..d023097d9d 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Utils/GraphQLRequest+toListQuery.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Utils/GraphQLRequest+toListQuery.swift @@ -16,7 +16,8 @@ extension GraphQLRequest { filter: [String: Any]? = nil, limit: Int? = nil, nextToken: String? = nil, - apiName: String? = nil) -> GraphQLRequest { + apiName: String? = nil, + authMode: AWSAuthorizationType?) -> GraphQLRequest { var documentBuilder = ModelBasedGraphQLDocumentBuilder(modelSchema: modelSchema, operationType: .query) documentBuilder.add(decorator: DirectiveNameDecorator(type: .list)) @@ -29,12 +30,14 @@ extension GraphQLRequest { document: document.stringValue, variables: document.variables, responseType: responseType.self, - decodePath: document.name) + decodePath: document.name, + authMode: authMode) } static func getRequest(_ modelType: M.Type, byIdentifiers identifiers: [LazyReferenceIdentifier], - apiName: String?) -> GraphQLRequest { + apiName: String?, + authMode: AWSAuthorizationType?) -> GraphQLRequest { var documentBuilder = ModelBasedGraphQLDocumentBuilder(modelSchema: modelType.schema, operationType: .query) documentBuilder.add(decorator: DirectiveNameDecorator(type: .get)) @@ -46,6 +49,7 @@ extension GraphQLRequest { document: document.stringValue, variables: document.variables, responseType: M?.self, - decodePath: document.name) + decodePath: document.name, + authMode: authMode) } } diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListDecoderTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListDecoderTests.swift index 0ce8f19840..87b2917b08 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListDecoderTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListDecoderTests.swift @@ -42,7 +42,7 @@ class AppSyncListDecoderTests: XCTestCase { ], "nextToken": "nextToken" ] - let appSyncPayload = AppSyncListPayload(graphQLData: json, apiName: nil, variables: nil) + let appSyncPayload = AppSyncListPayload(graphQLData: json, apiName: nil, authMode: nil, variables: nil) let data = try encoder.encode(appSyncPayload) let harness = try decoder.decode(AppSyncListDecoderHarness.self, from: data) @@ -70,7 +70,7 @@ class AppSyncListDecoderTests: XCTestCase { ], "nextToken": "nextToken" ] - let appSyncPayload = AppSyncListPayload(graphQLData: json, apiName: nil, variables: nil) + let appSyncPayload = AppSyncListPayload(graphQLData: json, apiName: nil, authMode: nil, variables: nil) let data = try encoder.encode(appSyncPayload) let result = try decoder.decode(AppSyncListDecoderHarness.self, from: data) XCTAssertNil(result.listProvider) @@ -79,7 +79,8 @@ class AppSyncListDecoderTests: XCTestCase { func testShouldDecodeFromModelMetadata() throws { let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], appSyncAssociatedFields: ["post"], - apiName: "apiName") + apiName: "apiName", + authMode: nil) let data = try encoder.encode(modelMetadata) let harness = try decoder.decode(AppSyncListDecoderHarness.self, from: data) XCTAssertNotNil(harness.listProvider) diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListPayloadTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListPayloadTests.swift index 86fd5f1e6c..51fac4dac3 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListPayloadTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListPayloadTests.swift @@ -22,6 +22,7 @@ class AppSyncListPayloadTests: XCTestCase { ] let payload = AppSyncListPayload(graphQLData: JSONValue.null, apiName: "apiName", + authMode: nil, variables: variables) guard let limit = payload.limit else { @@ -51,7 +52,8 @@ class AppSyncListPayloadTests: XCTestCase { "missingLimit": 500 ] let payload = AppSyncListPayload(graphQLData: JSONValue.null, - apiName: "apiName", + apiName: "apiName", + authMode: nil, variables: variables) XCTAssertNil(payload.graphQLFilter) @@ -60,7 +62,8 @@ class AppSyncListPayloadTests: XCTestCase { func testRetrieveNilFilterAndLimit_MissingVariables() { let payload = AppSyncListPayload(graphQLData: JSONValue.null, - apiName: "apiName", + apiName: "apiName", + authMode: nil, variables: nil) XCTAssertNil(payload.graphQLFilter) diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderPaginationTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderPaginationTests.swift index 47710842e3..57843d9c92 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderPaginationTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderPaginationTests.swift @@ -36,7 +36,8 @@ extension AppSyncListProviderTests { func testNotLoadedStateHasNextPageFalse() { let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], appSyncAssociatedFields: ["post"], - apiName: "apiName") + apiName: "apiName", + authMode: nil) let provider = AppSyncListProvider(metadata: modelMetadata) guard case .notLoaded = provider.loadedState else { XCTFail("Should not be loaded") @@ -109,13 +110,16 @@ extension AppSyncListProviderTests { func testLoadedStateGetNextPageFailure_GraphQLErrorResponse() async { mockAPIPlugin.responders[.queryRequestResponse] = - QueryRequestResponder> { _ in + QueryRequestResponder> { request in + + XCTAssertEqual(request.apiName, "apiName") + XCTAssertEqual(request.authMode as? AWSAuthorizationType, .amazonCognitoUserPools) let event: GraphQLOperation>.OperationResult = .success( .failure(GraphQLResponseError.error([GraphQLError]()))) return event } let elements = [Comment4(content: "content")] - let provider = AppSyncListProvider(elements: elements, nextToken: "nextToken") + let provider = AppSyncListProvider(elements: elements, nextToken: "nextToken", apiName: "apiName", authMode: .amazonCognitoUserPools) guard case .loaded = provider.loadedState else { XCTFail("Should be loaded") return @@ -138,7 +142,8 @@ extension AppSyncListProviderTests { func testNotLoadedStateGetNextPageFailure() async { let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], appSyncAssociatedFields: ["post"], - apiName: "apiName") + apiName: "apiName", + authMode: nil) let provider = AppSyncListProvider(metadata: modelMetadata) guard case .notLoaded = provider.loadedState else { XCTFail("Should not be loaded") diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderTests.swift index 0116c9c206..1e02c9beaf 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderTests.swift @@ -59,7 +59,10 @@ class AppSyncListProviderTests: XCTestCase { ], "limit": 500 ] - let appSyncPayload = AppSyncListPayload(graphQLData: json, apiName: "apiName", variables: variables) + let appSyncPayload = AppSyncListPayload(graphQLData: json, + apiName: "apiName", + authMode: nil, + variables: variables) let provider = try AppSyncListProvider(payload: appSyncPayload) guard case .loaded(let elements, let nextToken, let filter) = provider.loadedState else { XCTFail("Should be in loaded state") @@ -82,7 +85,10 @@ class AppSyncListProviderTests: XCTestCase { ], "nextToken": "nextToken" ] - let appSyncPayload = AppSyncListPayload(graphQLData: json, apiName: nil, variables: nil) + let appSyncPayload = AppSyncListPayload(graphQLData: json, + apiName: nil, + authMode: nil, + variables: nil) do { _ = try AppSyncListProvider(payload: appSyncPayload) } catch _ as DecodingError { @@ -95,7 +101,8 @@ class AppSyncListProviderTests: XCTestCase { func testInitWithModelMetadataShouldBeNotLoadedState() throws { let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], appSyncAssociatedFields: ["post"], - apiName: "apiName") + apiName: "apiName", + authMode: nil) let provider = AppSyncListProvider(metadata: modelMetadata) guard case .notLoaded(let associatedIdentifiers, let associatedFields) = provider.loadedState else { XCTFail("Should be in not loaded state") @@ -120,7 +127,9 @@ class AppSyncListProviderTests: XCTestCase { func testNotLoadedStateLoadSuccess() async throws { mockAPIPlugin.responders[.queryRequestResponse] = - QueryRequestResponder { _ in + QueryRequestResponder { request in + XCTAssertEqual(request.apiName, "apiName") + XCTAssertEqual(request.authMode as? AWSAuthorizationType, .amazonCognitoUserPools) let json: JSONValue = [ "items": [ [ @@ -138,7 +147,8 @@ class AppSyncListProviderTests: XCTestCase { } let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], appSyncAssociatedFields: ["post"], - apiName: "apiName") + apiName: "apiName", + authMode: .amazonCognitoUserPools) let provider = AppSyncListProvider(metadata: modelMetadata) guard case .notLoaded = provider.loadedState else { XCTFail("Should not be loaded") @@ -175,7 +185,8 @@ class AppSyncListProviderTests: XCTestCase { } let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], appSyncAssociatedFields: ["post"], - apiName: "apiName") + apiName: "apiName", + authMode: nil) let provider = AppSyncListProvider(metadata: modelMetadata) guard case .notLoaded = provider.loadedState else { XCTFail("Should not be loaded") @@ -222,7 +233,8 @@ class AppSyncListProviderTests: XCTestCase { } let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], appSyncAssociatedFields: ["post"], - apiName: "apiName") + apiName: "apiName", + authMode: nil) let provider = AppSyncListProvider(metadata: modelMetadata) guard case .notLoaded = provider.loadedState else { XCTFail("Should not be loaded") @@ -259,7 +271,8 @@ class AppSyncListProviderTests: XCTestCase { } let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], appSyncAssociatedFields: ["post"], - apiName: "apiName") + apiName: "apiName", + authMode: nil) let provider = AppSyncListProvider(metadata: modelMetadata) guard case .notLoaded = provider.loadedState else { XCTFail("Should not be loaded") @@ -294,8 +307,9 @@ class AppSyncListProviderTests: XCTestCase { return event } let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], - appSyncAssociatedFields: ["post"], - apiName: "apiName") + appSyncAssociatedFields: ["post"], + apiName: "apiName", + authMode: nil) let provider = AppSyncListProvider(metadata: modelMetadata) guard case .notLoaded = provider.loadedState else { XCTFail("Should not be loaded") @@ -344,7 +358,8 @@ class AppSyncListProviderTests: XCTestCase { } let modelMetadata = AppSyncListDecoder.Metadata(appSyncAssociatedIdentifiers: ["postId"], appSyncAssociatedFields: ["post"], - apiName: "apiName") + apiName: "apiName", + authMode: nil) let provider = AppSyncListProvider(metadata: modelMetadata) guard case .notLoaded = provider.loadedState else { XCTFail("Should not be loaded") diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListResponseTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListResponseTests.swift index 26ffd3c4e6..37910a8fc9 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListResponseTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListResponseTests.swift @@ -38,7 +38,8 @@ class AppSyncListResponseTests: XCTestCase { ] let listResponse = try AppSyncListResponse.initWithMetadata(type: Post4.self, graphQLData: graphQLData, - apiName: "apiName") + apiName: "apiName", + authMode: nil) XCTAssertEqual(listResponse.items.count, 2) XCTAssertEqual(listResponse.nextToken, "nextToken") @@ -59,8 +60,9 @@ class AppSyncListResponseTests: XCTestCase { ] ] let listResponse = try AppSyncListResponse.initWithMetadata(type: Comment4.self, - graphQLData: graphQLData, - apiName: "apiName") + graphQLData: graphQLData, + apiName: "apiName", + authMode: nil) XCTAssertEqual(listResponse.items.count, 2) XCTAssertNil(listResponse.nextToken) diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncModelMetadataTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncModelMetadataTests.swift index 8f1f3a40a4..f2ca4b3148 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncModelMetadataTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncModelMetadataTests.swift @@ -54,19 +54,23 @@ class ModelMetadataTests: XCTestCase { "__typename": "Post4" ] ] - let posts = AppSyncModelMetadataUtils.addMetadata(toModelArray: jsonArray, apiName: "apiName") + let posts = AppSyncModelMetadataUtils.addMetadata(toModelArray: jsonArray, + apiName: "apiName", + authMode: .amazonCognitoUserPools) XCTAssertEqual(posts.count, 2) for post in posts { guard case .object(let associationData) = post["comments"], case .array(let associatedFields) = associationData["appSyncAssociatedFields"], case .array(let associatedIdentifiers) = associationData["appSyncAssociatedIdentifiers"], - case .string(let apiName) = associationData["apiName"] else { + case .string(let apiName) = associationData["apiName"], + case .string(let authMode) = associationData["authMode"] else { XCTFail("Missing association metadata for comments") return } XCTAssertEqual(associatedIdentifiers[0], "postId") XCTAssertEqual(associatedFields, ["post"]) XCTAssertEqual(apiName, "apiName") + XCTAssertEqual(authMode, "AMAZON_COGNITO_USER_POOLS") } } @@ -77,22 +81,24 @@ class ModelMetadataTests: XCTestCase { "__typename": "Post4" ] - let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName") + let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName", authMode: .apiKey) guard case .object(let associationData) = post["comments"], case .array(let associatedFields) = associationData["appSyncAssociatedFields"], case .array(let associatedIdentifiers) = associationData["appSyncAssociatedIdentifiers"], - case .string(let apiName) = associationData["apiName"] else { + case .string(let apiName) = associationData["apiName"], + case .string(let authMode) = associationData["authMode"] else { XCTFail("Missing association metadata for comments") return } XCTAssertEqual(associatedIdentifiers[0], "postId") XCTAssertEqual(associatedFields, ["post"]) XCTAssertEqual(apiName, "apiName") + XCTAssertEqual(authMode, "API_KEY") } func testAddMetadataToModelNoOp_NotAnObject() { let json: JSONValue = "notAnObject" - let result = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName") + let result = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName", authMode: nil) XCTAssertEqual(result, "notAnObject") } @@ -102,7 +108,7 @@ class ModelMetadataTests: XCTestCase { "title": JSONValue.init(stringLiteral: "title") ] - let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName") + let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName", authMode: nil) guard case .object(let postObject) = post, case .string = postObject["id"], case .string = postObject["title"] else { @@ -119,7 +125,7 @@ class ModelMetadataTests: XCTestCase { "__typename": "InvalidModelName" ] - let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName") + let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName", authMode: nil) guard case .object(let postObject) = post, case .string = postObject["id"], case .string = postObject["title"], @@ -136,7 +142,7 @@ class ModelMetadataTests: XCTestCase { "__typename": "Post4" ] - let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName") + let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName", authMode: nil) guard case .object(let postObject) = post, case .string = postObject["title"], case .string = postObject["__typename"] else { @@ -159,7 +165,7 @@ class ModelMetadataTests: XCTestCase { ] ] - let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName") + let post = AppSyncModelMetadataUtils.addMetadata(toModel: json, apiName: "apiName", authMode: nil) guard case .object(let associationData) = post["comments"], associationData["appSyncAssociatedField"] == nil, associationData["appSyncAssociatedIdentifiers"] == nil, diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/GraphQLRequestToListQueryTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/GraphQLRequestToListQueryTests.swift index 955a1cf286..783ceb0c0c 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/GraphQLRequestToListQueryTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/GraphQLRequestToListQueryTests.swift @@ -28,7 +28,8 @@ class GraphQLRequestToListQueryTests: XCTestCase { modelSchema: Comment4.schema, filter: predicate.graphQLFilter(for: Comment4.schema), limit: 1_000, - apiName: "apiName") + apiName: "apiName", + authMode: .awsIAM) XCTAssertNotNil(request) let expectedDocument = """ query ListComment4s($filter: ModelComment4FilterInput, $limit: Int) { @@ -50,6 +51,7 @@ class GraphQLRequestToListQueryTests: XCTestCase { XCTAssertEqual(request.document, expectedDocument) XCTAssertEqual(request.decodePath, "listComment4s") XCTAssertEqual(request.apiName, "apiName") + XCTAssertEqual(request.authMode as? AWSAuthorizationType, .awsIAM) guard let variables = request.variables else { XCTFail("The document doesn't contain variables") return @@ -76,7 +78,8 @@ class GraphQLRequestToListQueryTests: XCTestCase { let request = GraphQLRequest.listQuery(responseType: List.self, modelSchema: Comment4.schema, nextToken: "nextToken", - apiName: "apiName") + apiName: "apiName", + authMode: .amazonCognitoUserPools) XCTAssertNotNil(request) let expectedDocument = """ query ListComment4s($limit: Int, $nextToken: String) { @@ -98,6 +101,7 @@ class GraphQLRequestToListQueryTests: XCTestCase { XCTAssertEqual(request.document, expectedDocument) XCTAssertEqual(request.decodePath, "listComment4s") XCTAssertEqual(request.apiName, "apiName") + XCTAssertEqual(request.authMode as? AWSAuthorizationType, .amazonCognitoUserPools) guard let variables = request.variables else { XCTFail("The document doesn't contain variables") return @@ -119,7 +123,8 @@ class GraphQLRequestToListQueryTests: XCTestCase { filter: previousFilter, limit: 1_000, nextToken: "nextToken", - apiName: "apiName") + apiName: "apiName", + authMode: .function) XCTAssertNotNil(request) let expectedDocument = """ query ListComment4s($filter: ModelComment4FilterInput, $limit: Int, $nextToken: String) { @@ -141,6 +146,7 @@ class GraphQLRequestToListQueryTests: XCTestCase { XCTAssertEqual(request.document, expectedDocument) XCTAssertEqual(request.decodePath, "listComment4s") XCTAssertEqual(request.apiName, "apiName") + XCTAssertEqual(request.authMode as? AWSAuthorizationType, .function) guard let variables = request.variables else { XCTFail("The document doesn't contain variables") return diff --git a/AmplifyPlugins/Core/AWSPluginsCore/Auth/AWSAuthorizationType.swift b/AmplifyPlugins/Core/AWSPluginsCore/Auth/AWSAuthorizationType.swift index 465cc35337..10e03c70a9 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/Auth/AWSAuthorizationType.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/Auth/AWSAuthorizationType.swift @@ -46,6 +46,8 @@ public enum AWSAuthorizationType: String, AuthorizationMode { extension AWSAuthorizationType: CaseIterable { } +extension AWSAuthorizationType: Codable { } + /// Indicates whether the authotization type requires the auth plugin to operate. extension AWSAuthorizationType { public var requiresAuthPlugin: Bool {