-
Notifications
You must be signed in to change notification settings - Fork 749
Create FetchOptions to allow GET Requests like in Web #572
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
8cb9fb4
53f2e51
bea8242
ad29887
fcb9533
ef686cb
5ff93a7
dee434b
2a77206
9766548
4f92bee
4a97304
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import Foundation | ||
|
|
||
| public enum FetchHTTPMethod: String { | ||
| case GET | ||
| case POST | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,32 @@ public struct GraphQLHTTPResponseError: Error, LocalizedError { | |
| } | ||
| } | ||
|
|
||
| public struct GraphQLHTTPRequestError: Error, LocalizedError { | ||
| public enum ErrorKind { | ||
|
designatednerd marked this conversation as resolved.
|
||
| case serializedBodyMessageError | ||
| case serializedQueryParamsMessageError | ||
|
|
||
| var description: String { | ||
| switch self { | ||
| case .serializedBodyMessageError: | ||
| return "JSONSerialization error: Error while serializing request's body" | ||
| case .serializedQueryParamsMessageError: | ||
| return "QueryParams error: Error while serializing variables as query parameters." | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public init(kind: ErrorKind) { | ||
| self.kind = kind | ||
| } | ||
|
|
||
| public let kind: ErrorKind | ||
|
|
||
| public var errorDescription: String? { | ||
| return "\(kind.description)" | ||
| } | ||
| } | ||
|
|
||
| /// A network transport that uses HTTP POST requests to send GraphQL operations to a server, and that uses `URLSession` as the networking implementation. | ||
| public class HTTPNetworkTransport: NetworkTransport { | ||
| let url: URL | ||
|
|
@@ -69,18 +95,32 @@ public class HTTPNetworkTransport: NetworkTransport { | |
| /// | ||
| /// - Parameters: | ||
| /// - operation: The operation to send. | ||
| /// - fetchHTTPMethod: The HTTP Method to be used. | ||
| /// - completionHandler: A closure to call when a request completes. | ||
| /// - response: The response received from the server, or `nil` if an error occurred. | ||
| /// - error: An error that indicates why a request failed, or `nil` if the request was succesful. | ||
| /// - Returns: An object that can be used to cancel an in progress request. | ||
| public func send<Operation>(operation: Operation, completionHandler: @escaping (_ response: GraphQLResponse<Operation>?, _ error: Error?) -> Void) -> Cancellable { | ||
| public func send<Operation>(operation: Operation, fetchHTTPMethod: FetchHTTPMethod, completionHandler: @escaping (_ response: GraphQLResponse<Operation>?, _ error: Error?) -> Void) -> Cancellable { | ||
| let body = requestBody(for: operation) | ||
| var request = URLRequest(url: url) | ||
| request.httpMethod = "POST" | ||
|
|
||
| switch fetchHTTPMethod { | ||
| case .GET: | ||
| if let urlForGet = mountUrlWithQueryParamsIfNeeded(body: body) { | ||
| request = URLRequest(url: urlForGet) | ||
| } else { | ||
| completionHandler(nil, GraphQLHTTPRequestError(kind: .serializedQueryParamsMessageError)) | ||
| } | ||
| default: | ||
| do { | ||
| request.httpBody = try serializationFormat.serialize(value: body) | ||
| } catch { | ||
| completionHandler(nil, GraphQLHTTPRequestError(kind: .serializedBodyMessageError)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should probably return here and above to prevent the rest of this from executing
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi! This method As the task must resume ( What do you think? Do you have any suggestion?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll poke around - I think there's probably something else that conforms to
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay! Thank you, once again!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now the only non private object/struct is an extension of But I was thinking about it... A request can be executed without a request body or a query parameter. It's like a request to an endpoint without parameters. So, if this try fails, it won't have a request body, and so, when the task resume, it will just return an error and the reason for it. But also, the only way for this to fail, right now, would be in the case that Maybe, that's why it was using
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But once again, tell me what is your opinion, please! I want to help with it, but if you want to keep this change and find another way to solve it, please, be my guest to fix it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think for this PR we're gonna leave it - I'll make a fix that addresses this in other areas as well in a separate PR.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, thank you for the review. :) !! |
||
| } | ||
| } | ||
|
|
||
| request.httpMethod = fetchHTTPMethod.rawValue | ||
| request.setValue("application/json", forHTTPHeaderField: "Content-Type") | ||
|
|
||
| let body = requestBody(for: operation) | ||
| request.httpBody = try! serializationFormat.serialize(value: body) | ||
|
|
||
| let task = session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in | ||
| if error != nil { | ||
|
|
@@ -129,4 +169,46 @@ public class HTTPNetworkTransport: NetworkTransport { | |
| } | ||
| return ["query": operation.queryDocument, "variables": operation.variables] | ||
| } | ||
|
|
||
| private func mountUrlWithQueryParamsIfNeeded(body: GraphQLMap) -> URL? { | ||
| guard let query = body.jsonObject["query"], var queryParam = queryString(withItems: [URLQueryItem(name: "query", value: "\(query)")]) else { | ||
| return self.url | ||
| } | ||
| if areThereVariables(in: body) { | ||
| guard let serializedVariables = try? serializationFormat.serialize(value: body.jsonObject["variables"]) else { | ||
| return URL(string: "\(self.url.absoluteString)?\(queryParam)") | ||
| } | ||
| queryParam += getVariablesEncodedString(of: serializedVariables) | ||
| } | ||
| guard let urlForGet = URL(string: "\(self.url.absoluteString)?\(queryParam)") else { | ||
| return URL(string: "\(self.url.absoluteString)?\(queryParam)") | ||
| } | ||
| return urlForGet | ||
| } | ||
|
|
||
| private func areThereVariables(in map: GraphQLMap) -> Bool { | ||
| if let variables = map.jsonObject["variables"], "\(variables)" != "<null>" { | ||
| return true | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| private func getVariablesEncodedString(of data: Data) -> String { | ||
| var dataString = String(data: data, encoding: String.Encoding.utf8) ?? "" | ||
| dataString = dataString.replacingOccurrences(of: ";", with: ",") | ||
| dataString = dataString.replacingOccurrences(of: "=", with: ":") | ||
| guard let variablesEncoded = queryString(withItems: [URLQueryItem(name: "variables", value: "\(dataString)")]) else { return "" } | ||
| return "&\(variablesEncoded)" | ||
| } | ||
|
|
||
| private func queryString(withItems items: [URLQueryItem], percentEncoded: Bool = true) -> String? { | ||
| let url = NSURLComponents() | ||
| url.queryItems = items | ||
| let queryString = percentEncoded ? url.percentEncodedQuery : url.query | ||
|
|
||
| if let queryString = queryString { | ||
| return "\(queryString)" | ||
| } | ||
| return nil | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.