Skip to content

Commit

Permalink
Update HTTP response to be optional until created.
Browse files Browse the repository at this point in the history
  • Loading branch information
designatednerd committed Aug 13, 2020
1 parent 6ad66ee commit 0bde055
Show file tree
Hide file tree
Showing 13 changed files with 68 additions and 50 deletions.
4 changes: 2 additions & 2 deletions Sources/Apollo/ApolloErrorInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ public protocol ApolloErrorInterceptor {
/// - error: The received error
/// - chain: The chain the error was received on
/// - request: The request, as far as it was constructed
/// - response: The response, as far as it was constructed
/// - response: [optional] The response, if one was received
/// - completion: The completion closure to fire when the operation has completed. Note that if you call `retry` on the chain, you will not want to call the completion block in this method.
func handleErrorAsync<Operation: GraphQLOperation>(
error: Error,
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void)
}
4 changes: 2 additions & 2 deletions Sources/Apollo/ApolloInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ public protocol ApolloInterceptor: class {
/// - Parameters:
/// - chain: The chain the interceptor is a part of.
/// - request: The request, as far as it has been constructed
/// - response: The response, as far as it has been constructed
/// - response: [optional] The response, if received
/// - completion: The completion block to fire when data needs to be returned to the UI.
func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void)
}
4 changes: 2 additions & 2 deletions Sources/Apollo/AutomaticPersistedQueryInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class AutomaticPersistedQueryInterceptor: ApolloInterceptor {
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {

guard
Expand All @@ -22,7 +22,7 @@ public class AutomaticPersistedQueryInterceptor: ApolloInterceptor {
return
}

guard let result = response.parsedResponse else {
guard let result = response?.parsedResponse else {
// This is in the wrong order - this needs to be parsed before we can check it.
chain.handleErrorAsync(APQError.noParsedResponse,
request: request,
Expand Down
25 changes: 20 additions & 5 deletions Sources/Apollo/CodableParsingInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Foundation

public enum ParserError: Error {
case nilData
case noResponseToParse
case couldNotParseToLegacyJSON
}

Expand All @@ -18,25 +19,39 @@ public class CodableParsingInterceptor<FlexDecoder: FlexibleDecoder>: ApolloInte
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
guard !self.isCancelled else {
return
}

guard let data = response.rawData else {
completion(.failure(ParserError.nilData))
guard let createdResponse = response else {
chain.handleErrorAsync(ParserError.noResponseToParse,
request: request,
response: response,
completion: completion)
return
}

guard let data = response?.rawData else {
chain.handleErrorAsync(ParserError.nilData,
request: request,
response: createdResponse,
completion: completion)
return
}

do {
let parsedData = try GraphQLResult<Operation.Data>(from: data, decoder: self.decoder)
response.parsedResponse = parsedData
createdResponse.parsedResponse = parsedData
chain.proceedAsync(request: request,
response: response,
completion: completion)
} catch {
completion(.failure(error))
chain.handleErrorAsync(error,
request: request,
response: createdResponse,
completion: completion)
}
}
}
8 changes: 4 additions & 4 deletions Sources/Apollo/FinalizingInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ public class FinalizingInterceptor: ApolloInterceptor {
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {

guard let parsed = response.parsedResponse else {
chain.handleErrorAsync(FinalizationError.nilParsedValue(httpResponse: response.httpResponse,
rawData: response.rawData),
guard let parsed = response?.parsedResponse else {
chain.handleErrorAsync(FinalizationError.nilParsedValue(httpResponse: response?.httpResponse,
rawData: response?.rawData),
request: request,
response: response,
completion: completion)
Expand Down
10 changes: 5 additions & 5 deletions Sources/Apollo/HTTPResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import Foundation

/// Data about a response received by an HTTP request.
public class HTTPResponse<Operation: GraphQLOperation> {
public var httpResponse: HTTPURLResponse?
public var httpResponse: HTTPURLResponse
public var rawData: Data?
public var parsedResponse: GraphQLResult<Operation.Data>?

/// Designated initializer
///
/// - Parameters:
/// - response: [optional] The `HTTPURLResponse` received from the server. Will be nil if not yet received or if the response received was not an `HTTPURLResponse`.
/// - rawData: [optional] The raw, unparsed data received from the server. Will be nil if not yet received or if data received from the server was nil.
/// - parsedResponse: [optional] The response parsed into the `ParsedValue` type. Will be nil if not yet received, not yet parsed, or if parsing failed.
public init(response: HTTPURLResponse?,
/// - response: The `HTTPURLResponse` received from the server.
/// - rawData: [optional] The raw, unparsed data received from the server. Will be nil if data received from the server was nil.
/// - parsedResponse: [optional] The response parsed into the `ParsedValue` type. Will be nil if not yet parsed, or if parsing failed.
public init(response: HTTPURLResponse,
rawData: Data?,
parsedResponse: GraphQLResult<Operation.Data>?) {
self.httpResponse = response
Expand Down
2 changes: 1 addition & 1 deletion Sources/Apollo/LegacyCacheReadInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class LegacyCacheReadInterceptor: ApolloInterceptor {
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {

switch request.operation.operationType {
Expand Down
4 changes: 2 additions & 2 deletions Sources/Apollo/LegacyCacheWriteInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class LegacyCacheWriteInterceptor: ApolloInterceptor {
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {

guard request.cachePolicy != .fetchIgnoringCacheCompletely else {
Expand All @@ -26,7 +26,7 @@ public class LegacyCacheWriteInterceptor: ApolloInterceptor {
return
}

guard let data = response.rawData else {
guard let data = response?.rawData else {
chain.handleErrorAsync(ParserError.nilData,
request: request,
response: response,
Expand Down
21 changes: 12 additions & 9 deletions Sources/Apollo/LegacyParsingInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ public class LegacyParsingInterceptor: ApolloInterceptor {
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {

guard let data = response.rawData else {
chain.handleErrorAsync(ParserError.nilData,
request: request,
response: response,
completion: completion)
guard
let createdResponse = response,
let data = createdResponse.rawData else {
chain.handleErrorAsync(ParserError.nilData,
request: request,
response: response,
completion: completion)
return
}

Expand All @@ -25,11 +27,12 @@ public class LegacyParsingInterceptor: ApolloInterceptor {

let graphQLResponse = GraphQLResponse(operation: request.operation, body: body)
let parsedResult = try graphQLResponse.parseResultFast()
let typedResult = parsedResult
response.parsedResponse = typedResult
let typedResult = parsedResult

createdResponse.parsedResponse = typedResult

chain.proceedAsync(request: request,
response: response,
response: createdResponse,
completion: completion)

} catch {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Apollo/MaxRetryInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class MaxRetryInterceptor: ApolloInterceptor {
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
guard request.retryCount <= self.maxRetries else {
let error = RetryError.hitMaxRetryCount(count: self.maxRetries,
Expand Down
7 changes: 4 additions & 3 deletions Sources/Apollo/NetworkFetchInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class NetworkFetchInterceptor: ApolloInterceptor, Cancellable {
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {

let urlRequest: URLRequest
Expand Down Expand Up @@ -45,8 +45,9 @@ public class NetworkFetchInterceptor: ApolloInterceptor, Cancellable {
response: response,
completion: completion)
case .success(let (data, httpResponse)):
response.httpResponse = httpResponse
response.rawData = data
let response = HTTPResponse<Operation>(response: httpResponse,
rawData: data,
parsedResponse: nil)
chain.proceedAsync(request: request,
response: response,
completion: completion)
Expand Down
18 changes: 8 additions & 10 deletions Sources/Apollo/RequestChain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,18 @@ public class RequestChain: Cancellable {
request: HTTPRequest<Operation>,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
assert(self.currentIndex == 0, "The interceptor index should be zero when calling this method")

let response: HTTPResponse<Operation> = HTTPResponse(response: nil,
rawData: nil,
parsedResponse: nil)

guard let firstInterceptor = self.interceptors.first else {
handleErrorAsync(ChainError.noInterceptors,
request: request,
response: response,
response: nil,
completion: completion)
return
}

firstInterceptor.interceptAsync(chain: self,
request: request,
response: response,
response: nil,
completion: completion)
}

Expand All @@ -68,9 +65,10 @@ public class RequestChain: Cancellable {
/// - request: The in-progress request object
/// - response: The in-progress response object
/// - completion: The completion closure to call when data has been processed and should be returned to the UI.
public func proceedAsync<Operation: GraphQLOperation>(request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
public func proceedAsync<Operation: GraphQLOperation>(
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
guard self.isNotCancelled else {
// Do not proceed, this chain has been cancelled.
return
Expand Down Expand Up @@ -135,7 +133,7 @@ public class RequestChain: Cancellable {
public func handleErrorAsync<Operation: GraphQLOperation>(
_ error: Error,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
guard self.isNotCancelled else {
return
Expand Down
9 changes: 5 additions & 4 deletions Sources/Apollo/ResponseCodeInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ public class ResponseCodeInterceptor: ApolloInterceptor {
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {

guard response.httpResponse?.apollo.isSuccessful == true else {
let error = ResponseCodeError.invalidResponseCode(response: response.httpResponse,

guard response?.httpResponse.apollo.isSuccessful == true else {
let error = ResponseCodeError.invalidResponseCode(response: response?.httpResponse,

rawData: response.rawData)
rawData: response?.rawData)

chain.handleErrorAsync(error,
request: request,
Expand Down

0 comments on commit 0bde055

Please sign in to comment.