@@ -3,22 +3,16 @@ import OrderedCollections
33/**
44 * Implements the "Subscribe" algorithm described in the GraphQL specification.
55 *
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`.
87 *
98 * 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.
1210 *
1311 * 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.
1613 *
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
1915 * representing the response stream.
20- *
21- * Accepts either an object with named arguments, or individual arguments.
2216 */
2317func subscribe(
2418 queryStrategy: QueryFieldExecutionStrategy ,
@@ -30,7 +24,7 @@ func subscribe(
3024 context: Any ,
3125 variableValues: [ String : Map ] = [ : ] ,
3226 operationName: String ? = nil
33- ) async throws -> SubscriptionResult {
27+ ) async throws -> Result < AsyncThrowingStream < GraphQLResult , Error > , GraphQLErrors > {
3428 let sourceResult = try await createSourceEventStream (
3529 queryStrategy: queryStrategy,
3630 mutationStrategy: mutationStrategy,
@@ -43,7 +37,7 @@ func subscribe(
4337 operationName: operationName
4438 )
4539
46- if let sourceStream = sourceResult. stream {
40+ return sourceResult. map { sourceStream in
4741 // We must create a new AsyncSequence because AsyncSequence.map requires a concrete type
4842 // (which we cannot know),
4943 // and we need the result to be a concrete type.
@@ -80,30 +74,24 @@ func subscribe(
8074 task. cancel ( )
8175 }
8276 }
83- return SubscriptionResult ( stream: subscriptionStream, errors: sourceResult. errors)
84- } else {
85- return SubscriptionResult ( errors: sourceResult. errors)
77+ return subscriptionStream
8678 }
8779}
8880
8981/**
9082 * Implements the "CreateSourceEventStream" algorithm described in the
9183 * GraphQL specification, resolving the subscription source event stream.
9284 *
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`.
9586 *
9687 * 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.
9989 *
10090 * 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.
10392 *
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.
10795 *
10896 * A Source Event Stream represents a sequence of events, each of which triggers
10997 * a GraphQL execution for that event.
@@ -123,32 +111,34 @@ func createSourceEventStream(
123111 context: Any ,
124112 variableValues: [ String : Map ] = [ : ] ,
125113 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+ )
127128 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- )
141129 return try await executeSubscription ( context: exeContext)
142130 } 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] ) )
144133 } catch {
145- return SourceEventStreamResult ( errors: [ GraphQLError ( error) ] )
134+ // Otherwise treat the error as a system-class error and re-throw it.
135+ throw error
146136 }
147137}
148138
149139func executeSubscription(
150140 context: ExecutionContext
151- ) async throws -> SourceEventStreamResult {
141+ ) async throws -> Result < any AsyncSequence , GraphQLErrors > {
152142 // Get the first node
153143 let type = try getOperationRootType ( schema: context. schema, operation: context. operation)
154144 var inputFields : OrderedDictionary < String , [ Field ] > = [ : ]
@@ -238,35 +228,21 @@ func executeSubscription(
238228 resolved = success
239229 }
240230 if !context. errors. isEmpty {
241- return SourceEventStreamResult ( errors : context. errors)
231+ return . failure ( . init ( context. errors) )
242232 } else if let error = resolved as? GraphQLError {
243- return SourceEventStreamResult ( errors : [ error] )
233+ return . failure ( . init ( [ error] ) )
244234 } else if let stream = resolved as? any AsyncSequence {
245- return SourceEventStreamResult ( stream : stream)
235+ return . success ( stream)
246236 } else if resolved == nil {
247- return SourceEventStreamResult ( errors : [
237+ return . failure ( . init ( [
248238 GraphQLError ( message: " Resolved subscription was nil " ) ,
249- ] )
239+ ] ) )
250240 } else {
251241 let resolvedObj = resolved as AnyObject
252- return SourceEventStreamResult ( errors : [
242+ return . failure ( . init ( [
253243 GraphQLError (
254244 message: " Subscription field resolver must return an AsyncSequence. Received: ' \( resolvedObj) ' "
255245 ) ,
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+ ] ) )
271247 }
272248}
0 commit comments