// // Copyright Amazon.com Inc. or its affiliates. // All Rights Reserved. // // SPDX-License-Identifier: Apache-2.0 // import Foundation import Amplify import AWSPluginsCore class GraphQLResponseDecoder { let request: GraphQLOperationRequest var response: Data let decoder = JSONDecoder() let encoder = JSONEncoder() let modelName: String? public init(request: GraphQLOperationRequest, response: Data = Data()) { self.request = request self.response = response decoder.dateDecodingStrategy = ModelDateFormatting.decodingStrategy encoder.dateEncodingStrategy = ModelDateFormatting.encodingStrategy if let pluginOptions = request.options.pluginOptions as? AWSPluginOptions, let modelName = pluginOptions.modelName { self.modelName = modelName } else { self.modelName = nil } } func appendResponse(_ data: Data) { response.append(data) } func decodeToGraphQLResponse() throws -> GraphQLResponse { let appSyncGraphQLResponse = try AWSAppSyncGraphQLResponse.decodeToAWSAppSyncGraphQLResponse(response: response) switch appSyncGraphQLResponse { case .data(let data): return try decodeData(data) case .errors(let errors): return try decodeErrors(errors) case .partial(let data, let errors): return try decodePartial(graphQLData: data, graphQLErrors: errors) case .invalidResponse: guard let rawGraphQLResponseString = String(data: response, encoding: .utf8) else { throw APIError.operationError( "Could not get the String representation of the GraphQL response", "") } throw APIError.unknown("The service returned some data without any `data` and `errors`", """ The service did not return an expected GraphQL response: \ \(rawGraphQLResponseString) """) } } func decodeData(_ graphQLData: [String: JSONValue]) throws -> GraphQLResponse { do { let responseData = try decodeToResponseType(graphQLData) return GraphQLResponse.success(responseData) } catch let decodingError as DecodingError { let error = APIError(error: decodingError) guard let rawGraphQLResponseString = String(data: response, encoding: .utf8) else { throw APIError.operationError( "Could not get the String representation of the GraphQL response", "") } return GraphQLResponse.failure(.transformationError(rawGraphQLResponseString, error)) } catch { throw error } } func decodeErrors(_ graphQLErrors: [JSONValue]) throws -> GraphQLResponse { let responseErrors = try GraphQLErrorDecoder.decodeErrors(graphQLErrors: graphQLErrors) return GraphQLResponse.failure(.error(responseErrors)) } func decodePartial(graphQLData: [String: JSONValue], graphQLErrors: [JSONValue]) throws -> GraphQLResponse { do { if let first = graphQLData.first, case .null = first.value { let responseErrors = try GraphQLErrorDecoder.decodeErrors(graphQLErrors: graphQLErrors) return GraphQLResponse.failure(.error(responseErrors)) } let responseData = try decodeToResponseType(graphQLData) let responseErrors = try GraphQLErrorDecoder.decodeErrors(graphQLErrors: graphQLErrors) return GraphQLResponse.failure(.partial(responseData, responseErrors)) } catch let decodingError as DecodingError { let error = APIError(error: decodingError) guard let rawGraphQLResponseString = String(data: response, encoding: .utf8) else { throw APIError.operationError( "Could not get the String representation of the GraphQL response", "") } return GraphQLResponse.failure(.transformationError(rawGraphQLResponseString, error)) } catch { throw error } } }