1+ import Dispatch
2+
13/**
24 * Terminology
35 *
2628 */
2729public final class ExecutionContext {
2830
29- let queryStrategy : FieldExecutionStrategy
30- let mutationStrategy : FieldExecutionStrategy
31- let subscriptionStrategy : FieldExecutionStrategy
32- let schema : GraphQLSchema
33- let fragments : [ String : FragmentDefinition ]
34- let rootValue : Any
35- let contextValue : Any
36- let operation : OperationDefinition
37- let variableValues : [ String : Map ]
38- var errors : [ GraphQLError ]
31+ let queryStrategy : QueryFieldExecutionStrategy
32+ let mutationStrategy : MutationFieldExecutionStrategy
33+ let subscriptionStrategy : SubscriptionFieldExecutionStrategy
34+ public let schema : GraphQLSchema
35+ public let fragments : [ String : FragmentDefinition ]
36+ public let rootValue : Any
37+ public let contextValue : Any
38+ public let operation : OperationDefinition
39+ public let variableValues : [ String : Map ]
40+
41+ private var errorsSemaphore = DispatchSemaphore ( value: 1 )
42+ private var _errors : [ GraphQLError ]
43+
44+ var errors : [ GraphQLError ] {
45+ get {
46+ errorsSemaphore. wait ( )
47+ defer {
48+ errorsSemaphore. signal ( )
49+ }
50+ return _errors
51+ }
52+ }
3953
4054 init (
41- queryStrategy: FieldExecutionStrategy ,
42- mutationStrategy: FieldExecutionStrategy ,
43- subscriptionStrategy: FieldExecutionStrategy ,
55+ queryStrategy: QueryFieldExecutionStrategy ,
56+ mutationStrategy: MutationFieldExecutionStrategy ,
57+ subscriptionStrategy: SubscriptionFieldExecutionStrategy ,
4458 schema: GraphQLSchema ,
4559 fragments: [ String : FragmentDefinition ] ,
4660 rootValue: Any ,
@@ -58,9 +72,17 @@ public final class ExecutionContext {
5872 self . contextValue = contextValue
5973 self . operation = operation
6074 self . variableValues = variableValues
61- self . errors = errors
75+ self . _errors = errors
76+ }
6277
78+ public func append( error: GraphQLError ) {
79+ errorsSemaphore. wait ( )
80+ defer {
81+ errorsSemaphore. signal ( )
82+ }
83+ _errors. append ( error)
6384 }
85+
6486}
6587
6688public protocol FieldExecutionStrategy {
@@ -73,10 +95,17 @@ public protocol FieldExecutionStrategy {
7395 ) throws -> [ String : Any ]
7496}
7597
98+ public protocol MutationFieldExecutionStrategy : FieldExecutionStrategy { }
99+ public protocol QueryFieldExecutionStrategy : FieldExecutionStrategy { }
100+ public protocol SubscriptionFieldExecutionStrategy : FieldExecutionStrategy { }
101+
76102/**
77103 * Serial field execution strategy that's suitable for the "Evaluating selection sets" section of the spec for "write" mode.
78104 */
79- public struct SerialFieldExecutionStrategy : FieldExecutionStrategy {
105+ public struct SerialFieldExecutionStrategy : QueryFieldExecutionStrategy , MutationFieldExecutionStrategy , SubscriptionFieldExecutionStrategy {
106+
107+ public init ( ) { }
108+
80109 public func executeFields(
81110 exeContext: ExecutionContext ,
82111 parentType: GraphQLObjectType ,
@@ -103,16 +132,88 @@ public struct SerialFieldExecutionStrategy: FieldExecutionStrategy {
103132 }
104133}
105134
135+ /**
136+ * Serial field execution strategy that's suitable for the "Evaluating selection sets" section of the spec for "read" mode.
137+ *
138+ * Each field is resolved as an individual task on a concurrent dispatch queue.
139+ */
140+ public struct ConcurrentDispatchFieldExecutionStrategy : QueryFieldExecutionStrategy , SubscriptionFieldExecutionStrategy {
141+
142+ let dispatchQueue : DispatchQueue
143+
144+ public init ( dispatchQueue: DispatchQueue ) {
145+ self . dispatchQueue = dispatchQueue
146+ }
147+
148+ public init ( queueLabel: String = " GraphQL field execution " , queueQoS: DispatchQoS = . userInitiated) {
149+ self . dispatchQueue = DispatchQueue (
150+ label: queueLabel,
151+ qos: queueQoS,
152+ attributes: . concurrent
153+ )
154+ }
155+
156+ public func executeFields(
157+ exeContext: ExecutionContext ,
158+ parentType: GraphQLObjectType ,
159+ sourceValue: Any ,
160+ path: [ IndexPathElement ] ,
161+ fields: [ String : [ Field ] ]
162+ ) throws -> [ String : Any ] {
163+
164+ let resultsQueue = DispatchQueue (
165+ label: " \( dispatchQueue. label) results " ,
166+ qos: dispatchQueue. qos
167+ )
168+ let group = DispatchGroup ( )
169+ var results : [ String : Any ] = [ : ]
170+ var err : Error ? = nil
171+
172+ fields. forEach { field in
173+ let fieldASTs = field. value
174+ let fieldKey = field. key
175+ let fieldPath = path + [ fieldKey] as [ IndexPathElement ]
176+ dispatchQueue. async ( group: group) {
177+ guard err == nil else {
178+ return
179+ }
180+ do {
181+ let result = try resolveField (
182+ exeContext: exeContext,
183+ parentType: parentType,
184+ source: sourceValue,
185+ fieldASTs: fieldASTs,
186+ path: fieldPath
187+ )
188+ resultsQueue. async ( group: group) {
189+ results [ fieldKey] = result ?? Map . null
190+ }
191+ } catch {
192+ resultsQueue. async ( group: group) {
193+ err = error
194+ }
195+ }
196+ }
197+ }
198+ group. wait ( )
199+ if let error = err {
200+ throw error
201+ }
202+ return results
203+ }
204+
205+ }
206+
106207/**
107208 * Implements the "Evaluating requests" section of the GraphQL specification.
108209 *
109210 * If the arguments to this func do not result in a legal execution context,
110211 * a GraphQLError will be thrown immediately explaining the invalid input.
111212 */
112213func execute(
113- queryStrategy: FieldExecutionStrategy ,
114- mutationStrategy: FieldExecutionStrategy ,
115- subscriptionStrategy: FieldExecutionStrategy ,
214+ queryStrategy: QueryFieldExecutionStrategy ,
215+ mutationStrategy: MutationFieldExecutionStrategy ,
216+ subscriptionStrategy: SubscriptionFieldExecutionStrategy ,
116217 schema: GraphQLSchema ,
117218 documentAST: Document ,
118219 rootValue: Any ,
@@ -166,9 +267,9 @@ func execute(
166267 * Throws a GraphQLError if a valid execution context cannot be created.
167268 */
168269func buildExecutionContext(
169- queryStrategy: FieldExecutionStrategy ,
170- mutationStrategy: FieldExecutionStrategy ,
171- subscriptionStrategy: FieldExecutionStrategy ,
270+ queryStrategy: QueryFieldExecutionStrategy ,
271+ mutationStrategy: MutationFieldExecutionStrategy ,
272+ subscriptionStrategy: SubscriptionFieldExecutionStrategy ,
172273 schema: GraphQLSchema ,
173274 documentAST: Document ,
174275 rootValue: Any ,
@@ -608,7 +709,7 @@ func completeValueCatchingError(
608709 } catch let error as GraphQLError {
609710 // If `completeValueWithLocatedError` returned abruptly (threw an error),
610711 // log the error and return .null.
611- exeContext. errors . append ( error)
712+ exeContext. append ( error : error)
612713 return nil
613714 } catch {
614715 fatalError ( )
0 commit comments