@@ -3,22 +3,16 @@ import OrderedCollections
3
3
/**
4
4
* Implements the "Subscribe" algorithm described in the GraphQL specification.
5
5
*
6
- * Returns a future which resolves to a SubscriptionResult containing either
7
- * a SubscriptionObservable (if successful), or GraphQLErrors (error).
6
+ * Returns a `Result` that either succeeds with an `AsyncThrowingStream`, or fails with `GraphQLErrors`.
8
7
*
9
8
* If the client-provided arguments to this function do not result in a
10
- * compliant subscription, the future will resolve to a
11
- * SubscriptionResult containing `errors` and no `observable`.
9
+ * compliant subscription, the `Result` will fails with descriptive errors.
12
10
*
13
11
* If the source stream could not be created due to faulty subscription
14
- * resolver logic or underlying systems, the future will resolve to a
15
- * SubscriptionResult containing `errors` and no `observable`.
12
+ * resolver logic or underlying systems, the `Result` will fail with errors.
16
13
*
17
- * If the operation succeeded, the future will resolve to a SubscriptionResult,
18
- * containing an `observable` which yields a stream of GraphQLResults
14
+ * If the operation succeeded, the `Result` will succeed with an `AsyncThrowingStream` of `GraphQLResult`s
19
15
* representing the response stream.
20
- *
21
- * Accepts either an object with named arguments, or individual arguments.
22
16
*/
23
17
func subscribe(
24
18
queryStrategy: QueryFieldExecutionStrategy ,
@@ -30,7 +24,7 @@ func subscribe(
30
24
context: Any ,
31
25
variableValues: [ String : Map ] = [ : ] ,
32
26
operationName: String ? = nil
33
- ) async throws -> SubscriptionResult {
27
+ ) async throws -> Result < AsyncThrowingStream < GraphQLResult , Error > , GraphQLErrors > {
34
28
let sourceResult = try await createSourceEventStream (
35
29
queryStrategy: queryStrategy,
36
30
mutationStrategy: mutationStrategy,
@@ -43,7 +37,7 @@ func subscribe(
43
37
operationName: operationName
44
38
)
45
39
46
- if let sourceStream = sourceResult. stream {
40
+ return sourceResult. map { sourceStream in
47
41
// We must create a new AsyncSequence because AsyncSequence.map requires a concrete type
48
42
// (which we cannot know),
49
43
// and we need the result to be a concrete type.
@@ -80,30 +74,24 @@ func subscribe(
80
74
task. cancel ( )
81
75
}
82
76
}
83
- return SubscriptionResult ( stream: subscriptionStream, errors: sourceResult. errors)
84
- } else {
85
- return SubscriptionResult ( errors: sourceResult. errors)
77
+ return subscriptionStream
86
78
}
87
79
}
88
80
89
81
/**
90
82
* Implements the "CreateSourceEventStream" algorithm described in the
91
83
* GraphQL specification, resolving the subscription source event stream.
92
84
*
93
- * Returns a Future which resolves to a SourceEventStreamResult, containing
94
- * either an Observable (if successful) or GraphQLErrors (error).
85
+ * Returns a Result that either succeeds with an `AsyncSequence` or fails with `GraphQLErrors`.
95
86
*
96
87
* If the client-provided arguments to this function do not result in a
97
- * compliant subscription, the future will resolve to a
98
- * SourceEventStreamResult containing `errors` and no `observable`.
88
+ * compliant subscription, the `Result` will fail with descriptive errors.
99
89
*
100
90
* If the source stream could not be created due to faulty subscription
101
- * resolver logic or underlying systems, the future will resolve to a
102
- * SourceEventStreamResult containing `errors` and no `observable`.
91
+ * resolver logic or underlying systems, the `Result` will fail with errors.
103
92
*
104
- * If the operation succeeded, the future will resolve to a SubscriptionResult,
105
- * containing an `observable` which yields a stream of event objects
106
- * returned by the subscription resolver.
93
+ * If the operation succeeded, the `Result` will succeed with an AsyncSequence for the
94
+ * event stream returned by the resolver.
107
95
*
108
96
* A Source Event Stream represents a sequence of events, each of which triggers
109
97
* a GraphQL execution for that event.
@@ -123,32 +111,34 @@ func createSourceEventStream(
123
111
context: Any ,
124
112
variableValues: [ String : Map ] = [ : ] ,
125
113
operationName: String ? = nil
126
- ) async throws -> SourceEventStreamResult {
114
+ ) async throws -> Result < any AsyncSequence , GraphQLErrors > {
115
+ // If a valid context cannot be created due to incorrect arguments,
116
+ // this will throw an error.
117
+ let exeContext = try buildExecutionContext (
118
+ queryStrategy: queryStrategy,
119
+ mutationStrategy: mutationStrategy,
120
+ subscriptionStrategy: subscriptionStrategy,
121
+ schema: schema,
122
+ documentAST: documentAST,
123
+ rootValue: rootValue,
124
+ context: context,
125
+ rawVariableValues: variableValues,
126
+ operationName: operationName
127
+ )
127
128
do {
128
- // If a valid context cannot be created due to incorrect arguments,
129
- // this will throw an error.
130
- let exeContext = try buildExecutionContext (
131
- queryStrategy: queryStrategy,
132
- mutationStrategy: mutationStrategy,
133
- subscriptionStrategy: subscriptionStrategy,
134
- schema: schema,
135
- documentAST: documentAST,
136
- rootValue: rootValue,
137
- context: context,
138
- rawVariableValues: variableValues,
139
- operationName: operationName
140
- )
141
129
return try await executeSubscription ( context: exeContext)
142
130
} catch let error as GraphQLError {
143
- return SourceEventStreamResult ( errors: [ error] )
131
+ // If it is a GraphQLError, report it as a failure.
132
+ return . failure( . init( [ error] ) )
144
133
} catch {
145
- return SourceEventStreamResult ( errors: [ GraphQLError ( error) ] )
134
+ // Otherwise treat the error as a system-class error and re-throw it.
135
+ throw error
146
136
}
147
137
}
148
138
149
139
func executeSubscription(
150
140
context: ExecutionContext
151
- ) async throws -> SourceEventStreamResult {
141
+ ) async throws -> Result < any AsyncSequence , GraphQLErrors > {
152
142
// Get the first node
153
143
let type = try getOperationRootType ( schema: context. schema, operation: context. operation)
154
144
var inputFields : OrderedDictionary < String , [ Field ] > = [ : ]
@@ -238,35 +228,21 @@ func executeSubscription(
238
228
resolved = success
239
229
}
240
230
if !context. errors. isEmpty {
241
- return SourceEventStreamResult ( errors : context. errors)
231
+ return . failure ( . init ( context. errors) )
242
232
} else if let error = resolved as? GraphQLError {
243
- return SourceEventStreamResult ( errors : [ error] )
233
+ return . failure ( . init ( [ error] ) )
244
234
} else if let stream = resolved as? any AsyncSequence {
245
- return SourceEventStreamResult ( stream : stream)
235
+ return . success ( stream)
246
236
} else if resolved == nil {
247
- return SourceEventStreamResult ( errors : [
237
+ return . failure ( . init ( [
248
238
GraphQLError ( message: " Resolved subscription was nil " ) ,
249
- ] )
239
+ ] ) )
250
240
} else {
251
241
let resolvedObj = resolved as AnyObject
252
- return SourceEventStreamResult ( errors : [
242
+ return . failure ( . init ( [
253
243
GraphQLError (
254
244
message: " Subscription field resolver must return an AsyncSequence. Received: ' \( resolvedObj) ' "
255
245
) ,
256
- ] )
257
- }
258
- }
259
-
260
- // Subscription resolvers MUST return observables that are declared as 'Any' due to Swift not having
261
- // covariant generic support for type
262
- // checking. Normal resolvers for subscription fields should handle type casting, same as resolvers
263
- // for query fields.
264
- struct SourceEventStreamResult {
265
- public let stream : ( any AsyncSequence ) ?
266
- public let errors : [ GraphQLError ]
267
-
268
- public init ( stream: ( any AsyncSequence ) ? = nil , errors: [ GraphQLError ] = [ ] ) {
269
- self . stream = stream
270
- self . errors = errors
246
+ ] ) )
271
247
}
272
248
}
0 commit comments