// // Copyright Amazon.com Inc. or its affiliates. // All Rights Reserved. // // SPDX-License-Identifier: Apache-2.0 // import Amplify import AWSPluginsCore import Combine import Foundation class MockAPICategoryPlugin: MessageReporter, APICategoryPlugin, APICategoryReachabilityBehavior, APICategoryGraphQLBehaviorExtended { var authProviderFactory: APIAuthProviderFactory? var responders = [ResponderKeys: Any]() private var _reachabilityPublisher: Any? private var oidcProvider: Any? init(reachabilityPublisher: AnyPublisher) { self._reachabilityPublisher = reachabilityPublisher super.init() } // We're declaring this even though it's not strictly necessary to expose the no-arg initializer // to iOS 13+ test contexts. override init() { super.init() } // MARK: - Properties var key: String { return "MockAPICategoryPlugin" } func configure(using configuration: Any?) throws { notify("configure") } func reset() { notify("reset") listeners.set([]) } // MARK: - Request-based GraphQL methods func mutate(request: GraphQLRequest, listener: GraphQLOperation.ResultListener?) -> GraphQLOperation { // This is a really weighty notification message, but needed for tests to be able to assert that a particular // model is being mutated notify("mutate(request) document: \(request.document); variables: \(String(describing: request.variables))") if let responder = responders[.mutateRequestListener] as? MutateRequestListenerResponder { if let operation = responder.callback((request, listener)) { return operation } } let requestOptions = GraphQLOperationRequest.Options(pluginOptions: nil) let request = GraphQLOperationRequest(apiName: request.apiName, operationType: .mutation, document: request.document, variables: request.variables, responseType: request.responseType, options: requestOptions) let operation = MockGraphQLOperation(request: request, responseType: request.responseType) return operation } func mutate(request: GraphQLRequest) async throws -> GraphQLTask.Success { // This is a really weighty notification message, but needed for tests to be able to assert that a particular // model is being mutated notify("mutate(request) document: \(request.document); variables: \(String(describing: request.variables))") return .failure(.unknown("", "'", nil)) } func query(request: GraphQLRequest, listener: GraphQLOperation.ResultListener?) -> GraphQLOperation { notify("query(request:listener:) request: \(request)") if let responder = responders[.queryRequestListener] as? QueryRequestListenerResponder { if let operation = responder.callback((request, listener)) { return operation } } let requestOptions = GraphQLOperationRequest.Options(pluginOptions: nil) let request = GraphQLOperationRequest(apiName: request.apiName, operationType: .query, document: request.document, variables: request.variables, responseType: request.responseType, options: requestOptions) let operation = MockGraphQLOperation(request: request, responseType: request.responseType) return operation } func query(request: GraphQLRequest) async throws -> GraphQLTask.Success { notify("query(request:) request: \(request)") if let responder = responders[.queryRequestResponse] as? QueryRequestResponder { let result = responder.callback(request) switch result { case .success(let response): return response case .failure(let error): throw error } } return .failure(.unknown("", "", nil)) } func subscribe(request: GraphQLRequest, valueListener: GraphQLSubscriptionOperation.InProcessListener?, completionListener: GraphQLSubscriptionOperation.ResultListener?) -> GraphQLSubscriptionOperation { notify( """ subscribe(request:listener:) document: \(request.document); \ variables: \(String(describing: request.variables)) """ ) if let responder = responders[.subscribeRequestListener] as? SubscribeRequestListenerResponder { if let operation = responder.callback((request, valueListener, completionListener)) { return operation } } let requestOptions = GraphQLOperationRequest.Options(pluginOptions: nil) let request = GraphQLOperationRequest(apiName: request.apiName, operationType: .subscription, document: request.document, variables: request.variables, responseType: request.responseType, options: requestOptions) let operation = MockSubscriptionGraphQLOperation(request: request, responseType: request.responseType) return operation } func subscribe(request: GraphQLRequest) -> AmplifyAsyncThrowingSequence> { notify( """ subscribe(request:listener:) document: \(request.document); \ variables: \(String(describing: request.variables)) """ ) let requestOptions = GraphQLOperationRequest.Options(pluginOptions: nil) let request = GraphQLOperationRequest(apiName: request.apiName, operationType: .subscription, document: request.document, variables: request.variables, responseType: request.responseType, options: requestOptions) let taskRunner = MockAWSGraphQLSubscriptionTaskRunner(request: request) return taskRunner.sequence } public func reachabilityPublisher(for apiName: String?) -> AnyPublisher? { reachabilityPublisher() } public func reachabilityPublisher() -> AnyPublisher? { if let reachabilityPublisher = _reachabilityPublisher as? AnyPublisher { return reachabilityPublisher } else { return Just(ReachabilityUpdate(isOnline: true)).eraseToAnyPublisher() } } // MARK: - REST methods func get(request: RESTRequest, listener: RESTOperation.ResultListener?) -> RESTOperation { notify("get") let operationRequest = RESTOperationRequest(apiName: request.apiName, operationType: .get, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: operationRequest) return operation } func get(request: RESTRequest) async throws -> RESTTask.Success { notify("get") return Data() } func put(request: RESTRequest, listener: RESTOperation.ResultListener?) -> RESTOperation { notify("put") let request = RESTOperationRequest(apiName: request.apiName, operationType: .put, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) return operation } func put(request: RESTRequest) async throws -> RESTTask.Success { notify("put") let request = RESTOperationRequest(apiName: request.apiName, operationType: .put, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) let taskAdapter = AmplifyOperationTaskAdapter(operation: operation) return try await taskAdapter.value } func post(request: RESTRequest, listener: RESTOperation.ResultListener?) -> RESTOperation { notify("post") let request = RESTOperationRequest(apiName: request.apiName, operationType: .post, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) return operation } func post(request: RESTRequest) async throws -> RESTTask.Success { notify("post") let request = RESTOperationRequest(apiName: request.apiName, operationType: .post, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) let taskAdapter = AmplifyOperationTaskAdapter(operation: operation) return try await taskAdapter.value } func delete(request: RESTRequest, listener: RESTOperation.ResultListener?) -> RESTOperation { notify("delete") let request = RESTOperationRequest(apiName: request.apiName, operationType: .delete, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) return operation } func delete(request: RESTRequest) async throws -> RESTTask.Success { notify("delete") let request = RESTOperationRequest(apiName: request.apiName, operationType: .delete, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) let taskAdapter = AmplifyOperationTaskAdapter(operation: operation) return try await taskAdapter.value } func patch(request: RESTRequest, listener: RESTOperation.ResultListener?) -> RESTOperation { notify("patch") let request = RESTOperationRequest(apiName: request.apiName, operationType: .patch, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) return operation } func patch(request: RESTRequest) async throws -> RESTTask.Success { notify("patch") let request = RESTOperationRequest(apiName: request.apiName, operationType: .patch, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) let taskAdapter = AmplifyOperationTaskAdapter(operation: operation) return try await taskAdapter.value } func head(request: RESTRequest, listener: RESTOperation.ResultListener?) -> RESTOperation { notify("head") let request = RESTOperationRequest(apiName: request.apiName, operationType: .head, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) return operation } func head(request: RESTRequest) async throws -> RESTTask.Success { notify("head") let request = RESTOperationRequest(apiName: request.apiName, operationType: .head, path: request.path, queryParameters: request.queryParameters, body: request.body, options: RESTOperationRequest.Options()) let operation = MockAPIOperation(request: request) let taskAdapter = AmplifyOperationTaskAdapter(operation: operation) return try await taskAdapter.value } func add(interceptor: URLRequestInterceptor, for apiName: String) { notify("addInterceptor") } // MARK: - APICategoryAuthProviderFactoryBehavior func apiAuthProviderFactory() -> APIAuthProviderFactory { if let authProviderFactory = authProviderFactory { return authProviderFactory } else { return APIAuthProviderFactory() } } } class MockSecondAPICategoryPlugin: MockAPICategoryPlugin { override var key: String { return "MockSecondAPICategoryPlugin" } } class MockGraphQLOperation: GraphQLOperation { override func pause() { } override func resume() { } init(request: Request, responseType: R.Type) { super.init(categoryType: .api, eventName: HubPayload.EventName.API.mutate, request: request) } } class MockSubscriptionGraphQLOperation: GraphQLSubscriptionOperation { override func pause() { } override func resume() { } init(request: Request, responseType: R.Type) { super.init(categoryType: .api, eventName: HubPayload.EventName.API.subscribe, request: request) } } class MockAPIOperation: AmplifyOperation, RESTOperation { override func pause() { } override func resume() { } init(request: Request) { super.init(categoryType: .api, eventName: request.operationType.hubEventName, request: request) } } class MockAPIAuthProviderFactory: APIAuthProviderFactory { let oidcProvider: AmplifyOIDCAuthProvider? let functionProvider: AmplifyFunctionAuthProvider? init(oidcProvider: AmplifyOIDCAuthProvider? = nil, functionProvider: AmplifyFunctionAuthProvider? = nil) { self.oidcProvider = oidcProvider self.functionProvider = functionProvider } override func functionAuthProvider() -> AmplifyFunctionAuthProvider? { return functionProvider } override func oidcAuthProvider() -> AmplifyOIDCAuthProvider? { return oidcProvider } } class MockOIDCAuthProvider: AmplifyOIDCAuthProvider { var result: Result? func getLatestAuthToken() async throws -> String { if case let .success(token) = result { return token } else { return "token" } } } class MockFunctionAuthProvider: AmplifyFunctionAuthProvider { var result: Result? func getLatestAuthToken() async throws -> String { if case let .success(token) = result { return token } else { return "token" } } } class MockAWSGraphQLSubscriptionTaskRunner: InternalTaskRunner, InternalTaskAsyncThrowingSequence, InternalTaskThrowingChannel { public typealias Request = GraphQLOperationRequest public typealias InProcess = GraphQLSubscriptionEvent public var request: GraphQLOperationRequest public var context = InternalTaskAsyncThrowingSequenceContext>() func run() async throws { } init(request: GraphQLOperationRequest) { self.request = request } }