Skip to content

Commit b3ca4b5

Browse files
committed
Add support for HTTPNetworkTransportDelegate to inspect a response before it's processed by Apollo
1 parent 7350082 commit b3ca4b5

2 files changed

Lines changed: 69 additions & 26 deletions

File tree

Sources/Apollo/HTTPNetworkTransport.swift

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public protocol HTTPNetworkTransportDelegate: class {
4646
/// Opportunity for the delegate to modify the URLRequest before it is sent. `completionHandler` must be called with the
4747
/// desired URLRequest to send.
4848
func networkTransport(_ networkTransport: HTTPNetworkTransport, prepareRequest request: URLRequest, completionHandler: @escaping (URLRequest) -> Void)
49+
/// Opportunity for the delegate to inspect and modify a resposne before it is processed by Apollo.
50+
func networkTransport(_ networkTransport: HTTPNetworkTransport, request: URLRequest, preprocessData data: Data?, response: URLResponse?, error: Error?, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void)
4951
}
5052

5153
/// A network transport that uses HTTP POST requests to send GraphQL operations to a server, and that uses `URLSession` as the networking implementation.
@@ -87,33 +89,12 @@ public class HTTPNetworkTransport: NetworkTransport {
8789
request.httpBody = try! serializationFormat.serialize(value: body)
8890

8991
let requestOperation = URLRequestOperation(session: session, request: request) { (data: Data?, response: URLResponse?, error: Error?) in
90-
if error != nil {
91-
completionHandler(nil, error)
92-
return
93-
}
94-
95-
guard let httpResponse = response as? HTTPURLResponse else {
96-
fatalError("Response should be an HTTPURLResponse")
97-
}
98-
99-
if (!httpResponse.isSuccessful) {
100-
completionHandler(nil, GraphQLHTTPResponseError(body: data, response: httpResponse, kind: .errorResponse))
101-
return
102-
}
103-
104-
guard let data = data else {
105-
completionHandler(nil, GraphQLHTTPResponseError(body: nil, response: httpResponse, kind: .invalidResponse))
106-
return
107-
}
108-
109-
do {
110-
guard let body = try self.serializationFormat.deserialize(data: data) as? JSONObject else {
111-
throw GraphQLHTTPResponseError(body: data, response: httpResponse, kind: .invalidResponse)
92+
if let delegate = self.delegate {
93+
delegate.networkTransport(self, request: request, preprocessData: data, response: response, error: error) {
94+
self.handleResponse(for: operation, data: $0, response: $1, error: $2, completionHandler: completionHandler)
11295
}
113-
let response = GraphQLResponse(operation: operation, body: body)
114-
completionHandler(response, nil)
115-
} catch {
116-
completionHandler(nil, error)
96+
} else {
97+
self.handleResponse(for: operation, data: data, response: response, error: error, completionHandler: completionHandler)
11798
}
11899
}
119100

@@ -128,6 +109,37 @@ public class HTTPNetworkTransport: NetworkTransport {
128109

129110
return requestOperation
130111
}
112+
113+
private func handleResponse<Operation>(for operation: Operation, data: Data?, response: URLResponse?, error: Error?, completionHandler: @escaping (_ response: GraphQLResponse<Operation>?, _ error: Error?) -> Void) {
114+
if error != nil {
115+
completionHandler(nil, error)
116+
return
117+
}
118+
119+
guard let httpResponse = response as? HTTPURLResponse else {
120+
fatalError("Response should be an HTTPURLResponse")
121+
}
122+
123+
if (!httpResponse.isSuccessful) {
124+
completionHandler(nil, GraphQLHTTPResponseError(body: data, response: httpResponse, kind: .errorResponse))
125+
return
126+
}
127+
128+
guard let data = data else {
129+
completionHandler(nil, GraphQLHTTPResponseError(body: nil, response: httpResponse, kind: .invalidResponse))
130+
return
131+
}
132+
133+
do {
134+
guard let body = try self.serializationFormat.deserialize(data: data) as? JSONObject else {
135+
throw GraphQLHTTPResponseError(body: data, response: httpResponse, kind: .invalidResponse)
136+
}
137+
let response = GraphQLResponse(operation: operation, body: body)
138+
completionHandler(response, nil)
139+
} catch {
140+
completionHandler(nil, error)
141+
}
142+
}
131143

132144
private let sendOperationIdentifiers: Bool
133145

Tests/ApolloTests/HTTPNetworkTransportDelegateTests.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,26 @@ class HTTPNetworkTransportDelegateTests: XCTestCase {
2323
}
2424
self.waitForExpectations(timeout: 1)
2525
}
26+
27+
func testProcessResponse() {
28+
let overrideError = NSError(domain: "com.apollographql.Apollo.tests", code: 0, userInfo: nil)
29+
30+
let delegate = TransportDelegate()
31+
delegate.responseProcessor = { data, response, error in
32+
return (nil, nil, overrideError)
33+
}
34+
35+
let url = URL(string: "http://localhost/endpoint")!
36+
MockURLProtocol.nextResponse = MockURLProtocol.Response.make(url: url, response: "{}", statusCode: 200)
37+
let transport = HTTPNetworkTransport(url: url, configuration: .mock(), sendOperationIdentifiers: false, delegate: delegate)
38+
39+
let expectation = self.expectation(description: "Trigger request with custom preparation hook")
40+
_ = transport.send(operation: MockGraphQLQuery()) { (_, error) in
41+
XCTAssertEqual(error! as NSError, overrideError)
42+
expectation.fulfill()
43+
}
44+
self.waitForExpectations(timeout: 1)
45+
}
2646
}
2747

2848
private class TransportDelegate: HTTPNetworkTransportDelegate {
@@ -38,6 +58,17 @@ private class TransportDelegate: HTTPNetworkTransportDelegate {
3858
}
3959
}
4060
}
61+
62+
var responseProcessor: ((Data?, URLResponse?, Error?) -> (Data?, URLResponse?, Error?))?
63+
64+
func networkTransport(_ networkTransport: HTTPNetworkTransport, request: URLRequest, preprocessData data: Data?, response: URLResponse?, error: Error?, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) {
65+
if let responseProcessor = self.responseProcessor {
66+
let (modifiedData, modifiedResponse, modifiedError) = responseProcessor(data, response, error)
67+
completionHandler(modifiedData, modifiedResponse, modifiedError)
68+
} else {
69+
completionHandler(data, response, error)
70+
}
71+
}
4172
}
4273

4374
private extension URLSessionConfiguration {

0 commit comments

Comments
 (0)