forked from apollographql/apollo-ios
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRequestCreator.swift
More file actions
127 lines (112 loc) · 5.41 KB
/
RequestCreator.swift
File metadata and controls
127 lines (112 loc) · 5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import Foundation
public protocol RequestCreator {
/// Creates a `GraphQLMap` out of the passed-in operation
///
/// - Parameters:
/// - operation: The operation to use
/// - sendOperationIdentifiers: Whether or not to send operation identifiers. Defaults to false.
/// - Returns: The created `GraphQLMap`
func requestBody<Operation: GraphQLOperation>(for operation: Operation, sendOperationIdentifiers: Bool) -> GraphQLMap
/// Creates multi-part form data to send with a request
///
/// - Parameters:
/// - operation: The operation to create the data for.
/// - files: An array of files to use.
/// - sendOperationIdentifiers: True if operation identifiers should be sent, false if not.
/// - serializationFormat: The format to use to serialize data.
/// - manualBoundary: [optional] A manual boundary to pass in. A default boundary will be used otherwise.
/// - Returns: The created form data
/// - Throws: Errors creating or loading the form data
func requestMultipartFormData<Operation: GraphQLOperation>(for operation: Operation,
files: [GraphQLFile],
sendOperationIdentifiers: Bool,
serializationFormat: JSONSerializationFormat.Type,
manualBoundary: String?) throws -> MultipartFormData
}
extension RequestCreator {
/// Creates a `GraphQLMap` out of the passed-in operation
///
/// - Parameters:
/// - operation: The operation to use
/// - sendOperationIdentifiers: Whether or not to send operation identifiers. Defaults to false.
/// - Returns: The created `GraphQLMap`
public func requestBody<Operation: GraphQLOperation>(for operation: Operation, sendOperationIdentifiers: Bool) -> GraphQLMap {
var body: GraphQLMap = [
"variables": operation.variables,
"operationName": operation.operationName,
]
if sendOperationIdentifiers {
guard let operationIdentifier = operation.operationIdentifier else {
preconditionFailure("To send operation identifiers, Apollo types must be generated with operationIdentifiers")
}
body["id"] = operationIdentifier
} else {
body["query"] = operation.queryDocument
}
return body
}
/// Creates multi-part form data to send with a request
///
/// - Parameters:
/// - operation: The operation to create the data for.
/// - files: An array of files to use.
/// - sendOperationIdentifiers: True if operation identifiers should be sent, false if not.
/// - serializationFormat: The format to use to serialize data.
/// - manualBoundary: [optional] A manual boundary to pass in. A default boundary will be used otherwise.
/// - Returns: The created form data
/// - Throws: Errors creating or loading the form data
public func requestMultipartFormData<Operation: GraphQLOperation>(for operation: Operation,
files: [GraphQLFile],
sendOperationIdentifiers: Bool,
serializationFormat: JSONSerializationFormat.Type,
manualBoundary: String?) throws -> MultipartFormData {
let formData: MultipartFormData
if let boundary = manualBoundary {
formData = MultipartFormData(boundary: boundary)
} else {
formData = MultipartFormData()
}
// Make sure all fields for files are set to null, or the server won't look
// for the files in the rest of the form data
let fieldsForFiles = Set(files.map { $0.fieldName })
var fields = requestBody(for: operation, sendOperationIdentifiers: sendOperationIdentifiers)
var variables = fields["variables"] as? GraphQLMap ?? GraphQLMap()
for fieldName in fieldsForFiles {
if
let value = variables[fieldName],
let arrayValue = value as? [JSONEncodable] {
let updatedArray: [JSONEncodable?] = arrayValue.map { _ in nil }
variables.updateValue(updatedArray, forKey: fieldName)
} else {
variables.updateValue(nil, forKey: fieldName)
}
}
fields["variables"] = variables
let operationData = try serializationFormat.serialize(value: fields)
formData.appendPart(data: operationData, name: "operations")
var map = [String: [String]]()
if files.count == 1 {
let firstFile = files.first!
map["0"] = ["variables.\(firstFile.fieldName)"]
} else {
for (index, file) in files.enumerated() {
map["\(index)"] = ["variables.\(file.fieldName).\(index)"]
}
}
let mapData = try serializationFormat.serialize(value: map)
formData.appendPart(data: mapData, name: "map")
for (index, file) in files.enumerated() {
formData.appendPart(inputStream: file.inputStream,
contentLength: file.contentLength,
name: "\(index)",
contentType: file.mimeType,
filename: file.originalName)
}
return formData
}
}
// Helper struct to create requests independently of HTTP operations.
public struct ApolloRequestCreator: RequestCreator {
// Internal init methods cannot be used in public methods
public init() { }
}