Skip to content

Commit b47c2aa

Browse files
chore: Deprecate field execution strategy customization
The only real logical choices are serial or concurrent (which are deterministic via the spec), and the graphql-js reference implementation doesn't allow customization.
1 parent d3366a2 commit b47c2aa

1 file changed

Lines changed: 105 additions & 56 deletions

File tree

Sources/GraphQL/Execution/Execute.swift

Lines changed: 105 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ import OrderedCollections
2626
/// Namely, schema of the type system that is currently executing,
2727
/// and the fragments defined in the query document
2828
public final class ExecutionContext: @unchecked Sendable {
29-
let queryStrategy: QueryFieldExecutionStrategy = ConcurrentFieldExecutionStrategy()
30-
let mutationStrategy: MutationFieldExecutionStrategy = SerialFieldExecutionStrategy()
31-
let subscriptionStrategy: SubscriptionFieldExecutionStrategy =
32-
ConcurrentFieldExecutionStrategy()
3329
public let schema: GraphQLSchema
3430
public let fragments: [String: FragmentDefinition]
3531
public let rootValue: any Sendable
@@ -84,6 +80,7 @@ public final class ExecutionContext: @unchecked Sendable {
8480
}
8581
}
8682

83+
@available(*, deprecated, message: "FieldExecutionStrategy type no longer supported")
8784
public protocol FieldExecutionStrategy: Sendable {
8885
func executeFields(
8986
exeContext: ExecutionContext,
@@ -94,78 +91,63 @@ public protocol FieldExecutionStrategy: Sendable {
9491
) async throws -> OrderedDictionary<String, any Sendable>
9592
}
9693

94+
@available(*, deprecated, message: "FieldExecutionStrategy type no longer supported")
9795
public protocol MutationFieldExecutionStrategy: FieldExecutionStrategy {}
96+
97+
@available(*, deprecated, message: "FieldExecutionStrategy type no longer supported")
9898
public protocol QueryFieldExecutionStrategy: FieldExecutionStrategy {}
99+
100+
@available(*, deprecated, message: "FieldExecutionStrategy type no longer supported")
99101
public protocol SubscriptionFieldExecutionStrategy: FieldExecutionStrategy {}
100102

101103
/// Serial field execution strategy that's suitable for the "Evaluating selection sets" section of the spec for "write" mode.
104+
@available(*, deprecated, message: "FieldExecutionStrategy type no longer supported")
102105
public struct SerialFieldExecutionStrategy: QueryFieldExecutionStrategy,
103106
MutationFieldExecutionStrategy, SubscriptionFieldExecutionStrategy
104107
{
108+
@available(*, deprecated, message: "FieldExecutionStrategy type no longer supported")
105109
public init() {}
106110

111+
@available(*, deprecated, message: "FieldExecutionStrategy type no longer supported")
107112
public func executeFields(
108113
exeContext: ExecutionContext,
109114
parentType: GraphQLObjectType,
110115
sourceValue: any Sendable,
111116
path: IndexPath,
112117
fields: OrderedDictionary<String, [Field]>
113118
) async throws -> OrderedDictionary<String, any Sendable> {
114-
var results = OrderedDictionary<String, any Sendable>()
115-
for field in fields {
116-
let fieldASTs = field.value
117-
let fieldPath = path.appending(field.key)
118-
results[field.key] =
119-
try await resolveField(
120-
exeContext: exeContext,
121-
parentType: parentType,
122-
source: sourceValue,
123-
fieldASTs: fieldASTs,
124-
path: fieldPath
125-
) ?? Map.null
126-
}
127-
return results
119+
return try await GraphQL.executeFieldsSerially(
120+
exeContext: exeContext,
121+
parentType: parentType,
122+
sourceValue: sourceValue,
123+
path: path,
124+
fields: fields
125+
)
128126
}
129127
}
130128

131129
/// Serial field execution strategy that's suitable for the "Evaluating selection sets" section of the spec for "read" mode.
132130
///
133131
/// Each field is resolved as an individual task on a concurrent dispatch queue.
132+
@available(*, deprecated, message: "FieldExecutionStrategy type no longer supported")
134133
public struct ConcurrentFieldExecutionStrategy: QueryFieldExecutionStrategy,
135134
SubscriptionFieldExecutionStrategy
136135
{
136+
@available(*, deprecated, message: "FieldExecutionStrategy type no longer supported")
137137
public func executeFields(
138138
exeContext: ExecutionContext,
139139
parentType: GraphQLObjectType,
140140
sourceValue: any Sendable,
141141
path: IndexPath,
142142
fields: OrderedDictionary<String, [Field]>
143143
) async throws -> OrderedDictionary<String, any Sendable> {
144-
return try await withThrowingTaskGroup(of: (String, (any Sendable)?).self) { group in
145-
// preserve field order by assigning to null and filtering later
146-
var results: OrderedDictionary<String, (any Sendable)?> =
147-
fields
148-
.mapValues { _ -> Any? in nil }
149-
for field in fields {
150-
group.addTask {
151-
let fieldASTs = field.value
152-
let fieldPath = path.appending(field.key)
153-
let result =
154-
try await resolveField(
155-
exeContext: exeContext,
156-
parentType: parentType,
157-
source: sourceValue,
158-
fieldASTs: fieldASTs,
159-
path: fieldPath
160-
) ?? Map.null
161-
return (field.key, result)
162-
}
163-
}
164-
for try await result in group {
165-
results[result.0] = result.1
166-
}
167-
return results.compactMapValues { $0 }
168-
}
144+
return try await GraphQL.executeFields(
145+
exeContext: exeContext,
146+
parentType: parentType,
147+
sourceValue: sourceValue,
148+
path: path,
149+
fields: fields
150+
)
169151
}
170152
}
171153

@@ -311,24 +293,91 @@ func executeOperation(
311293
visitedFragmentNames: &visitedFragmentNames
312294
)
313295

314-
let fieldExecutionStrategy: FieldExecutionStrategy
315-
316296
switch operation.operation {
317297
case .query:
318-
fieldExecutionStrategy = exeContext.queryStrategy
298+
return try await executeFields(
299+
exeContext: exeContext,
300+
parentType: type,
301+
sourceValue: rootValue,
302+
path: [],
303+
fields: fields
304+
)
319305
case .mutation:
320-
fieldExecutionStrategy = exeContext.mutationStrategy
306+
return try await executeFieldsSerially(
307+
exeContext: exeContext,
308+
parentType: type,
309+
sourceValue: rootValue,
310+
path: [],
311+
fields: fields
312+
)
321313
case .subscription:
322-
fieldExecutionStrategy = exeContext.subscriptionStrategy
314+
return try await executeFields(
315+
exeContext: exeContext,
316+
parentType: type,
317+
sourceValue: rootValue,
318+
path: [],
319+
fields: fields
320+
)
321+
}
322+
}
323+
324+
/// Implements the "Executing selection sets" section of the spec for fields that must be executed serially.
325+
func executeFieldsSerially(
326+
exeContext: ExecutionContext,
327+
parentType: GraphQLObjectType,
328+
sourceValue: any Sendable,
329+
path: IndexPath,
330+
fields: OrderedDictionary<String, [Field]>
331+
) async throws -> OrderedDictionary<String, any Sendable> {
332+
var results = OrderedDictionary<String, any Sendable>()
333+
for field in fields {
334+
let fieldASTs = field.value
335+
let fieldPath = path.appending(field.key)
336+
results[field.key] =
337+
try await resolveField(
338+
exeContext: exeContext,
339+
parentType: parentType,
340+
source: sourceValue,
341+
fieldASTs: fieldASTs,
342+
path: fieldPath
343+
) ?? Map.null
323344
}
345+
return results
346+
}
324347

325-
return try await fieldExecutionStrategy.executeFields(
326-
exeContext: exeContext,
327-
parentType: type,
328-
sourceValue: rootValue,
329-
path: [],
330-
fields: fields
331-
)
348+
/// Implements the "Executing selection sets" section of the spec for fields that may be executed in parallel.
349+
func executeFields(
350+
exeContext: ExecutionContext,
351+
parentType: GraphQLObjectType,
352+
sourceValue: any Sendable,
353+
path: IndexPath,
354+
fields: OrderedDictionary<String, [Field]>
355+
) async throws -> OrderedDictionary<String, any Sendable> {
356+
return try await withThrowingTaskGroup(of: (String, (any Sendable)?).self) { group in
357+
// preserve field order by assigning to null and filtering later
358+
var results: OrderedDictionary<String, (any Sendable)?> =
359+
fields
360+
.mapValues { _ -> Any? in nil }
361+
for field in fields {
362+
group.addTask {
363+
let fieldASTs = field.value
364+
let fieldPath = path.appending(field.key)
365+
let result =
366+
try await resolveField(
367+
exeContext: exeContext,
368+
parentType: parentType,
369+
source: sourceValue,
370+
fieldASTs: fieldASTs,
371+
path: fieldPath
372+
) ?? Map.null
373+
return (field.key, result)
374+
}
375+
}
376+
for try await result in group {
377+
results[result.0] = result.1
378+
}
379+
return results.compactMapValues { $0 }
380+
}
332381
}
333382

334383
/// Extracts the root type of the operation from the schema.
@@ -969,7 +1018,7 @@ func completeObjectValue(
9691018
}
9701019
}
9711020

972-
return try await exeContext.queryStrategy.executeFields(
1021+
return try await executeFields(
9731022
exeContext: exeContext,
9741023
parentType: returnType,
9751024
sourceValue: result,

0 commit comments

Comments
 (0)