diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Configuration/AWSAPICategoryPluginConfiguration.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Configuration/AWSAPICategoryPluginConfiguration.swift index b4aa82783b..200a813e96 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Configuration/AWSAPICategoryPluginConfiguration.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Configuration/AWSAPICategoryPluginConfiguration.swift @@ -72,7 +72,7 @@ public struct AWSAPICategoryPluginConfiguration { self.authService = authService } - /// Registers an interceptor for the provided API endpoint + /// Registers an customer interceptor for the provided API endpoint /// - Parameter interceptor: operation interceptor used to decorate API requests /// - Parameter toEndpoint: API endpoint name mutating func addInterceptor(_ interceptor: URLRequestInterceptor, @@ -84,18 +84,17 @@ public struct AWSAPICategoryPluginConfiguration { interceptors[apiName]?.addInterceptor(interceptor) } - /// Returns all the customer defined interceptors registered for `apiName` API endpoint + /// Returns all the interceptors registered for `apiName` API endpoint /// - Parameter apiName: API endpoint name - /// - Returns: request interceptors + /// - Returns: Optional AWSAPIEndpointInterceptors for the apiName internal func interceptorsForEndpoint(named apiName: APIEndpointName) -> AWSAPIEndpointInterceptors? { return interceptors[apiName] } - /// Returns customer defined interceptors for the provided endpointConfig + /// Returns the interceptors for the provided endpointConfig /// - Parameters: /// - endpointConfig: endpoint configuration - /// - Throws: PluginConfigurationError in case of failure building an instance of AWSAuthorizationConfiguration - /// - Returns: An array of URLRequestInterceptor + /// - Returns: Optional AWSAPIEndpointInterceptors for the endpointConfig internal func interceptorsForEndpoint(withConfig endpointConfig: EndpointConfig) -> AWSAPIEndpointInterceptors? { return interceptorsForEndpoint(named: endpointConfig.name) } @@ -105,7 +104,7 @@ public struct AWSAPICategoryPluginConfiguration { /// - endpointConfig: endpoint configuration /// - authType: overrides the registered auth interceptor /// - Throws: PluginConfigurationError in case of failure building an instance of AWSAuthorizationConfiguration - /// - Returns: An array of URLRequestInterceptor + /// - Returns: Optional AWSAPIEndpointInterceptors for the endpointConfig and authType internal func interceptorsForEndpoint( withConfig endpointConfig: EndpointConfig, authType: AWSAuthorizationType diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSGraphQLOperation.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSGraphQLOperation.swift index 0ecf4771fb..51386e8c0e 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSGraphQLOperation.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSGraphQLOperation.swift @@ -87,7 +87,7 @@ final public class AWSGraphQLOperation: GraphQLOperation { task.resume() case .failure(let error): dispatch(result: .failure(error)) - cancel() + finish() } } diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSRESTOperation.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSRESTOperation.swift index 6beb8b8b79..38fd7160bd 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSRESTOperation.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSRESTOperation.swift @@ -45,99 +45,48 @@ final public class AWSRESTOperation: AmplifyOperation< return } - // Validate the request - do { - try request.validate() - } catch let error as APIError { - dispatch(result: .failure(error)) - finish() - return - } catch { - dispatch(result: .failure(APIError.unknown("Could not validate request", "", nil))) - finish() - return - } - - // Retrieve endpoint configuration - let endpointConfig: AWSAPICategoryPluginConfiguration.EndpointConfig - let amplifyInterceptors: [URLRequestInterceptor] - let customerInterceptors: [URLRequestInterceptor] - let checksumInterceptors: [URLRequestInterceptor] - do { - endpointConfig = try pluginConfig.endpoints.getConfig(for: request.apiName, endpointType: .rest) - let interceptorConfig = pluginConfig.interceptorsForEndpoint(withConfig: endpointConfig) - amplifyInterceptors = interceptorConfig?.amplifyInterceptors ?? [] - customerInterceptors = interceptorConfig?.interceptors ?? [] - checksumInterceptors = interceptorConfig?.checksumInterceptors ?? [] - } catch let error as APIError { - dispatch(result: .failure(error)) - finish() - return - } catch { - dispatch(result: .failure(APIError.unknown("Could not get endpoint configuration", "", nil))) - finish() - return - } - - // Construct URL with path - let url: URL - do { - url = try RESTOperationRequestUtils.constructURL( - for: endpointConfig.baseURL, - withPath: request.path, - withParams: request.queryParameters - ) - } catch let error as APIError { - dispatch(result: .failure(error)) - finish() - return - } catch { - let apiError = APIError.operationError("Failed to construct URL", "", error) - dispatch(result: .failure(apiError)) - finish() - return - } - - // Construct URL Request with url and request body - let urlRequest = RESTOperationRequestUtils.constructURLRequest( - with: url, - operationType: request.operationType, - requestPayload: request.body - ) - Task { - var finalResult: Result = .success(urlRequest) - // apply amplify interceptors - for interceptor in amplifyInterceptors { - finalResult = await finalResult.flatMapAsync { request in - await applyInterceptor(interceptor, request: request) + let urlRequest = validateRequest(request).flatMap(buildURLRequest(from:)) + let finalRequest = await getEndpointConfig(from: request).flatMapAsync { endpointConfig in + let interceptorConfig = pluginConfig.interceptorsForEndpoint(withConfig: endpointConfig) + let amplifyInterceptors = interceptorConfig?.amplifyInterceptors ?? [] + let customerInterceptors = interceptorConfig?.interceptors ?? [] + let checksumInterceptors = interceptorConfig?.checksumInterceptors ?? [] + + var finalResult = urlRequest + // apply amplify interceptors + for interceptor in amplifyInterceptors { + finalResult = await finalResult.flatMapAsync { request in + await applyInterceptor(interceptor, request: request) + } } - } - // apply customer headers - finalResult = finalResult.map { urlRequest in - var mutableRequest = urlRequest - for (key, value) in request.headers ?? [:] { - mutableRequest.setValue(value, forHTTPHeaderField: key) + // apply customer headers + finalResult = finalResult.map { urlRequest in + var mutableRequest = urlRequest + for (key, value) in request.headers ?? [:] { + mutableRequest.setValue(value, forHTTPHeaderField: key) + } + return mutableRequest } - return mutableRequest - } - // apply customer interceptors - for interceptor in customerInterceptors { - finalResult = await finalResult.flatMapAsync { request in - await applyInterceptor(interceptor, request: request) + // apply customer interceptors + for interceptor in customerInterceptors { + finalResult = await finalResult.flatMapAsync { request in + await applyInterceptor(interceptor, request: request) + } } - } - // apply checksum interceptor - for interceptor in checksumInterceptors { - finalResult = await finalResult.flatMapAsync { request in - await applyInterceptor(interceptor, request: request) + // apply checksum interceptor + for interceptor in checksumInterceptors { + finalResult = await finalResult.flatMapAsync { request in + await applyInterceptor(interceptor, request: request) + } } + return finalResult } - switch finalResult { + switch finalRequest { case .success(let finalRequest): if isCancelled { finish() @@ -150,8 +99,53 @@ final public class AWSRESTOperation: AmplifyOperation< mapper.addPair(operation: self, task: task) task.resume() case .failure(let error): + Amplify.API.log.debug("Dispatching error \(error)") dispatch(result: .failure(error)) - cancel() + finish() + } + } + } + + private func validateRequest(_ request: RESTOperationRequest) -> Result { + do { + try request.validate() + return .success(request) + } catch let error as APIError { + return .failure(error) + } catch { + return .failure(APIError.unknown("Could not validate request", "", nil)) + } + } + + private func getEndpointConfig( + from request: RESTOperationRequest + ) -> Result { + do { + return .success(try pluginConfig.endpoints.getConfig(for: request.apiName, endpointType: .rest)) + } catch let error as APIError { + return .failure(error) + } catch { + return .failure(APIError.unknown("Could not get endpoint configuration", "", nil)) + } + } + + private func buildURLRequest(from request: RESTOperationRequest) -> Result { + getEndpointConfig(from: request).flatMap { endpointConfig in + do { + let url = try RESTOperationRequestUtils.constructURL( + for: endpointConfig.baseURL, + withPath: request.path, + withParams: request.queryParameters + ) + return .success(RESTOperationRequestUtils.constructURLRequest( + with: url, + operationType: request.operationType, + requestPayload: request.body + )) + } catch let error as APIError { + return .failure(error) + } catch { + return .failure(APIError.operationError("Failed to construct URL", "", error)) } } } diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/Result+AsyncTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/Result+AsyncTests.swift index 148d630fff..5ae473b899 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/Result+AsyncTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/Result+AsyncTests.swift @@ -46,6 +46,7 @@ class ResultAsyncTests: XCTestCase { XCTFail("Should fail") case .failure(let error): XCTAssertTrue(error is TestError) + XCTAssertEqual(ObjectIdentifier(expectedError), ObjectIdentifier(error as! TestError)) } } }