diff --git a/Package.resolved b/Package.resolved index d84a2e53..aa537bc7 100644 --- a/Package.resolved +++ b/Package.resolved @@ -3,19 +3,10 @@ { "identity" : "graphql", "kind" : "remoteSourceControl", - "location" : "https://github.com/GraphQLSwift/GraphQL.git", + "location" : "https://github.com/NeedleInAJayStack/GraphQL.git", "state" : { - "revision" : "5e098b3b1789169dded5116c171f716d584225ea", - "version" : "3.0.0" - } - }, - { - "identity" : "swift-atomics", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-atomics.git", - "state" : { - "revision" : "cd142fd2f64be2100422d658e7411e39489da985", - "version" : "1.2.0" + "branch" : "feat/swift-concurrency", + "revision" : "9ec313a8730ecd8b2d7c3768ea0d0791d018b786" } }, { @@ -23,26 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", - "version" : "1.1.4" - } - }, - { - "identity" : "swift-nio", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio.git", - "state" : { - "revision" : "f7dc3f527576c398709b017584392fb58592e7f5", - "version" : "2.75.0" - } - }, - { - "identity" : "swift-system", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-system.git", - "state" : { - "revision" : "c8a44d836fe7913603e246acab7c528c2e780168", - "version" : "1.4.0" + "revision" : "c1805596154bb3a265fd91b8ac0c4433b4348fb0", + "version" : "1.2.0" } } ], diff --git a/Package.swift b/Package.swift index 26346f1e..f5996f5e 100644 --- a/Package.swift +++ b/Package.swift @@ -3,11 +3,16 @@ import PackageDescription let package = Package( name: "Graphiti", + platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6)], products: [ .library(name: "Graphiti", targets: ["Graphiti"]), ], dependencies: [ - .package(url: "https://github.com/GraphQLSwift/GraphQL.git", from: "3.0.0"), + // TODO: Mainline after merge: https://github.com/GraphQLSwift/GraphQL/pull/166 + .package( + url: "https://github.com/NeedleInAJayStack/GraphQL.git", + branch: "feat/swift-concurrency" + ), ], targets: [ .target(name: "Graphiti", dependencies: ["GraphQL"]), diff --git a/README.md b/README.md index e9eeef12..b2efcb6d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Graphiti +# Graphiti Graphiti is a Swift library for building GraphQL schemas fast, safely and easily. @@ -86,7 +86,7 @@ struct MessageAPI : API { let resolver: Resolver let schema: Schema } - + let api = MessageAPI( resolver: Resolver() schema: try! Schema { @@ -124,7 +124,7 @@ let api = MessageAPI( resolver: Resolver() schema: schema ) -``` +```
@@ -136,7 +136,7 @@ final class ChatSchema: PartialSchema { public override var types: Types { Type(Message.self) { Field("content", at: \.content) - } + } } @FieldDefinitions @@ -152,7 +152,7 @@ let api = MessageAPI( resolver: Resolver() schema: schema ) -``` +```
@@ -164,7 +164,7 @@ let chatSchema = PartialSchema( types: { Type(Message.self) { Field("content", at: \.content) - } + } }, query: { Field("message", at: Resolver.message) @@ -178,7 +178,7 @@ let api = MessageAPI( resolver: Resolver() schema: schema ) -``` +``` @@ -190,20 +190,10 @@ let api = MessageAPI( #### Querying -To query the schema we need to pass in a NIO EventLoopGroup to feed the execute function alongside the query itself. - ```swift -import NIO - -let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) -defer { - try? group.syncShutdownGracefully() -} - let result = try await api.execute( request: "{ message { content } }", - context: Context(), - on: group + context: Context() ) print(result) ``` @@ -228,27 +218,13 @@ struct Resolver { } ``` -#### NIO resolvers - -The resolver functions also support `NIO`-style concurrency. To do so, just add one more parameter with type `EventLoopGroup` to the resolver function and change the return type to `EventLoopFuture`. Don't forget to import NIO. - -```swift -import NIO - -struct Resolver { - func message(context: Context, arguments: NoArguments, group: EventLoopGroup) -> EventLoopFuture { - group.next().makeSucceededFuture(context.message()) - } -} -``` - #### Subscription This library supports GraphQL subscriptions, and supports them through the Swift Concurrency `AsyncThrowingStream` type. See the [Usage Guide](UsageGuide.md#subscriptions) for details. If you are unable to use Swift Concurrency, you must create a concrete subclass of the `EventStream` class that implements event streaming functionality. If you don't feel like creating a subclass yourself, you can use the [GraphQLRxSwift](https://github.com/GraphQLSwift/GraphQLRxSwift) repository -to integrate [RxSwift](https://github.com/ReactiveX/RxSwift) observables out-of-the-box. Or you can use that repository as a reference to connect a different +to integrate [RxSwift](https://github.com/ReactiveX/RxSwift) observables out-of-the-box. Or you can use that repository as a reference to connect a different stream library like [ReactiveSwift](https://github.com/ReactiveCocoa/ReactiveSwift), [OpenCombine](https://github.com/OpenCombine/OpenCombine), or one that you've created yourself. diff --git a/Sources/Graphiti/API/API.swift b/Sources/Graphiti/API/API.swift index 32dc1490..3795962e 100644 --- a/Sources/Graphiti/API/API.swift +++ b/Sources/Graphiti/API/API.swift @@ -1,5 +1,4 @@ import GraphQL -import NIO public protocol API { associatedtype Resolver @@ -12,80 +11,6 @@ public extension API { func execute( request: String, context: ContextType, - on eventLoopGroup: EventLoopGroup, - variables: [String: Map] = [:], - operationName: String? = nil, - validationRules: [(ValidationContext) -> Visitor] = [] - ) -> EventLoopFuture { - return schema.execute( - request: request, - resolver: resolver, - context: context, - eventLoopGroup: eventLoopGroup, - variables: variables, - operationName: operationName, - validationRules: validationRules - ) - } - - func execute( - request: GraphQLRequest, - context: ContextType, - on eventLoopGroup: EventLoopGroup, - validationRules: [(ValidationContext) -> Visitor] = [] - ) -> EventLoopFuture { - return execute( - request: request.query, - context: context, - on: eventLoopGroup, - variables: request.variables, - operationName: request.operationName, - validationRules: validationRules - ) - } - - func subscribe( - request: String, - context: ContextType, - on eventLoopGroup: EventLoopGroup, - variables: [String: Map] = [:], - operationName: String? = nil, - validationRules: [(ValidationContext) -> Visitor] = [] - ) -> EventLoopFuture { - return schema.subscribe( - request: request, - resolver: resolver, - context: context, - eventLoopGroup: eventLoopGroup, - variables: variables, - operationName: operationName, - validationRules: validationRules - ) - } - - func subscribe( - request: GraphQLRequest, - context: ContextType, - on eventLoopGroup: EventLoopGroup, - validationRules: [(ValidationContext) -> Visitor] = [] - ) -> EventLoopFuture { - return subscribe( - request: request.query, - context: context, - on: eventLoopGroup, - variables: request.variables, - operationName: request.operationName, - validationRules: validationRules - ) - } -} - -public extension API { - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - func execute( - request: String, - context: ContextType, - on eventLoopGroup: EventLoopGroup, variables: [String: Map] = [:], operationName: String? = nil, validationRules: [(ValidationContext) -> Visitor] = [] @@ -94,35 +19,29 @@ public extension API { request: request, resolver: resolver, context: context, - eventLoopGroup: eventLoopGroup, variables: variables, operationName: operationName, validationRules: validationRules - ).get() + ) } - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) func execute( request: GraphQLRequest, context: ContextType, - on eventLoopGroup: EventLoopGroup, validationRules: [(ValidationContext) -> Visitor] = [] ) async throws -> GraphQLResult { return try await execute( request: request.query, context: context, - on: eventLoopGroup, variables: request.variables, operationName: request.operationName, validationRules: validationRules ) } - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) func subscribe( request: String, context: ContextType, - on eventLoopGroup: EventLoopGroup, variables: [String: Map] = [:], operationName: String? = nil, validationRules: [(ValidationContext) -> Visitor] = [] @@ -131,24 +50,20 @@ public extension API { request: request, resolver: resolver, context: context, - eventLoopGroup: eventLoopGroup, variables: variables, operationName: operationName, validationRules: validationRules - ).get() + ) } - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) func subscribe( request: GraphQLRequest, context: ContextType, - on eventLoopGroup: EventLoopGroup, validationRules: [(ValidationContext) -> Visitor] = [] ) async throws -> SubscriptionResult { return try await subscribe( request: request.query, context: context, - on: eventLoopGroup, variables: request.variables, operationName: request.operationName, validationRules: validationRules diff --git a/Sources/Graphiti/Connection/Connection.swift b/Sources/Graphiti/Connection/Connection.swift index 7a94b490..58a99f75 100644 --- a/Sources/Graphiti/Connection/Connection.swift +++ b/Sources/Graphiti/Connection/Connection.swift @@ -1,13 +1,11 @@ import Foundation import GraphQL -import NIO public struct Connection { public let edges: [Edge] public let pageInfo: PageInfo } -@available(macOS 10.15, macCatalyst 13.0, iOS 13.0, tvOS 13, watchOS 6.0, *) // For Identifiable public extension Connection where Node: Identifiable, Node.ID: LosslessStringConvertible { static func id(_ cursor: String) -> Node.ID? { cursor.base64Decoded().flatMap { Node.ID($0) } @@ -18,54 +16,6 @@ public extension Connection where Node: Identifiable, Node.ID: LosslessStringCon } } -@available(macOS 10.15, macCatalyst 13.0, iOS 13.0, tvOS 13, watchOS 6.0, *) // For Identifiable -public extension EventLoopFuture where Value: Sequence, Value.Element: Identifiable, -Value.Element.ID: LosslessStringConvertible { - func connection(from arguments: Paginatable) -> EventLoopFuture> { - connection(from: arguments, makeCursor: Connection.cursor) - } - - func connection(from arguments: ForwardPaginatable) - -> EventLoopFuture> { - connection(from: arguments, makeCursor: Connection.cursor) - } - - func connection(from arguments: BackwardPaginatable) - -> EventLoopFuture> { - connection(from: arguments, makeCursor: Connection.cursor) - } -} - -public extension EventLoopFuture where Value: Sequence { - func connection( - from arguments: Paginatable, - makeCursor: @escaping (Value.Element) throws -> String - ) -> EventLoopFuture> { - flatMapThrowing { value in - try value.connection(from: arguments, makeCursor: makeCursor) - } - } - - func connection( - from arguments: ForwardPaginatable, - makeCursor: @escaping (Value.Element) throws -> String - ) -> EventLoopFuture> { - flatMapThrowing { value in - try value.connection(from: arguments, makeCursor: makeCursor) - } - } - - func connection( - from arguments: BackwardPaginatable, - makeCursor: @escaping (Value.Element) throws -> String - ) -> EventLoopFuture> { - flatMapThrowing { value in - try value.connection(from: arguments, makeCursor: makeCursor) - } - } -} - -@available(macOS 10.15, macCatalyst 13.0, iOS 13.0, tvOS 13, watchOS 6.0, *) // For Identifiable public extension Sequence where Element: Identifiable, Element.ID: LosslessStringConvertible { func connection(from arguments: Paginatable) throws -> Connection { diff --git a/Sources/Graphiti/Federation/Entity.swift b/Sources/Graphiti/Federation/Entity.swift index eaf21cfe..fbe1f130 100644 --- a/Sources/Graphiti/Federation/Entity.swift +++ b/Sources/Graphiti/Federation/Entity.swift @@ -1,6 +1,5 @@ import Foundation import GraphQL -import NIO struct EntityArguments: Codable { let representations: [Map] diff --git a/Sources/Graphiti/Federation/Key/Key.swift b/Sources/Graphiti/Federation/Key/Key.swift index e05c8e68..e4f00a1c 100644 --- a/Sources/Graphiti/Federation/Key/Key.swift +++ b/Sources/Graphiti/Federation/Key/Key.swift @@ -1,5 +1,4 @@ import GraphQL -import NIO public class Key: KeyComponent< ObjectType, @@ -18,11 +17,10 @@ public class Key: KeyComponen resolver: Resolver, context: Context, map: Map, - eventLoopGroup: EventLoopGroup, coders: Coders - ) throws -> EventLoopFuture { + ) async throws -> Any? { let arguments = try coders.decoder.decode(Arguments.self, from: map) - return try resolve(resolver)(context, arguments, eventLoopGroup).map { $0 as Any? } + return try await resolve(resolver)(context, arguments) } override func validate( @@ -55,61 +53,16 @@ public class Key: KeyComponen resolve = asyncResolve } - convenience init( - arguments: [ArgumentComponent], - simpleAsyncResolve: @escaping SimpleAsyncResolve< - Resolver, - Context, - Arguments, - ObjectType? - > - ) { - let asyncResolve: AsyncResolve = { type in - { context, arguments, group in - // We hop to guarantee that the future will - // return in the same event loop group of the execution. - try simpleAsyncResolve(type)(context, arguments).hop(to: group.next()) - } - } - - self.init(arguments: arguments, asyncResolve: asyncResolve) - } - convenience init( arguments: [ArgumentComponent], syncResolve: @escaping SyncResolve ) { let asyncResolve: AsyncResolve = { type in - { context, arguments, group in - let result = try syncResolve(type)(context, arguments) - return group.next().makeSucceededFuture(result) + { context, arguments in + try syncResolve(type)(context, arguments) } } self.init(arguments: arguments, asyncResolve: asyncResolve) } } - -public extension Key { - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - arguments: [ArgumentComponent], - concurrentResolve: @escaping ConcurrentResolve< - Resolver, - Context, - Arguments, - ObjectType? - > - ) { - let asyncResolve: AsyncResolve = { type in - { context, arguments, eventLoopGroup in - let promise = eventLoopGroup.next().makePromise(of: ObjectType?.self) - promise.completeWithTask { - try await concurrentResolve(type)(context, arguments) - } - return promise.futureResult - } - } - self.init(arguments: arguments, asyncResolve: asyncResolve) - } -} diff --git a/Sources/Graphiti/Federation/Key/KeyComponent.swift b/Sources/Graphiti/Federation/Key/KeyComponent.swift index 0bb51bc4..0fe9ff01 100644 --- a/Sources/Graphiti/Federation/Key/KeyComponent.swift +++ b/Sources/Graphiti/Federation/Key/KeyComponent.swift @@ -1,5 +1,4 @@ import GraphQL -import NIO public class KeyComponent { func mapMatchesArguments(_: Map, coders _: Coders) -> Bool { @@ -10,9 +9,8 @@ public class KeyComponent { resolver _: Resolver, context _: Context, map _: Map, - eventLoopGroup _: EventLoopGroup, coders _: Coders - ) throws -> EventLoopFuture { + ) async throws -> Any? { fatalError() } diff --git a/Sources/Graphiti/Federation/Key/Type+Key.swift b/Sources/Graphiti/Federation/Key/Type+Key.swift index d1e8443c..8685cdf7 100644 --- a/Sources/Graphiti/Federation/Key/Type+Key.swift +++ b/Sources/Graphiti/Federation/Key/Type+Key.swift @@ -34,39 +34,6 @@ public extension Type { return self } - /// Define and add the federated key to this type. - /// - /// For more information, see https://www.apollographql.com/docs/federation/entities - /// - Parameters: - /// - function: The resolver function used to load this entity based on the key value. - /// - _: The key value. The name of this argument must match a Type field. - /// - Returns: Self for chaining. - @discardableResult - func key( - at function: @escaping SimpleAsyncResolve, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) -> Self { - keys.append(Key(arguments: [argument()], simpleAsyncResolve: function)) - return self - } - - /// Define and add the federated key to this type. - /// - /// For more information, see https://www.apollographql.com/docs/federation/entities - /// - Parameters: - /// - function: The resolver function used to load this entity based on the key value. - /// - _: The key values. The names of these arguments must match Type fields. - /// - Returns: Self for chaining. - @discardableResult - func key( - at function: @escaping SimpleAsyncResolve, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) -> Self { - keys.append(Key(arguments: arguments(), simpleAsyncResolve: function)) - return self - } - /// Define and add the federated key to this type. /// /// For more information, see https://www.apollographql.com/docs/federation/entities @@ -100,39 +67,3 @@ public extension Type { return self } } - -public extension Type { - /// Define and add the federated key to this type. - /// - /// For more information, see https://www.apollographql.com/docs/federation/entities - /// - Parameters: - /// - function: The resolver function used to load this entity based on the key value. - /// - _: The key value. The name of this argument must match a Type field. - /// - Returns: Self for chaining. - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - @discardableResult - func key( - at function: @escaping ConcurrentResolve, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) -> Self { - keys.append(Key(arguments: [argument()], concurrentResolve: function)) - return self - } - - /// Define and add the federated key to this type. - /// - /// For more information, see https://www.apollographql.com/docs/federation/entities - /// - Parameters: - /// - function: The resolver function used to load this entity based on the key value. - /// - _: The key values. The names of these arguments must match Type fields. - /// - Returns: Self for chaining. - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - @discardableResult - func key( - at function: @escaping ConcurrentResolve, - @ArgumentComponentBuilder _ arguments: () -> [ArgumentComponent] - ) -> Self { - keys.append(Key(arguments: arguments(), concurrentResolve: function)) - return self - } -} diff --git a/Sources/Graphiti/Federation/Queries.swift b/Sources/Graphiti/Federation/Queries.swift index 15f5dd1e..14265105 100644 --- a/Sources/Graphiti/Federation/Queries.swift +++ b/Sources/Graphiti/Federation/Queries.swift @@ -1,13 +1,12 @@ import GraphQL -import NIO func serviceQuery(for sdl: String) -> GraphQLField { return GraphQLField( type: GraphQLNonNull(serviceType), description: "Return the SDL string for the subschema", - resolve: { _, _, _, eventLoopGroup, _ in + resolve: { _, _, _, _ in let result = Service(sdl: sdl) - return eventLoopGroup.any().makeSucceededFuture(result) + return result } ) } @@ -23,30 +22,34 @@ func entitiesQuery( args: [ "representations": GraphQLArgument(type: GraphQLNonNull(GraphQLList(GraphQLNonNull(anyType)))), ], - resolve: { source, args, context, eventLoopGroup, info in + resolve: { source, args, context, info in let arguments = try coders.decoder.decode(EntityArguments.self, from: args) - let futures: [EventLoopFuture] = try arguments.representations - .map { (representationMap: Map) in - let representation = try coders.decoder.decode( - EntityRepresentation.self, - from: representationMap - ) - guard let resolve = federatedResolvers[representation.__typename] else { - throw GraphQLError( - message: "Federated type not found: \(representation.__typename)" + + var result: [Any?] = arguments.representations.map { _ in nil } + + try await withThrowingTaskGroup(of: Void.self) { group in + for (index, representationMap) in arguments.representations.enumerated() { + group.addTask { + let representation = try coders.decoder.decode( + EntityRepresentation.self, + from: representationMap + ) + guard let resolve = federatedResolvers[representation.__typename] else { + throw GraphQLError( + message: "Federated type not found: \(representation.__typename)" + ) + } + result[index] = try await resolve( + source, + representationMap, + context, + info ) } - return try resolve( - source, - representationMap, - context, - eventLoopGroup, - info - ) } - - return futures.flatten(on: eventLoopGroup) - .map { $0 as Any? } + try await group.waitForAll() + } + return result } ) } diff --git a/Sources/Graphiti/Field/Field/Field.swift b/Sources/Graphiti/Field/Field/Field.swift index 5ba7ea14..0f8db205 100644 --- a/Sources/Graphiti/Field/Field/Field.swift +++ b/Sources/Graphiti/Field/Field/Field.swift @@ -1,5 +1,4 @@ import GraphQL -import NIO public class Field: FieldComponent< ObjectType, @@ -18,7 +17,7 @@ public class Field: FieldC description: description, deprecationReason: deprecationReason, args: arguments(typeProvider: typeProvider, coders: coders), - resolve: { source, arguments, context, eventLoopGroup, _ in + resolve: { source, arguments, context, _ in guard let s = source as? ObjectType else { throw GraphQLError( message: "Expected source type \(ObjectType.self) but got \(type(of: source))" @@ -32,7 +31,7 @@ public class Field: FieldC } let a = try coders.decoder.decode(Arguments.self, from: arguments) - return try self.resolve(s)(c, a, eventLoopGroup) + return try await self.resolve(s)(c, a) } ) @@ -66,43 +65,21 @@ public class Field: FieldC asyncResolve: @escaping AsyncResolve ) { let resolve: AsyncResolve = { type in - { context, arguments, eventLoopGroup in - try asyncResolve(type)(context, arguments, eventLoopGroup).map { $0 as Any? } + { context, arguments in + try await asyncResolve(type)(context, arguments) } } self.init(name: name, arguments: arguments, resolve: resolve) } - convenience init( - name: String, - arguments: [ArgumentComponent], - simpleAsyncResolve: @escaping SimpleAsyncResolve< - ObjectType, - Context, - Arguments, - ResolveType - > - ) { - let asyncResolve: AsyncResolve = { type in - { context, arguments, group in - // We hop to guarantee that the future will - // return in the same event loop group of the execution. - try simpleAsyncResolve(type)(context, arguments).hop(to: group.next()) - } - } - - self.init(name: name, arguments: arguments, asyncResolve: asyncResolve) - } - convenience init( name: String, arguments: [ArgumentComponent], syncResolve: @escaping SyncResolve ) { let asyncResolve: AsyncResolve = { type in - { context, arguments, group in - let result = try syncResolve(type)(context, arguments) - return group.next().makeSucceededFuture(result) + { context, arguments in + try syncResolve(type)(context, arguments) } } @@ -131,27 +108,6 @@ public extension Field { } } -// MARK: SimpleAsyncResolve Initializers - -public extension Field { - convenience init( - _ name: String, - at function: @escaping SimpleAsyncResolve, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init(name: name, arguments: [argument()], simpleAsyncResolve: function) - } - - convenience init( - _ name: String, - at function: @escaping SimpleAsyncResolve, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init(name: name, arguments: arguments(), simpleAsyncResolve: function) - } -} - // MARK: SyncResolve Initializers // '@_disfavoredOverload' is included below because otherwise `SimpleAsyncResolve` initializers also match this signature, causing the @@ -194,51 +150,3 @@ public extension Field where Arguments == NoArguments { self.init(name: name, arguments: [], syncResolve: syncResolve) } } - -public extension Field { - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - name: String, - arguments: [ArgumentComponent], - concurrentResolve: @escaping ConcurrentResolve< - ObjectType, - Context, - Arguments, - ResolveType - > - ) { - let asyncResolve: AsyncResolve = { type in - { context, arguments, eventLoopGroup in - let promise = eventLoopGroup.next().makePromise(of: ResolveType.self) - promise.completeWithTask { - try await concurrentResolve(type)(context, arguments) - } - return promise.futureResult - } - } - self.init(name: name, arguments: arguments, asyncResolve: asyncResolve) - } -} - -// MARK: ConcurrentResolve Initializers - -public extension Field { - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - _ name: String, - at function: @escaping ConcurrentResolve, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init(name: name, arguments: [argument()], concurrentResolve: function) - } - - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - _ name: String, - at function: @escaping ConcurrentResolve, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init(name: name, arguments: arguments(), concurrentResolve: function) - } -} diff --git a/Sources/Graphiti/Field/Resolve/AsyncResolve.swift b/Sources/Graphiti/Field/Resolve/AsyncResolve.swift index 9f476631..0c9d7de8 100644 --- a/Sources/Graphiti/Field/Resolve/AsyncResolve.swift +++ b/Sources/Graphiti/Field/Resolve/AsyncResolve.swift @@ -1,9 +1,7 @@ -import NIO public typealias AsyncResolve = ( _ object: ObjectType ) -> ( _ context: Context, - _ arguments: Arguments, - _ eventLoopGroup: EventLoopGroup -) throws -> EventLoopFuture + _ arguments: Arguments +) async throws -> ResolveType diff --git a/Sources/Graphiti/Field/Resolve/ConcurrentResolve.swift b/Sources/Graphiti/Field/Resolve/ConcurrentResolve.swift deleted file mode 100644 index abfb0704..00000000 --- a/Sources/Graphiti/Field/Resolve/ConcurrentResolve.swift +++ /dev/null @@ -1,9 +0,0 @@ -import NIO - -@available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) -public typealias ConcurrentResolve = ( - _ object: ObjectType -) -> ( - _ context: Context, - _ arguments: Arguments -) async throws -> ResolveType diff --git a/Sources/Graphiti/Field/Resolve/SimpleAsyncResolve.swift b/Sources/Graphiti/Field/Resolve/SimpleAsyncResolve.swift deleted file mode 100644 index 4eaa6db7..00000000 --- a/Sources/Graphiti/Field/Resolve/SimpleAsyncResolve.swift +++ /dev/null @@ -1,8 +0,0 @@ -import NIO - -public typealias SimpleAsyncResolve = ( - _ object: ObjectType -) -> ( - _ context: Context, - _ arguments: Arguments -) throws -> EventLoopFuture diff --git a/Sources/Graphiti/Mutation/Mutation.swift b/Sources/Graphiti/Mutation/Mutation.swift index 51f8773e..9910d1df 100644 --- a/Sources/Graphiti/Mutation/Mutation.swift +++ b/Sources/Graphiti/Mutation/Mutation.swift @@ -3,7 +3,7 @@ import GraphQL public final class Mutation: Component { let fields: [FieldComponent] - let isTypeOf: GraphQLIsTypeOf = { source, _, _ in + let isTypeOf: GraphQLIsTypeOf = { source, _ in source is Resolver } diff --git a/Sources/Graphiti/Query/Query.swift b/Sources/Graphiti/Query/Query.swift index 7c257e1d..4a7b04e5 100644 --- a/Sources/Graphiti/Query/Query.swift +++ b/Sources/Graphiti/Query/Query.swift @@ -3,7 +3,7 @@ import GraphQL public final class Query: Component { let fields: [FieldComponent] - let isTypeOf: GraphQLIsTypeOf = { source, _, _ in + let isTypeOf: GraphQLIsTypeOf = { source, _ in source is Resolver } diff --git a/Sources/Graphiti/Schema/Schema.swift b/Sources/Graphiti/Schema/Schema.swift index 590b8607..596ed99f 100644 --- a/Sources/Graphiti/Schema/Schema.swift +++ b/Sources/Graphiti/Schema/Schema.swift @@ -1,5 +1,4 @@ import GraphQL -import NIO public struct SchemaError: Error, Equatable { let description: String @@ -65,49 +64,37 @@ public extension Schema { request: String, resolver: Resolver, context: Context, - eventLoopGroup: EventLoopGroup, variables: [String: Map] = [:], operationName: String? = nil, validationRules: [(ValidationContext) -> Visitor] = [] - ) -> EventLoopFuture { - do { - return try graphql( - validationRules: GraphQL.specifiedRules + validationRules, - schema: schema, - request: request, - rootValue: resolver, - context: context, - eventLoopGroup: eventLoopGroup, - variableValues: variables, - operationName: operationName - ) - } catch { - return eventLoopGroup.next().makeFailedFuture(error) - } + ) async throws -> GraphQLResult { + return try await graphql( + validationRules: GraphQL.specifiedRules + validationRules, + schema: schema, + request: request, + rootValue: resolver, + context: context, + variableValues: variables, + operationName: operationName + ) } func subscribe( request: String, resolver: Resolver, context: Context, - eventLoopGroup: EventLoopGroup, variables: [String: Map] = [:], operationName: String? = nil, validationRules: [(ValidationContext) -> Visitor] = [] - ) -> EventLoopFuture { - do { - return try graphqlSubscribe( - validationRules: GraphQL.specifiedRules + validationRules, - schema: schema, - request: request, - rootValue: resolver, - context: context, - eventLoopGroup: eventLoopGroup, - variableValues: variables, - operationName: operationName - ) - } catch { - return eventLoopGroup.next().makeFailedFuture(error) - } + ) async throws -> SubscriptionResult { + return try await graphqlSubscribe( + validationRules: GraphQL.specifiedRules + validationRules, + schema: schema, + request: request, + rootValue: resolver, + context: context, + variableValues: variables, + operationName: operationName + ) } } diff --git a/Sources/Graphiti/Subscription/SubscribeField.swift b/Sources/Graphiti/Subscription/SubscribeField.swift index 1e4029ed..cfa70c43 100644 --- a/Sources/Graphiti/Subscription/SubscribeField.swift +++ b/Sources/Graphiti/Subscription/SubscribeField.swift @@ -1,5 +1,4 @@ import GraphQL -import NIO // Subscription resolver MUST return an Observer, not a specific type, due to lack of support for covariance generics in Swift @@ -8,12 +7,13 @@ public class SubscriptionField< ObjectType, Context, FieldType, - Arguments: Decodable ->: FieldComponent { + Arguments: Decodable, + SubSequence: AsyncSequence +>: FieldComponent where SubSequence.Element == SourceEventType { let name: String let arguments: [ArgumentComponent] let resolve: AsyncResolve - let subscribe: AsyncResolve> + let subscribe: AsyncResolve override func field( typeProvider: TypeProvider, @@ -24,7 +24,7 @@ public class SubscriptionField< description: description, deprecationReason: deprecationReason, args: arguments(typeProvider: typeProvider, coders: coders), - resolve: { source, arguments, context, eventLoopGroup, _ in + resolve: { source, arguments, context, _ in guard let _source = source as? SourceEventType else { throw GraphQLError( message: "Expected source type \(SourceEventType.self) but got \(type(of: source))" @@ -38,9 +38,9 @@ public class SubscriptionField< } let args = try coders.decoder.decode(Arguments.self, from: arguments) - return try self.resolve(_source)(_context, args, eventLoopGroup) + return try await self.resolve(_source)(_context, args) }, - subscribe: { source, arguments, context, eventLoopGroup, _ in + subscribe: { source, arguments, context, _ in guard let _source = source as? ObjectType else { throw GraphQLError( message: "Expected source type \(ObjectType.self) but got \(type(of: source))" @@ -54,8 +54,8 @@ public class SubscriptionField< } let args = try coders.decoder.decode(Arguments.self, from: arguments) - return try self.subscribe(_source)(_context, args, eventLoopGroup) - .map { $0.map { $0 as Any } } + return try await self.subscribe(_source)(_context, args) + .map { $0 as Any } } ) @@ -81,9 +81,9 @@ public class SubscriptionField< ObjectType, Context, Arguments, - EventStream + SubSequence > - ) { + ) { self.name = name self.arguments = arguments self.resolve = resolve @@ -98,12 +98,12 @@ public class SubscriptionField< ObjectType, Context, Arguments, - EventStream + SubSequence > - ) { + ) { let resolve: AsyncResolve = { type in - { context, arguments, eventLoopGroup in - try asyncResolve(type)(context, arguments, eventLoopGroup).map { $0 } + { context, arguments in + try await asyncResolve(type)(context, arguments) } } self.init(name: name, arguments: arguments, resolve: resolve, subscribe: asyncSubscribe) @@ -117,87 +117,17 @@ public class SubscriptionField< ObjectType, Context, Arguments, - EventStream + SubSequence > - ) { + ) { let resolve: AsyncResolve = { source in - { _, _, eventLoopGroup in - eventLoopGroup.next().makeSucceededFuture(source) + { _, _ in + source } } self.init(name: name, arguments: arguments, resolve: resolve, subscribe: asyncSubscribe) } - convenience init( - name: String, - arguments: [ArgumentComponent], - simpleAsyncResolve: @escaping SimpleAsyncResolve< - SourceEventType, - Context, - Arguments, - ResolveType - >, - simpleAsyncSubscribe: @escaping SimpleAsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - > - ) { - let asyncResolve: AsyncResolve = { type in - { context, arguments, group in - // We hop to guarantee that the future will - // return in the same event loop group of the execution. - try simpleAsyncResolve(type)(context, arguments).hop(to: group.next()) - } - } - - let asyncSubscribe: AsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - > = { type in - { context, arguments, group in - // We hop to guarantee that the future will - // return in the same event loop group of the execution. - try simpleAsyncSubscribe(type)(context, arguments).hop(to: group.next()) - } - } - self.init( - name: name, - arguments: arguments, - asyncResolve: asyncResolve, - asyncSubscribe: asyncSubscribe - ) - } - - convenience init( - name: String, - arguments: [ArgumentComponent], - as: FieldType.Type, - simpleAsyncSubscribe: @escaping SimpleAsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - > - ) { - let asyncSubscribe: AsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - > = { type in - { context, arguments, group in - // We hop to guarantee that the future will - // return in the same event loop group of the execution. - try simpleAsyncSubscribe(type)(context, arguments).hop(to: group.next()) - } - } - self.init(name: name, arguments: arguments, as: `as`, asyncSubscribe: asyncSubscribe) - } - convenience init( name: String, arguments: [ArgumentComponent], @@ -206,13 +136,12 @@ public class SubscriptionField< ObjectType, Context, Arguments, - EventStream + SubSequence > - ) { + ) { let asyncResolve: AsyncResolve = { type in - { context, arguments, group in - let result = try syncResolve(type)(context, arguments) - return group.next().makeSucceededFuture(result) + { context, arguments in + try syncResolve(type)(context, arguments) } } @@ -220,11 +149,10 @@ public class SubscriptionField< ObjectType, Context, Arguments, - EventStream + SubSequence > = { type in - { context, arguments, group in - let result = try syncSubscribe(type)(context, arguments) - return group.next().makeSucceededFuture(result) + { context, arguments in + try syncSubscribe(type)(context, arguments) } } self.init( @@ -243,18 +171,17 @@ public class SubscriptionField< ObjectType, Context, Arguments, - EventStream + SubSequence > ) { let asyncSubscribe: AsyncResolve< ObjectType, Context, Arguments, - EventStream + SubSequence > = { type in - { context, arguments, group in - let result = try syncSubscribe(type)(context, arguments) - return group.next().makeSucceededFuture(result) + { context, arguments in + try syncSubscribe(type)(context, arguments) } } self.init(name: name, arguments: arguments, as: `as`, asyncSubscribe: asyncSubscribe) @@ -271,7 +198,7 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ argument: () -> ArgumentComponent ) { @@ -290,7 +217,7 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ arguments: () -> [ArgumentComponent] = { [] } @@ -312,7 +239,7 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ argument: () -> ArgumentComponent ) { @@ -326,7 +253,7 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ arguments: () -> [ArgumentComponent] = { [] } @@ -341,7 +268,7 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ argument: () -> ArgumentComponent ) { @@ -360,7 +287,7 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ arguments: () -> [ArgumentComponent] = { [] } @@ -374,121 +301,6 @@ public extension SubscriptionField { } } -// MARK: SimpleAsyncResolve Initializers - -public extension SubscriptionField { - convenience init( - _ name: String, - at function: @escaping SimpleAsyncResolve, - atSub subFunc: @escaping SimpleAsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init( - name: name, - arguments: [argument()], - simpleAsyncResolve: function, - simpleAsyncSubscribe: subFunc - ) - } - - convenience init( - _ name: String, - at function: @escaping SimpleAsyncResolve, - atSub subFunc: @escaping SimpleAsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init( - name: name, - arguments: arguments(), - simpleAsyncResolve: function, - simpleAsyncSubscribe: subFunc - ) - } -} - -public extension SubscriptionField { - convenience init( - _ name: String, - as: FieldType.Type, - atSub subFunc: @escaping SimpleAsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init(name: name, arguments: [argument()], as: `as`, simpleAsyncSubscribe: subFunc) - } - - convenience init( - _ name: String, - as: FieldType.Type, - atSub subFunc: @escaping SimpleAsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init(name: name, arguments: arguments(), as: `as`, simpleAsyncSubscribe: subFunc) - } - - convenience init( - _ name: String, - at function: @escaping SimpleAsyncResolve, - as: FieldType.Type, - atSub subFunc: @escaping SimpleAsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init( - name: name, - arguments: [argument()], - simpleAsyncResolve: function, - simpleAsyncSubscribe: subFunc - ) - } - - convenience init( - _ name: String, - at function: @escaping SimpleAsyncResolve, - as: FieldType.Type, - atSub subFunc: @escaping SimpleAsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init( - name: name, - arguments: arguments(), - simpleAsyncResolve: function, - simpleAsyncSubscribe: subFunc - ) - } -} - // MARK: SyncResolve Initializers // '@_disfavoredOverload' is included below because otherwise `SimpleAsyncResolve` initializers also match this signature, causing the @@ -503,10 +315,10 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { + ) { self.init( name: name, arguments: [argument()], @@ -523,11 +335,11 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ arguments: () -> [ArgumentComponent] = { [] } - ) { + ) { self.init(name: name, arguments: arguments(), syncResolve: function, syncSubscribe: subFunc) } } @@ -541,10 +353,10 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { + ) { self.init(name: name, arguments: [argument()], as: `as`, syncSubscribe: subFunc) } @@ -556,11 +368,11 @@ public extension SubscriptionField { ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ arguments: () -> [ArgumentComponent] = { [] } - ) { + ) { self.init(name: name, arguments: arguments(), as: `as`, syncSubscribe: subFunc) } @@ -568,15 +380,15 @@ public extension SubscriptionField { convenience init( _ name: String, at function: @escaping SyncResolve, - as: FieldType.Type, + as _: FieldType.Type, atSub subFunc: @escaping SyncResolve< ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { + ) { self.init( name: name, arguments: [argument()], @@ -589,242 +401,20 @@ public extension SubscriptionField { convenience init( _ name: String, at function: @escaping SyncResolve, - as: FieldType.Type, + as _: FieldType.Type, atSub subFunc: @escaping SyncResolve< ObjectType, Context, Arguments, - EventStream + SubSequence >, @ArgumentComponentBuilder _ arguments: () -> [ArgumentComponent] = { [] } - ) { + ) { self.init(name: name, arguments: arguments(), syncResolve: function, syncSubscribe: subFunc) } } -public extension SubscriptionField { - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - name: String, - arguments: [ArgumentComponent], - concurrentResolve: @escaping ConcurrentResolve< - SourceEventType, - Context, - Arguments, - ResolveType - >, - concurrentSubscribe: @escaping ConcurrentResolve< - ObjectType, - Context, - Arguments, - EventStream - > - ) { - let asyncResolve: AsyncResolve = { type in - { context, arguments, eventLoopGroup in - let promise = eventLoopGroup.next().makePromise(of: ResolveType.self) - promise.completeWithTask { - try await concurrentResolve(type)(context, arguments) - } - return promise.futureResult - } - } - let asyncSubscribe: AsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - > = { type in - { context, arguments, eventLoopGroup in - let promise = eventLoopGroup.next() - .makePromise(of: EventStream.self) - promise.completeWithTask { - try await concurrentSubscribe(type)(context, arguments) - } - return promise.futureResult - } - } - self.init( - name: name, - arguments: arguments, - asyncResolve: asyncResolve, - asyncSubscribe: asyncSubscribe - ) - } - - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - name: String, - arguments: [ArgumentComponent], - as: FieldType.Type, - concurrentSubscribe: @escaping ConcurrentResolve< - ObjectType, - Context, - Arguments, - EventStream - > - ) { - let asyncSubscribe: AsyncResolve< - ObjectType, - Context, - Arguments, - EventStream - > = { type in - { context, arguments, eventLoopGroup in - let promise = eventLoopGroup.next() - .makePromise(of: EventStream.self) - promise.completeWithTask { - try await concurrentSubscribe(type)(context, arguments) - } - return promise.futureResult - } - } - self.init(name: name, arguments: arguments, as: `as`, asyncSubscribe: asyncSubscribe) - } -} - -// MARK: ConcurrentResolve Initializers - -public extension SubscriptionField { - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - _ name: String, - at function: @escaping ConcurrentResolve< - SourceEventType, - Context, - Arguments, - FieldType - >, - atSub subFunc: @escaping ConcurrentResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init( - name: name, - arguments: [argument()], - concurrentResolve: function, - concurrentSubscribe: subFunc - ) - } - - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - _ name: String, - at function: @escaping ConcurrentResolve< - SourceEventType, - Context, - Arguments, - FieldType - >, - atSub subFunc: @escaping ConcurrentResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init( - name: name, - arguments: arguments(), - concurrentResolve: function, - concurrentSubscribe: subFunc - ) - } - - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - _ name: String, - as: FieldType.Type, - atSub subFunc: @escaping ConcurrentResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ arguments: () -> ArgumentComponent - ) { - self.init(name: name, arguments: [arguments()], as: `as`, concurrentSubscribe: subFunc) - } - - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - _ name: String, - as: FieldType.Type, - atSub subFunc: @escaping ConcurrentResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init(name: name, arguments: arguments(), as: `as`, concurrentSubscribe: subFunc) - } -} - -public extension SubscriptionField { - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - _ name: String, - at function: @escaping ConcurrentResolve< - SourceEventType, - Context, - Arguments, - ResolveType - >, - as: FieldType.Type, - atSub subFunc: @escaping ConcurrentResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init( - name: name, - arguments: [argument()], - concurrentResolve: function, - concurrentSubscribe: subFunc - ) - } - - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - _ name: String, - at function: @escaping ConcurrentResolve< - SourceEventType, - Context, - Arguments, - ResolveType - >, - as: FieldType.Type, - atSub subFunc: @escaping ConcurrentResolve< - ObjectType, - Context, - Arguments, - EventStream - >, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init( - name: name, - arguments: arguments(), - concurrentResolve: function, - concurrentSubscribe: subFunc - ) - } -} - // TODO: Determine if we can use keypaths to initialize // MARK: Keypath Initializers diff --git a/Sources/Graphiti/Subscription/SubscribeResolve.swift b/Sources/Graphiti/Subscription/SubscribeResolve.swift index 65fdc962..9e55582f 100644 --- a/Sources/Graphiti/Subscription/SubscribeResolve.swift +++ b/Sources/Graphiti/Subscription/SubscribeResolve.swift @@ -1,9 +1,7 @@ -import NIO public typealias SubscribeResolve = ( _ object: ObjectType ) -> ( _ context: Context, - _ arguments: Arguments, - _ eventLoopGroup: EventLoopGroup -) throws -> EventLoopFuture + _ arguments: Arguments +) async throws -> ResolveType diff --git a/Sources/Graphiti/Subscription/Subscription.swift b/Sources/Graphiti/Subscription/Subscription.swift index e7752054..44ccc225 100644 --- a/Sources/Graphiti/Subscription/Subscription.swift +++ b/Sources/Graphiti/Subscription/Subscription.swift @@ -3,7 +3,7 @@ import GraphQL public final class Subscription: Component { let fields: [FieldComponent] - let isTypeOf: GraphQLIsTypeOf = { source, _, _ in + let isTypeOf: GraphQLIsTypeOf = { source, _ in source is Resolver } diff --git a/Sources/Graphiti/Type/Type.swift b/Sources/Graphiti/Type/Type.swift index 96c74bdc..44beef05 100644 --- a/Sources/Graphiti/Type/Type.swift +++ b/Sources/Graphiti/Type/Type.swift @@ -8,7 +8,7 @@ public final class Type: TypeComponent< var keys: [KeyComponent] let fields: [FieldComponent] - let isTypeOf: GraphQLIsTypeOf = { source, _, _ in + let isTypeOf: GraphQLIsTypeOf = { source, _ in source is ObjectType } @@ -36,7 +36,7 @@ public final class Type: TypeComponent< // If federation keys are included, create resolver closure if !keys.isEmpty { - let resolve: GraphQLFieldResolve = { source, args, context, eventLoopGroup, _ in + let resolve: GraphQLFieldResolve = { source, args, context, _ in guard let s = source as? Resolver else { throw GraphQLError( message: "Expected source type \(ObjectType.self) but got \(type(of: source))" @@ -58,11 +58,10 @@ public final class Type: TypeComponent< ) } - return try key.resolveMap( + return try await key.resolveMap( resolver: s, context: c, map: args, - eventLoopGroup: eventLoopGroup, coders: coders ) } diff --git a/Tests/GraphitiTests/ConnectionTests.swift b/Tests/GraphitiTests/ConnectionTests.swift index 96e3f43c..bba44987 100644 --- a/Tests/GraphitiTests/ConnectionTests.swift +++ b/Tests/GraphitiTests/ConnectionTests.swift @@ -1,6 +1,5 @@ import Foundation import Graphiti -import NIO import XCTest class ConnectionTests: XCTestCase { @@ -40,35 +39,33 @@ class ConnectionTests: XCTestCase { } } - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - /// Test that connection objects work as expected - func testConnection() throws { - XCTAssertEqual( - try schema.execute( - request: """ - { - comments { - edges { - cursor - node { - id - message - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor + func testConnection() async throws { + let result = try await schema.execute( + request: """ + { + comments { + edges { + cursor + node { + id + message } } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } } - """, - resolver: .init(), - context: NoContext(), - eventLoopGroup: eventLoopGroup - ).wait(), + } + """, + resolver: .init(), + context: NoContext() + ) + XCTAssertEqual( + result, .init( data: [ "comments": [ @@ -108,31 +105,31 @@ class ConnectionTests: XCTestCase { } /// Test that `first` argument works as intended - func testFirst() throws { - XCTAssertEqual( - try schema.execute( - request: """ - { - comments(first: 1) { - edges { - node { - id - message - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor + func testFirst() async throws { + let result = try await schema.execute( + request: """ + { + comments(first: 1) { + edges { + node { + id + message } } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } } - """, - resolver: .init(), - context: NoContext(), - eventLoopGroup: eventLoopGroup - ).wait(), + } + """, + resolver: .init(), + context: NoContext() + ) + XCTAssertEqual( + result, .init( data: [ "comments": [ @@ -157,31 +154,31 @@ class ConnectionTests: XCTestCase { } /// Test that `after` argument works as intended - func testAfter() throws { - XCTAssertEqual( - try schema.execute( - request: """ - { - comments(after: "MQ==") { - edges { - node { - id - message - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor + func testAfter() async throws { + let result = try await schema.execute( + request: """ + { + comments(after: "MQ==") { + edges { + node { + id + message } } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } } - """, - resolver: .init(), - context: NoContext(), - eventLoopGroup: eventLoopGroup - ).wait(), + } + """, + resolver: .init(), + context: NoContext() + ) + XCTAssertEqual( + result, .init( data: [ "comments": [ @@ -212,31 +209,31 @@ class ConnectionTests: XCTestCase { } /// Test that mixing `first` and `after` arguments works as intended - func testFirstAfter() throws { - XCTAssertEqual( - try schema.execute( - request: """ - { - comments(first: 1, after: "MQ==") { - edges { - node { - id - message - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor + func testFirstAfter() async throws { + let result = try await schema.execute( + request: """ + { + comments(first: 1, after: "MQ==") { + edges { + node { + id + message } } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } } - """, - resolver: .init(), - context: NoContext(), - eventLoopGroup: eventLoopGroup - ).wait(), + } + """, + resolver: .init(), + context: NoContext() + ) + XCTAssertEqual( + result, .init( data: [ "comments": [ @@ -261,31 +258,31 @@ class ConnectionTests: XCTestCase { } /// Test that `last` argument works as intended - func testLast() throws { - XCTAssertEqual( - try schema.execute( - request: """ - { - comments(last: 1) { - edges { - node { - id - message - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor + func testLast() async throws { + let result = try await schema.execute( + request: """ + { + comments(last: 1) { + edges { + node { + id + message } } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } } - """, - resolver: .init(), - context: NoContext(), - eventLoopGroup: eventLoopGroup - ).wait(), + } + """, + resolver: .init(), + context: NoContext() + ) + XCTAssertEqual( + result, .init( data: [ "comments": [ @@ -310,31 +307,31 @@ class ConnectionTests: XCTestCase { } /// Test that `before` argument works as intended - func testBefore() throws { - XCTAssertEqual( - try schema.execute( - request: """ - { - comments(before: "Mw==") { - edges { - node { - id - message - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor + func testBefore() async throws { + let result = try await schema.execute( + request: """ + { + comments(before: "Mw==") { + edges { + node { + id + message } } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } } - """, - resolver: .init(), - context: NoContext(), - eventLoopGroup: eventLoopGroup - ).wait(), + } + """, + resolver: .init(), + context: NoContext() + ) + XCTAssertEqual( + result, .init( data: [ "comments": [ @@ -365,31 +362,31 @@ class ConnectionTests: XCTestCase { } /// Test that mixing `last` with `before` argument works as intended - func testLastBefore() throws { - XCTAssertEqual( - try schema.execute( - request: """ - { - comments(last: 1, before: "Mw==") { - edges { - node { - id - message - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor + func testLastBefore() async throws { + let result = try await schema.execute( + request: """ + { + comments(last: 1, before: "Mw==") { + edges { + node { + id + message } } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } } - """, - resolver: .init(), - context: NoContext(), - eventLoopGroup: eventLoopGroup - ).wait(), + } + """, + resolver: .init(), + context: NoContext() + ) + XCTAssertEqual( + result, .init( data: [ "comments": [ @@ -414,31 +411,31 @@ class ConnectionTests: XCTestCase { } /// Test that mixing `after` with `before` argument works as intended - func testAfterBefore() throws { - XCTAssertEqual( - try schema.execute( - request: """ - { - comments(after: "MQ==", before: "Mw==") { - edges { - node { - id - message - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor + func testAfterBefore() async throws { + let result = try await schema.execute( + request: """ + { + comments(after: "MQ==", before: "Mw==") { + edges { + node { + id + message } } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } } - """, - resolver: .init(), - context: NoContext(), - eventLoopGroup: eventLoopGroup - ).wait(), + } + """, + resolver: .init(), + context: NoContext() + ) + XCTAssertEqual( + result, .init( data: [ "comments": [ @@ -463,7 +460,7 @@ class ConnectionTests: XCTestCase { } /// Test that adjusting names using `as` works - func testNaming() throws { + func testNaming() async throws { struct ChatObject: Codable { func messages( context _: NoContext, @@ -509,26 +506,26 @@ class ConnectionTests: XCTestCase { } } - XCTAssertEqual( - try schema.execute( - request: """ - { - chatObject { - messages { - edges { - node { - id - text - } + let result = try await schema.execute( + request: """ + { + chatObject { + messages { + edges { + node { + id + text } } } } - """, - resolver: .init(), - context: NoContext(), - eventLoopGroup: eventLoopGroup - ).wait(), + } + """, + resolver: .init(), + context: NoContext() + ) + XCTAssertEqual( + result, .init( data: [ "chatObject": [ diff --git a/Tests/GraphitiTests/DefaultValueTests.swift b/Tests/GraphitiTests/DefaultValueTests.swift index f5b41a8e..81f11c54 100644 --- a/Tests/GraphitiTests/DefaultValueTests.swift +++ b/Tests/GraphitiTests/DefaultValueTests.swift @@ -1,120 +1,117 @@ import Graphiti import GraphQL -import NIO import XCTest class DefaultValueTests: XCTestCase { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - func testBoolDefault() async throws { + let result = try await DefaultValueAPI().execute( + request: """ + { + bool + } + """, + context: NoContext() + ) XCTAssertEqual( - try DefaultValueAPI().execute( - request: """ - { - bool - } - """, - context: NoContext(), - on: eventLoopGroup - ).wait(), + result, .init(data: ["bool": true]) ) } func testIntDefault() async throws { + let result = try await DefaultValueAPI().execute( + request: """ + { + int + } + """, + context: NoContext() + ) XCTAssertEqual( - try DefaultValueAPI().execute( - request: """ - { - int - } - """, - context: NoContext(), - on: eventLoopGroup - ).wait(), + result, .init(data: ["int": 1]) ) } func testFloatDefault() async throws { + let result = try await DefaultValueAPI().execute( + request: """ + { + float + } + """, + context: NoContext() + ) XCTAssertEqual( - try DefaultValueAPI().execute( - request: """ - { - float - } - """, - context: NoContext(), - on: eventLoopGroup - ).wait(), + result, .init(data: ["float": 1.1]) ) } func testStringDefault() async throws { + let result = try await DefaultValueAPI().execute( + request: """ + { + string + } + """, + context: NoContext() + ) XCTAssertEqual( - try DefaultValueAPI().execute( - request: """ - { - string - } - """, - context: NoContext(), - on: eventLoopGroup - ).wait(), + result, .init(data: ["string": "hello"]) ) } func testEnumDefault() async throws { + let result = try await DefaultValueAPI().execute( + request: """ + { + enum + } + """, + context: NoContext() + ) XCTAssertEqual( - try DefaultValueAPI().execute( - request: """ - { - enum - } - """, - context: NoContext(), - on: eventLoopGroup - ).wait(), + result, .init(data: ["enum": "valueA"]) ) } func testArrayDefault() async throws { + let result = try await DefaultValueAPI().execute( + request: """ + { + array + } + """, + context: NoContext() + ) XCTAssertEqual( - try DefaultValueAPI().execute( - request: """ - { - array - } - """, - context: NoContext(), - on: eventLoopGroup - ).wait(), + result, .init(data: ["array": ["a", "b", "c"]]) ) } func testInputDefault() async throws { // Test input object argument default - XCTAssertEqual( - try DefaultValueAPI().execute( - request: """ - { - input { - bool - int - float - string - enum - array - } + var result = try await DefaultValueAPI().execute( + request: """ + { + input { + bool + int + float + string + enum + array } - """, - context: NoContext(), - on: eventLoopGroup - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, .init(data: [ "input": [ "bool": true, @@ -128,23 +125,23 @@ class DefaultValueTests: XCTestCase { ) // Test input object field defaults - XCTAssertEqual( - try DefaultValueAPI().execute( - request: """ - { - input(input: {bool: true}) { - bool - int - float - string - enum - array - } + result = try await DefaultValueAPI().execute( + request: """ + { + input(input: {bool: true}) { + bool + int + float + string + enum + array } - """, - context: NoContext(), - on: eventLoopGroup - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, .init(data: [ "input": [ "bool": true, diff --git a/Tests/GraphitiTests/DirectiveTests/DirectiveTests.swift b/Tests/GraphitiTests/DirectiveTests/DirectiveTests.swift index 34db5c8f..5df7d389 100644 --- a/Tests/GraphitiTests/DirectiveTests/DirectiveTests.swift +++ b/Tests/GraphitiTests/DirectiveTests/DirectiveTests.swift @@ -1,17 +1,11 @@ @testable import Graphiti import GraphQL -import NIO import XCTest class DirectiveTests: XCTestCase { private let api = StarWarsAPI() - private var group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - deinit { - try? self.group.syncShutdownGracefully() - } - - func testSkip() throws { + func testSkip() async throws { let query = """ query FetchHeroNameWithSkip($skipName: Boolean!) { hero { @@ -25,12 +19,11 @@ class DirectiveTests: XCTestCase { "skipName": true, ] - let response = try api.execute( + let response = try await api.execute( request: query, context: StarWarsContext(), - on: group, variables: input - ).wait() + ) let expected = GraphQLResult( data: [ @@ -43,7 +36,7 @@ class DirectiveTests: XCTestCase { XCTAssertEqual(response, expected) } - func testInclude() throws { + func testInclude() async throws { let query = """ query FetchHeroNameWithSkip($includeName: Boolean!) { hero { @@ -57,12 +50,11 @@ class DirectiveTests: XCTestCase { "includeName": false, ] - let response = try api.execute( + let response = try await api.execute( request: query, context: StarWarsContext(), - on: group, variables: input - ).wait() + ) let expected = GraphQLResult( data: [ @@ -75,20 +67,20 @@ class DirectiveTests: XCTestCase { XCTAssertEqual(response, expected) } - func testOneOfAcceptsGoodValue() throws { - try XCTAssertEqual( - OneOfAPI().execute( - request: """ - query { - test(input: {a: "abc"}) { - a - b - } + func testOneOfAcceptsGoodValue() async throws { + let result = try await OneOfAPI().execute( + request: """ + query { + test(input: {a: "abc"}) { + a + b } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "test": [ @@ -100,20 +92,20 @@ class DirectiveTests: XCTestCase { ) } - func testOneOfRejectsBadValue() throws { - try XCTAssertEqual( - OneOfAPI().execute( - request: """ - query { - test(input: {a: "abc", b: 123}) { - a - b - } + func testOneOfRejectsBadValue() async throws { + let result = try await OneOfAPI().execute( + request: """ + query { + test(input: {a: "abc", b: 123}) { + a + b } - """, - context: NoContext(), - on: group - ).wait().errors[0].message, + } + """, + context: NoContext() + ) + XCTAssertEqual( + result.errors[0].message, #"OneOf Input Object "TestInputObject" must specify exactly one key."# ) } diff --git a/Tests/GraphitiTests/FederationTests/FederationOnlySchemaTests.swift b/Tests/GraphitiTests/FederationTests/FederationOnlySchemaTests.swift index 9092c8d5..8eeec7fd 100644 --- a/Tests/GraphitiTests/FederationTests/FederationOnlySchemaTests.swift +++ b/Tests/GraphitiTests/FederationTests/FederationOnlySchemaTests.swift @@ -1,11 +1,9 @@ import Foundation import Graphiti import GraphQL -import NIO import XCTest final class FederationOnlySchemaTests: XCTestCase { - private var group: MultiThreadedEventLoopGroup! private var api: FederationOnlyAPI! struct Profile: Codable { @@ -71,28 +69,22 @@ final class FederationOnlySchemaTests: XCTestCase { } } .build() - group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) api = FederationOnlyAPI(resolver: FederationOnlyResolver(), schema: schema) } override func tearDownWithError() throws { - try group.syncShutdownGracefully() - group = nil api = nil } - func execute(request: String, variables: [String: Map] = [:]) throws -> GraphQLResult { - try api - .execute( - request: request, - context: NoContext(), - on: group, - variables: variables - ) - .wait() + func execute(request: String, variables: [String: Map] = [:]) async throws -> GraphQLResult { + try await api.execute( + request: request, + context: NoContext(), + variables: variables + ) } - func testUserFederationSimple() throws { + func testUserFederationSimple() async throws { let representations: [String: Map] = [ "representations": [ ["__typename": "User", "id": "1234"], @@ -107,11 +99,12 @@ final class FederationOnlySchemaTests: XCTestCase { id } } - } + } """ - try XCTAssertEqual( - execute(request: query, variables: representations), + let result = try await execute(request: query, variables: representations) + XCTAssertEqual( + result, GraphQLResult(data: [ "_entities": [ [ @@ -122,7 +115,7 @@ final class FederationOnlySchemaTests: XCTestCase { ) } - func testUserFederationNested() throws { + func testUserFederationNested() async throws { let representations: [String: Map] = [ "representations": [ ["__typename": "User", "id": "1234"], @@ -138,11 +131,12 @@ final class FederationOnlySchemaTests: XCTestCase { profile { name, email } } } - } + } """ - try XCTAssertEqual( - execute(request: query, variables: representations), + let result = try await execute(request: query, variables: representations) + XCTAssertEqual( + result, GraphQLResult(data: [ "_entities": [ [ @@ -157,7 +151,7 @@ final class FederationOnlySchemaTests: XCTestCase { ) } - func testUserFederationNestedOptional() throws { + func testUserFederationNestedOptional() async throws { let representations: [String: Map] = [ "representations": [ ["__typename": "User", "id": "1"], @@ -173,11 +167,12 @@ final class FederationOnlySchemaTests: XCTestCase { profile { name, email } } } - } + } """ - try XCTAssertEqual( - execute(request: query, variables: representations), + let result = try await execute(request: query, variables: representations) + XCTAssertEqual( + result, GraphQLResult(data: [ "_entities": [ [ diff --git a/Tests/GraphitiTests/FederationTests/FederationTests.swift b/Tests/GraphitiTests/FederationTests/FederationTests.swift index c7cde52d..14f5620c 100644 --- a/Tests/GraphitiTests/FederationTests/FederationTests.swift +++ b/Tests/GraphitiTests/FederationTests/FederationTests.swift @@ -1,11 +1,9 @@ import Foundation import Graphiti import GraphQL -import NIO import XCTest final class FederationTests: XCTestCase { - private var group: MultiThreadedEventLoopGroup! private var api: ProductAPI! override func setUpWithError() throws { @@ -13,35 +11,37 @@ final class FederationTests: XCTestCase { .use(partials: [ProductSchema()]) .setFederatedSDL(to: loadSDL()) .build() - group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) api = try ProductAPI(resolver: ProductResolver(sdl: loadSDL()), schema: schema) } override func tearDownWithError() throws { - try group.syncShutdownGracefully() - group = nil api = nil } // Test Queries from https://github.com/apollographql/apollo-federation-subgraph-compatibility/blob/main/COMPATIBILITY.md - func testServiceQuery() throws { - try XCTAssertEqual(execute(request: query("service")), GraphQLResult(data: [ - "_service": [ - "sdl": Map(stringLiteral: loadSDL()), - ], - ])) + func testServiceQuery() async throws { + let result = try await execute(request: query("service")) + try XCTAssertEqual( + result, + GraphQLResult(data: [ + "_service": [ + "sdl": Map(stringLiteral: loadSDL()), + ], + ]) + ) } - func testEntityKey() throws { + func testEntityKey() async throws { let representations: [String: Map] = [ "representations": [ ["__typename": "User", "email": "support@apollographql.com"], ], ] - try XCTAssertEqual( - execute(request: query("entities"), variables: representations), + let result = try await execute(request: query("entities"), variables: representations) + XCTAssertEqual( + result, GraphQLResult(data: [ "_entities": [ [ @@ -56,7 +56,7 @@ final class FederationTests: XCTestCase { ) } - func testEntityMultipleKey() throws { + func testEntityMultipleKey() async throws { let representations: [String: Map] = [ "representations": [ [ @@ -67,8 +67,9 @@ final class FederationTests: XCTestCase { ], ] - try XCTAssertEqual( - execute(request: query("entities"), variables: representations), + let result = try await execute(request: query("entities"), variables: representations) + XCTAssertEqual( + result, GraphQLResult(data: [ "_entities": [ [ @@ -88,15 +89,16 @@ final class FederationTests: XCTestCase { ) } - func testEntityCompositeKey() throws { + func testEntityCompositeKey() async throws { let representations: [String: Map] = [ "representations": [ ["__typename": "ProductResearch", "study": ["caseNumber": "1234"]], ], ] - try XCTAssertEqual( - execute(request: query("entities"), variables: representations), + let result = try await execute(request: query("entities"), variables: representations) + XCTAssertEqual( + result, GraphQLResult(data: [ "_entities": [ [ @@ -111,7 +113,7 @@ final class FederationTests: XCTestCase { ) } - func testEntityMultipleKeys() throws { + func testEntityMultipleKeys() async throws { let representations: [String: Map] = [ "representations": [ ["__typename": "Product", "id": "apollo-federation"], @@ -120,8 +122,9 @@ final class FederationTests: XCTestCase { ], ] - try XCTAssertEqual( - execute(request: query("entities"), variables: representations), + let result = try await execute(request: query("entities"), variables: representations) + XCTAssertEqual( + result, GraphQLResult(data: [ "_entities": [ [ @@ -253,9 +256,7 @@ extension FederationTests { return try String(contentsOf: url) } - func execute(request: String, variables: [String: Map] = [:]) throws -> GraphQLResult { - try api - .execute(request: request, context: ProductContext(), on: group, variables: variables) - .wait() + func execute(request: String, variables: [String: Map] = [:]) async throws -> GraphQLResult { + try await api.execute(request: request, context: ProductContext(), variables: variables) } } diff --git a/Tests/GraphitiTests/HelloWorldTests/HelloWorldAsyncTests.swift b/Tests/GraphitiTests/HelloWorldTests/HelloWorldAsyncTests.swift index 186d4984..f3b855d1 100644 --- a/Tests/GraphitiTests/HelloWorldTests/HelloWorldAsyncTests.swift +++ b/Tests/GraphitiTests/HelloWorldTests/HelloWorldAsyncTests.swift @@ -1,6 +1,5 @@ @testable import Graphiti import GraphQL -import NIO import XCTest @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) @@ -17,22 +16,21 @@ extension HelloResolver { }.value } - func subscribeUser(context _: HelloContext, arguments _: NoArguments) -> EventStream { + func subscribeUser(context _: HelloContext, arguments _: NoArguments) -> AsyncThrowingStream { pubsub.subscribe() } func futureSubscribeUser( context _: HelloContext, - arguments _: NoArguments, - group: EventLoopGroup - ) -> EventLoopFuture> { - group.next().makeSucceededFuture(pubsub.subscribe()) + arguments _: NoArguments + ) -> AsyncThrowingStream { + pubsub.subscribe() } func asyncSubscribeUser( context _: HelloContext, arguments _: NoArguments - ) async -> EventStream { + ) async -> AsyncThrowingStream { return await Task { pubsub.subscribe() }.value @@ -123,15 +121,13 @@ struct HelloAsyncAPI: API { @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) class HelloWorldAsyncTests: XCTestCase { private let api = HelloAsyncAPI() - private var group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) /// Tests that async version of API.execute works as expected func testAsyncExecute() async throws { let query = "{ hello }" let result = try await api.execute( request: query, - context: api.context, - on: group + context: api.context ) XCTAssertEqual( result, @@ -144,8 +140,7 @@ class HelloWorldAsyncTests: XCTestCase { let query = "{ asyncHello }" let result = try await api.execute( request: query, - context: api.context, - on: group + context: api.context ) XCTAssertEqual( result, @@ -166,22 +161,17 @@ class HelloWorldAsyncTests: XCTestCase { let subscriptionResult = try await api.subscribe( request: request, - context: api.context, - on: group + context: api.context ) guard let subscription = subscriptionResult.stream else { XCTFail(subscriptionResult.errors.description) return } - guard let stream = subscription as? ConcurrentEventStream else { - XCTFail("stream isn't ConcurrentEventStream") - return - } - var iterator = stream.stream.makeAsyncIterator() + var iterator = subscription.makeAsyncIterator() pubsub.publish(event: User(id: "124", name: "Jerry", friends: nil)) - let result = try await iterator.next()?.get() + let result = try await iterator.next() XCTAssertEqual( result, GraphQLResult(data: [ @@ -208,22 +198,17 @@ class HelloWorldAsyncTests: XCTestCase { let subscriptionResult = try await api.subscribe( request: request, - context: api.context, - on: group + context: api.context ) guard let subscription = subscriptionResult.stream else { XCTFail(subscriptionResult.errors.description) return } - guard let stream = subscription as? ConcurrentEventStream else { - XCTFail("stream isn't ConcurrentEventStream") - return - } - var iterator = stream.stream.makeAsyncIterator() + var iterator = subscription.makeAsyncIterator() pubsub.publish(event: User(id: "124", name: "Jerry", friends: nil)) - let result = try await iterator.next()?.get() + let result = try await iterator.next() XCTAssertEqual( result, GraphQLResult(data: [ @@ -250,22 +235,17 @@ class HelloWorldAsyncTests: XCTestCase { let subscriptionResult = try await api.subscribe( request: request, - context: api.context, - on: group + context: api.context ) guard let subscription = subscriptionResult.stream else { XCTFail(subscriptionResult.errors.description) return } - guard let stream = subscription as? ConcurrentEventStream else { - XCTFail("stream isn't ConcurrentEventStream") - return - } - var iterator = stream.stream.makeAsyncIterator() + var iterator = subscription.makeAsyncIterator() pubsub.publish(event: User(id: "124", name: "Jerry", friends: nil)) - let result = try await iterator.next()?.get() + let result = try await iterator.next() XCTAssertEqual( result, GraphQLResult(data: [ @@ -290,22 +270,17 @@ class HelloWorldAsyncTests: XCTestCase { let subscriptionResult = try await api.subscribe( request: request, - context: api.context, - on: group + context: api.context ) guard let subscription = subscriptionResult.stream else { XCTFail(subscriptionResult.errors.description) return } - guard let stream = subscription as? ConcurrentEventStream else { - XCTFail("stream isn't ConcurrentEventStream") - return - } - var iterator = stream.stream.makeAsyncIterator() + var iterator = subscription.makeAsyncIterator() pubsub.publish(event: User(id: "124", name: "Jerry", friends: nil)) - let result = try await iterator.next()?.get() + let result = try await iterator.next() XCTAssertEqual( result, GraphQLResult(data: [ @@ -339,8 +314,8 @@ class SimplePubSub { } } - func subscribe() -> ConcurrentEventStream { - let asyncStream = AsyncThrowingStream { continuation in + func subscribe() -> AsyncThrowingStream { + return AsyncThrowingStream { continuation in let subscriber = Subscriber( callback: { newValue in continuation.yield(newValue) @@ -351,7 +326,6 @@ class SimplePubSub { ) subscribers.append(subscriber) } - return ConcurrentEventStream(asyncStream) } } diff --git a/Tests/GraphitiTests/HelloWorldTests/HelloWorldTests.swift b/Tests/GraphitiTests/HelloWorldTests/HelloWorldTests.swift index eb316bc0..e0c318be 100644 --- a/Tests/GraphitiTests/HelloWorldTests/HelloWorldTests.swift +++ b/Tests/GraphitiTests/HelloWorldTests/HelloWorldTests.swift @@ -1,6 +1,5 @@ @testable import Graphiti import GraphQL -import NIO import XCTest struct ID: Codable { @@ -70,10 +69,9 @@ struct HelloResolver { func futureHello( context: HelloContext, - arguments _: NoArguments, - group: EventLoopGroup - ) -> EventLoopFuture { - group.next().makeSucceededFuture(context.hello()) + arguments _: NoArguments + ) -> String { + context.hello() } struct FloatArguments: Codable { @@ -159,41 +157,36 @@ struct HelloAPI: API { class HelloWorldTests: XCTestCase { private let api = HelloAPI() - private var group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - - deinit { - try? self.group.syncShutdownGracefully() - } - func testHello() throws { + func testHello() async throws { + let result = try await api.execute( + request: "{ hello }", + context: api.context + ) XCTAssertEqual( - try api.execute( - request: "{ hello }", - context: api.context, - on: group - ).wait(), + result, GraphQLResult(data: ["hello": "world"]) ) } - func testFutureHello() throws { + func testFutureHello() async throws { + let result = try await api.execute( + request: "{ futureHello }", + context: api.context + ) XCTAssertEqual( - try api.execute( - request: "{ futureHello }", - context: api.context, - on: group - ).wait(), + result, GraphQLResult(data: ["futureHello": "world"]) ) } - func testBoyhowdy() throws { + func testBoyhowdy() async throws { + let result = try await api.execute( + request: "{ boyhowdy }", + context: api.context + ) XCTAssertEqual( - try api.execute( - request: "{ boyhowdy }", - context: api.context, - on: group - ).wait(), + result, GraphQLResult( errors: [ GraphQLError( @@ -205,66 +198,87 @@ class HelloWorldTests: XCTestCase { ) } - func testScalar() throws { + func testScalar() async throws { + var result = try await api.execute( + request: """ + query Query($float: Float!) { + float(float: $float) + } + """, + context: api.context, + variables: ["float": 4] + ) XCTAssertEqual( - try api.execute( - request: """ - query Query($float: Float!) { - float(float: $float) - } - """, - context: api.context, - on: group, - variables: ["float": 4] - ).wait(), + result, GraphQLResult(data: ["float": 4.0]) ) + result = try await api.execute( + request: """ + query Query { + float(float: 4) + } + """, + context: api.context + ) XCTAssertEqual( - try api.execute( - request: """ - query Query { - float(float: 4) - } - """, - context: api.context, - on: group - ).wait(), + result, GraphQLResult(data: ["float": 4.0]) ) + result = try await api.execute( + request: """ + query Query($id: ID!) { + id(id: $id) + } + """, + context: api.context, + variables: ["id": "85b8d502-8190-40ab-b18f-88edd297d8b6"] + ) XCTAssertEqual( - try api.execute( - request: """ - query Query($id: ID!) { - id(id: $id) - } - """, - context: api.context, - on: group, - variables: ["id": "85b8d502-8190-40ab-b18f-88edd297d8b6"] - ).wait(), + result, GraphQLResult(data: ["id": "85b8d502-8190-40ab-b18f-88edd297d8b6"]) ) + result = try await api.execute( + request: """ + query Query { + id(id: "85b8d502-8190-40ab-b18f-88edd297d8b6") + } + """, + context: api.context + ) XCTAssertEqual( - try api.execute( - request: """ - query Query { - id(id: "85b8d502-8190-40ab-b18f-88edd297d8b6") - } - """, - context: api.context, - on: group - ).wait(), + result, GraphQLResult(data: ["id": "85b8d502-8190-40ab-b18f-88edd297d8b6"]) ) } - func testInput() throws { + func testInput() async throws { + let result = try await api.execute( + request: """ + mutation addUser($user: UserInput!) { + addUser(user: $user) { + id, + name + } + } + """, + context: api.context, + variables: ["user": ["id": "123", "name": "bob"]] + ) XCTAssertEqual( - try api.execute( - request: """ + result, + GraphQLResult( + data: ["addUser": ["id": "123", "name": "bob"]] + ) + ) + } + + func testInputRequest() async throws { + let result = try await api.execute( + request: GraphQLRequest( + query: """ mutation addUser($user: UserInput!) { addUser(user: $user) { id, @@ -272,64 +286,43 @@ class HelloWorldTests: XCTestCase { } } """, - context: api.context, - on: group, variables: ["user": ["id": "123", "name": "bob"]] - ).wait(), - GraphQLResult( - data: ["addUser": ["id": "123", "name": "bob"]] - ) + ), + context: api.context ) - } - - func testInputRequest() throws { XCTAssertEqual( - try api.execute( - request: GraphQLRequest( - query: """ - mutation addUser($user: UserInput!) { - addUser(user: $user) { - id, - name - } - } - """, - variables: ["user": ["id": "123", "name": "bob"]] - ), - context: api.context, - on: group - ).wait(), + result, GraphQLResult( data: ["addUser": ["id": "123", "name": "bob"]] ) ) } - func testInputRecursive() throws { - XCTAssertEqual( - try api.execute( - request: """ - mutation addUser($user: UserInput!) { - addUser(user: $user) { + func testInputRecursive() async throws { + let result = try await api.execute( + request: """ + mutation addUser($user: UserInput!) { + addUser(user: $user) { + id, + name, + friends { id, - name, - friends { - id, - name - } + name } } - """, - context: api.context, - on: group, - variables: [ - "user": [ - "id": "123", - "name": "bob", - "friends": [["id": "124", "name": "jeff"]], - ], - ] - ).wait(), + } + """, + context: api.context, + variables: [ + "user": [ + "id": "123", + "name": "bob", + "friends": [["id": "124", "name": "jeff"]], + ], + ] + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "addUser": [ diff --git a/Tests/GraphitiTests/PartialSchemaTests.swift b/Tests/GraphitiTests/PartialSchemaTests.swift index 0a38c66e..6577e082 100644 --- a/Tests/GraphitiTests/PartialSchemaTests.swift +++ b/Tests/GraphitiTests/PartialSchemaTests.swift @@ -1,6 +1,5 @@ import Graphiti import GraphQL -import NIO import XCTest class PartialSchemaTests: XCTestCase { @@ -96,13 +95,7 @@ class PartialSchemaTests: XCTestCase { } } - func testPartialSchemaWithBuilder() throws { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - - defer { - try? group.syncShutdownGracefully() - } - + func testPartialSchemaWithBuilder() async throws { let builder = SchemaBuilder(StarWarsResolver.self, StarWarsContext.self) builder.use(partials: [BaseSchema(), SearchSchema()]) @@ -116,18 +109,18 @@ class PartialSchemaTests: XCTestCase { let api = PartialSchemaTestAPI(resolver: StarWarsResolver(), schema: schema) - XCTAssertEqual( - try api.execute( - request: """ - query { - human(id: "1000") { - name - } + let result = try await api.execute( + request: """ + query { + human(id: "1000") { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "human": [ "name": "Luke Skywalker", @@ -136,13 +129,7 @@ class PartialSchemaTests: XCTestCase { ) } - func testPartialSchema() throws { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - - defer { - try? group.syncShutdownGracefully() - } - + func testPartialSchema() async throws { /// Double check if static func works and the types are inferred properly let schema = try Schema.create(from: [BaseSchema(), SearchSchema()]) @@ -153,18 +140,18 @@ class PartialSchemaTests: XCTestCase { let api = PartialSchemaTestAPI(resolver: StarWarsResolver(), schema: schema) - XCTAssertEqual( - try api.execute( - request: """ - query { - human(id: "1000") { - name - } + let result = try await api.execute( + request: """ + query { + human(id: "1000") { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "human": [ "name": "Luke Skywalker", @@ -173,13 +160,7 @@ class PartialSchemaTests: XCTestCase { ) } - func testPartialSchemaOutOfOrder() throws { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - - defer { - try? group.syncShutdownGracefully() - } - + func testPartialSchemaOutOfOrder() async throws { /// Double check if ordering of partial schema doesn't matter let schema = try Schema.create(from: [SearchSchema(), BaseSchema()]) @@ -190,18 +171,18 @@ class PartialSchemaTests: XCTestCase { let api = PartialSchemaTestAPI(resolver: StarWarsResolver(), schema: schema) - XCTAssertEqual( - try api.execute( - request: """ - query { - human(id: "1000") { - name - } + let result = try await api.execute( + request: """ + query { + human(id: "1000") { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "human": [ "name": "Luke Skywalker", @@ -210,7 +191,7 @@ class PartialSchemaTests: XCTestCase { ) } - func testInstancePartialSchema() throws { + func testInstancePartialSchema() async throws { let baseSchema = PartialSchema( types: { Interface(Character.self) { @@ -301,12 +282,6 @@ class PartialSchemaTests: XCTestCase { } ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - - defer { - try? group.syncShutdownGracefully() - } - /// Double check if ordering of partial schema doesn't matter let schema = try Schema.create(from: [searchSchema, baseSchema]) @@ -317,18 +292,18 @@ class PartialSchemaTests: XCTestCase { let api = PartialSchemaTestAPI(resolver: StarWarsResolver(), schema: schema) - XCTAssertEqual( - try api.execute( - request: """ - query { - human(id: "1000") { - name - } + let result = try await api.execute( + request: """ + query { + human(id: "1000") { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "human": [ "name": "Luke Skywalker", diff --git a/Tests/GraphitiTests/ProductAPI/ProductResolver.swift b/Tests/GraphitiTests/ProductAPI/ProductResolver.swift index 635bdc75..4e748103 100644 --- a/Tests/GraphitiTests/ProductAPI/ProductResolver.swift +++ b/Tests/GraphitiTests/ProductAPI/ProductResolver.swift @@ -1,7 +1,6 @@ import Foundation import Graphiti import GraphQL -import NIO struct ProductResolver { var sdl: String diff --git a/Tests/GraphitiTests/ScalarTests.swift b/Tests/GraphitiTests/ScalarTests.swift index 3f0bb235..fdb2a7b1 100644 --- a/Tests/GraphitiTests/ScalarTests.swift +++ b/Tests/GraphitiTests/ScalarTests.swift @@ -1,13 +1,12 @@ import Foundation @testable import Graphiti import GraphQL -import NIO import XCTest class ScalarTests: XCTestCase { // MARK: Test UUID converts to String as expected - func testUUIDOutput() throws { + func testUUIDOutput() async throws { struct UUIDOutput { let value: UUID } @@ -32,21 +31,18 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - uuid { - value - } + let result = try await api.execute( + request: """ + query { + uuid { + value } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "uuid": [ "value": "E621E1F8-C36C-495A-93FC-0C247A3E6E5F", @@ -55,7 +51,7 @@ class ScalarTests: XCTestCase { ) } - func testUUIDArg() throws { + func testUUIDArg() async throws { struct UUIDOutput { let value: UUID } @@ -86,21 +82,18 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - uuid (value: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F") { - value - } + let result = try await api.execute( + request: """ + query { + uuid (value: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F") { + value } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "uuid": [ "value": "E621E1F8-C36C-495A-93FC-0C247A3E6E5F", @@ -109,7 +102,7 @@ class ScalarTests: XCTestCase { ) } - func testUUIDInput() throws { + func testUUIDInput() async throws { struct UUIDOutput { let value: UUID } @@ -147,21 +140,18 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - uuid (input: {value: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F"}) { - value - } + let result = try await api.execute( + request: """ + query { + uuid (input: {value: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F"}) { + value } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "uuid": [ "value": "E621E1F8-C36C-495A-93FC-0C247A3E6E5F", @@ -172,7 +162,7 @@ class ScalarTests: XCTestCase { // MARK: Test Date scalars convert to String using ISO8601 encoders - func testDateOutput() throws { + func testDateOutput() async throws { struct DateOutput { let value: Date } @@ -202,21 +192,18 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - date { - value - } + let result = try await api.execute( + request: """ + query { + date { + value } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "date": [ "value": "2001-01-01T00:00:00Z", @@ -225,7 +212,7 @@ class ScalarTests: XCTestCase { ) } - func testDateArg() throws { + func testDateArg() async throws { struct DateOutput { let value: Date } @@ -261,21 +248,18 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - date (value: "2001-01-01T00:00:00Z") { - value - } + let result = try await api.execute( + request: """ + query { + date (value: "2001-01-01T00:00:00Z") { + value } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "date": [ "value": "2001-01-01T00:00:00Z", @@ -284,7 +268,7 @@ class ScalarTests: XCTestCase { ) } - func testDateInput() throws { + func testDateInput() async throws { struct DateOutput { let value: Date } @@ -327,21 +311,18 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - date (input: {value: "2001-01-01T00:00:00Z"}) { - value - } + let result = try await api.execute( + request: """ + query { + date (input: {value: "2001-01-01T00:00:00Z"}) { + value } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "date": [ "value": "2001-01-01T00:00:00Z", @@ -352,7 +333,7 @@ class ScalarTests: XCTestCase { // MARK: Test a scalar that converts to a single-value Map (StringCodedCoordinate -> String) - func testStringCoordOutput() throws { + func testStringCoordOutput() async throws { struct CoordinateOutput { let value: StringCodedCoordinate } @@ -377,21 +358,18 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - coord { - value - } + let result = try await api.execute( + request: """ + query { + coord { + value } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "coord": [ "value": "(0.0, 0.0)", @@ -400,7 +378,7 @@ class ScalarTests: XCTestCase { ) } - func testStringCoordArg() throws { + func testStringCoordArg() async throws { struct CoordinateOutput { let value: StringCodedCoordinate } @@ -431,21 +409,18 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - coord (value: "(0.0, 0.0)") { - value - } + let result = try await api.execute( + request: """ + query { + coord (value: "(0.0, 0.0)") { + value } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "coord": [ "value": "(0.0, 0.0)", @@ -454,7 +429,7 @@ class ScalarTests: XCTestCase { ) } - func testStringCoordInput() throws { + func testStringCoordInput() async throws { struct CoordinateOutput { let value: StringCodedCoordinate } @@ -492,21 +467,18 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - coord (input: {value: "(0.0, 0.0)"}) { - value - } + let result = try await api.execute( + request: """ + query { + coord (input: {value: "(0.0, 0.0)"}) { + value } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "coord": [ "value": "(0.0, 0.0)", @@ -517,7 +489,7 @@ class ScalarTests: XCTestCase { // MARK: Test a scalar that converts to a multi-value Map (Coordinate -> Dict) - func testDictCoordOutput() throws { + func testDictCoordOutput() async throws { struct CoordinateOutput { let value: DictCodedCoordinate } @@ -542,11 +514,8 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - // Test individual fields because we can't be confident we'll match the ordering of Map's OrderedDictionary - let result = try api.execute( + let result = try await api.execute( request: """ query { coord { @@ -554,9 +523,9 @@ class ScalarTests: XCTestCase { } } """, - context: NoContext(), - on: group - ).wait() + context: NoContext() + ) + let value = result.data?.dictionary?["coord"]?.dictionary?["value"]?.dictionary XCTAssertEqual( @@ -569,7 +538,7 @@ class ScalarTests: XCTestCase { ) } - func testDictCoordArg() throws { + func testDictCoordArg() async throws { struct CoordinateOutput { let value: DictCodedCoordinate } @@ -600,11 +569,8 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - // Test individual fields because we can't be confident we'll match the ordering of Map's OrderedDictionary - let result = try api.execute( + let result = try await api.execute( request: """ query { coord (value: {latitude: 0.0, longitude: 0.0}) { @@ -612,9 +578,9 @@ class ScalarTests: XCTestCase { } } """, - context: NoContext(), - on: group - ).wait() + context: NoContext() + ) + let value = result.data?.dictionary?["coord"]?.dictionary?["value"]?.dictionary XCTAssertEqual( @@ -627,7 +593,7 @@ class ScalarTests: XCTestCase { ) } - func testDictCoordInput() throws { + func testDictCoordInput() async throws { struct CoordinateOutput { let value: DictCodedCoordinate } @@ -665,11 +631,8 @@ class ScalarTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - // Test individual fields because we can't be confident we'll match the ordering of Map's OrderedDictionary - let result = try api.execute( + let result = try await api.execute( request: """ query { coord (input: {value: {latitude: 0.0, longitude: 0.0}}) { @@ -677,9 +640,9 @@ class ScalarTests: XCTestCase { } } """, - context: NoContext(), - on: group - ).wait() + context: NoContext() + ) + let value = result.data?.dictionary?["coord"]?.dictionary?["value"]?.dictionary XCTAssertEqual( diff --git a/Tests/GraphitiTests/SchemaBuilderTests.swift b/Tests/GraphitiTests/SchemaBuilderTests.swift index e531e57d..fb98635e 100644 --- a/Tests/GraphitiTests/SchemaBuilderTests.swift +++ b/Tests/GraphitiTests/SchemaBuilderTests.swift @@ -1,12 +1,9 @@ import Graphiti import GraphQL -import NIO import XCTest class SchemaBuilderTests: XCTestCase { - func testSchemaBuilder() throws { - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - + func testSchemaBuilder() async throws { let builder = SchemaBuilder(StarWarsResolver.self, StarWarsContext.self) // Add assets slightly out of order @@ -104,18 +101,18 @@ class SchemaBuilderTests: XCTestCase { let api = SchemaBuilderTestAPI(resolver: StarWarsResolver(), schema: schema) - XCTAssertEqual( - try api.execute( - request: """ - query { - human(id: "1000") { - name - } + let result = try await api.execute( + request: """ + query { + human(id: "1000") { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "human": [ "name": "Luke Skywalker", diff --git a/Tests/GraphitiTests/SchemaTests.swift b/Tests/GraphitiTests/SchemaTests.swift index b4db8ce8..f481999f 100644 --- a/Tests/GraphitiTests/SchemaTests.swift +++ b/Tests/GraphitiTests/SchemaTests.swift @@ -1,12 +1,11 @@ import Foundation @testable import Graphiti import GraphQL -import NIO import XCTest class SchemaTests: XCTestCase { // Tests that circularly dependent objects can be used in schema and resolved correctly - func testCircularDependencies() throws { + func testCircularDependencies() async throws { struct A: Codable { let name: String var b: B { @@ -45,23 +44,20 @@ class SchemaTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - a { - b { - name - } - } + let result = try await api.execute( + request: """ + query { + a { + b { + name + } } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "a": [ "b": [ @@ -73,7 +69,7 @@ class SchemaTests: XCTestCase { } // Tests that we can resolve type references for named types - func testTypeReferenceForNamedType() throws { + func testTypeReferenceForNamedType() async throws { struct LocationObject: Codable { let id: String let name: String @@ -114,23 +110,20 @@ class SchemaTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - user { - location { - name - } - } + let result = try await api.execute( + request: """ + query { + user { + location { + name + } } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: [ "user": [ "location": [ diff --git a/Tests/GraphitiTests/StarWarsTests/StarWarsIntrospectionTests.swift b/Tests/GraphitiTests/StarWarsTests/StarWarsIntrospectionTests.swift index 4cdf31fa..6dd85dce 100644 --- a/Tests/GraphitiTests/StarWarsTests/StarWarsIntrospectionTests.swift +++ b/Tests/GraphitiTests/StarWarsTests/StarWarsIntrospectionTests.swift @@ -1,32 +1,26 @@ import GraphQL -import NIO import XCTest @testable import Graphiti class StarWarsIntrospectionTests: XCTestCase { private let api = StarWarsAPI() - private let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - deinit { - try? group.syncShutdownGracefully() - } - - func testIntrospectionTypeQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query IntrospectionTypeQuery { - __schema { - types { - name - } + func testIntrospectionTypeQuery() async throws { + let result = try await api.execute( + request: """ + query IntrospectionTypeQuery { + __schema { + types { + name } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "__schema": [ @@ -92,21 +86,21 @@ class StarWarsIntrospectionTests: XCTestCase { ) } - func testIntrospectionQueryTypeQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query IntrospectionQueryTypeQuery { - __schema { - queryType { - name - } + func testIntrospectionQueryTypeQuery() async throws { + let result = try await api.execute( + request: """ + query IntrospectionQueryTypeQuery { + __schema { + queryType { + name } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "__schema": [ @@ -119,19 +113,19 @@ class StarWarsIntrospectionTests: XCTestCase { ) } - func testIntrospectionDroidTypeQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query IntrospectionDroidTypeQuery { - __type(name: \"Droid\") { - name - } + func testIntrospectionDroidTypeQuery() async throws { + let result = try await api.execute( + request: """ + query IntrospectionDroidTypeQuery { + __type(name: \"Droid\") { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "__type": [ @@ -142,20 +136,20 @@ class StarWarsIntrospectionTests: XCTestCase { ) } - func testIntrospectionDroidKindQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query IntrospectionDroidKindQuery { - __type(name: \"Droid\") { - name - kind - } + func testIntrospectionDroidKindQuery() async throws { + let result = try await api.execute( + request: """ + query IntrospectionDroidKindQuery { + __type(name: \"Droid\") { + name + kind } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "__type": [ @@ -167,20 +161,20 @@ class StarWarsIntrospectionTests: XCTestCase { ) } - func testIntrospectionCharacterKindQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query IntrospectionCharacterKindQuery { - __type(name: \"Character\") { - name - kind - } + func testIntrospectionCharacterKindQuery() async throws { + let result = try await api.execute( + request: """ + query IntrospectionCharacterKindQuery { + __type(name: \"Character\") { + name + kind } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "__type": [ @@ -192,26 +186,26 @@ class StarWarsIntrospectionTests: XCTestCase { ) } - func testIntrospectionDroidFieldsQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query IntrospectionDroidFieldsQuery { - __type(name: \"Droid\") { + func testIntrospectionDroidFieldsQuery() async throws { + let result = try await api.execute( + request: """ + query IntrospectionDroidFieldsQuery { + __type(name: \"Droid\") { + name + fields { name - fields { + type { name - type { - name - kind - } + kind } } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "__type": [ @@ -266,30 +260,30 @@ class StarWarsIntrospectionTests: XCTestCase { ) } - func testIntrospectionDroidNestedFieldsQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query IntrospectionDroidNestedFieldsQuery { - __type(name: \"Droid\") { + func testIntrospectionDroidNestedFieldsQuery() async throws { + let result = try await api.execute( + request: """ + query IntrospectionDroidNestedFieldsQuery { + __type(name: \"Droid\") { + name + fields { name - fields { + type { name - type { + kind + ofType { name kind - ofType { - name - kind - } } } } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "__type": [ @@ -365,36 +359,36 @@ class StarWarsIntrospectionTests: XCTestCase { ) } - func testIntrospectionFieldArgsQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query IntrospectionFieldArgsQuery { - __schema { - queryType { - fields { + func testIntrospectionFieldArgsQuery() async throws { + let result = try await api.execute( + request: """ + query IntrospectionFieldArgsQuery { + __schema { + queryType { + fields { + name + args { name - args { + description + type { name - description - type { + kind + ofType { name kind - ofType { - name - kind - } } - defaultValue - } - } + } + defaultValue + } } } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "__schema": [ @@ -477,20 +471,20 @@ class StarWarsIntrospectionTests: XCTestCase { ) } - func testIntrospectionDroidDescriptionQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query IntrospectionDroidDescriptionQuery { - __type(name: \"Droid\") { - name - description - } + func testIntrospectionDroidDescriptionQuery() async throws { + let result = try await api.execute( + request: """ + query IntrospectionDroidDescriptionQuery { + __type(name: \"Droid\") { + name + description } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "__type": [ diff --git a/Tests/GraphitiTests/StarWarsTests/StarWarsQueryTests.swift b/Tests/GraphitiTests/StarWarsTests/StarWarsQueryTests.swift index 33b68750..bb667a57 100644 --- a/Tests/GraphitiTests/StarWarsTests/StarWarsQueryTests.swift +++ b/Tests/GraphitiTests/StarWarsTests/StarWarsQueryTests.swift @@ -1,50 +1,44 @@ @testable import Graphiti import GraphQL -import NIO import XCTest class StarWarsQueryTests: XCTestCase { private let api = StarWarsAPI() - private var group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - deinit { - try? self.group.syncShutdownGracefully() - } - - func testHeroNameQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query HeroNameQuery { - hero { - name - } + func testHeroNameQuery() async throws { + let result = try await api.execute( + request: """ + query HeroNameQuery { + hero { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult(data: ["hero": ["name": "R2-D2"]]) ) } - func testHeroNameAndFriendsQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query HeroNameAndFriendsQuery { - hero { - id + func testHeroNameAndFriendsQuery() async throws { + let result = try await api.execute( + request: """ + query HeroNameAndFriendsQuery { + hero { + id + name + friends { name - friends { - name - } } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "hero": [ @@ -61,26 +55,26 @@ class StarWarsQueryTests: XCTestCase { ) } - func testNestedQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query NestedQuery { - hero { + func testNestedQuery() async throws { + let result = try await api.execute( + request: """ + query NestedQuery { + hero { + name + friends { name + appearsIn friends { name - appearsIn - friends { - name - } } } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "hero": [ @@ -122,19 +116,19 @@ class StarWarsQueryTests: XCTestCase { ) } - func testFetchLukeQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query FetchLukeQuery { - human(id: "1000") { - name - } + func testFetchLukeQuery() async throws { + let result = try await api.execute( + request: """ + query FetchLukeQuery { + human(id: "1000") { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "human": [ @@ -145,20 +139,20 @@ class StarWarsQueryTests: XCTestCase { ) } - func testFetchSomeIDQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query FetchSomeIDQuery($someId: String!) { - human(id: $someId) { - name - } + func testFetchSomeIDQuery() async throws { + var result = try await api.execute( + request: """ + query FetchSomeIDQuery($someId: String!) { + human(id: $someId) { + name } - """, - context: StarWarsContext(), - on: group, - variables: ["someId": "1000"] - ).wait(), + } + """, + context: StarWarsContext(), + variables: ["someId": "1000"] + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "human": [ @@ -168,19 +162,19 @@ class StarWarsQueryTests: XCTestCase { ) ) - XCTAssertEqual( - try api.execute( - request: """ - query FetchSomeIDQuery($someId: String!) { - human(id: $someId) { - name - } + result = try await api.execute( + request: """ + query FetchSomeIDQuery($someId: String!) { + human(id: $someId) { + name } - """, - context: StarWarsContext(), - on: group, - variables: ["someId": "1002"] - ).wait(), + } + """, + context: StarWarsContext(), + variables: ["someId": "1002"] + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "human": [ @@ -190,19 +184,19 @@ class StarWarsQueryTests: XCTestCase { ) ) - XCTAssertEqual( - try api.execute( - request: """ - query FetchSomeIDQuery($someId: String!) { - human(id: $someId) { - name - } + result = try await api.execute( + request: """ + query FetchSomeIDQuery($someId: String!) { + human(id: $someId) { + name } - """, - context: StarWarsContext(), - on: group, - variables: ["someId": "not a valid id"] - ).wait(), + } + """, + context: StarWarsContext(), + variables: ["someId": "not a valid id"] + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "human": nil, @@ -211,19 +205,19 @@ class StarWarsQueryTests: XCTestCase { ) } - func testFetchLukeAliasedQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query FetchLukeAliasedQuery { - luke: human(id: "1000") { - name - } + func testFetchLukeAliasedQuery() async throws { + let result = try await api.execute( + request: """ + query FetchLukeAliasedQuery { + luke: human(id: "1000") { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "luke": [ @@ -234,22 +228,22 @@ class StarWarsQueryTests: XCTestCase { ) } - func testFetchLukeAndLeiaAliasedQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query FetchLukeAndLeiaAliasedQuery { - luke: human(id: "1000") { - name - } - leia: human(id: "1003") { - name - } + func testFetchLukeAndLeiaAliasedQuery() async throws { + let result = try await api.execute( + request: """ + query FetchLukeAndLeiaAliasedQuery { + luke: human(id: "1000") { + name + } + leia: human(id: "1003") { + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "luke": [ @@ -263,24 +257,24 @@ class StarWarsQueryTests: XCTestCase { ) } - func testDuplicateFieldsQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query DuplicateFieldsQuery { - luke: human(id: "1000") { - name - homePlanet { name } - } - leia: human(id: "1003") { - name - homePlanet { name } - } + func testDuplicateFieldsQuery() async throws { + let result = try await api.execute( + request: """ + query DuplicateFieldsQuery { + luke: human(id: "1000") { + name + homePlanet { name } + } + leia: human(id: "1003") { + name + homePlanet { name } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "luke": [ @@ -296,26 +290,26 @@ class StarWarsQueryTests: XCTestCase { ) } - func testUseFragmentQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query UseFragmentQuery { - luke: human(id: "1000") { - ...HumanFragment - } - leia: human(id: "1003") { - ...HumanFragment - } + func testUseFragmentQuery() async throws { + let result = try await api.execute( + request: """ + query UseFragmentQuery { + luke: human(id: "1000") { + ...HumanFragment } - fragment HumanFragment on Human { - name - homePlanet { name } + leia: human(id: "1003") { + ...HumanFragment } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + fragment HumanFragment on Human { + name + homePlanet { name } + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "luke": [ @@ -331,20 +325,20 @@ class StarWarsQueryTests: XCTestCase { ) } - func testCheckTypeOfR2Query() throws { - XCTAssertEqual( - try api.execute( - request: """ - query CheckTypeOfR2Query { - hero { - __typename - name - } + func testCheckTypeOfR2Query() async throws { + let result = try await api.execute( + request: """ + query CheckTypeOfR2Query { + hero { + __typename + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "hero": [ @@ -356,20 +350,20 @@ class StarWarsQueryTests: XCTestCase { ) } - func testCheckTypeOfLukeQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query CheckTypeOfLukeQuery { - hero(episode: EMPIRE) { - __typename - name - } + func testCheckTypeOfLukeQuery() async throws { + let result = try await api.execute( + request: """ + query CheckTypeOfLukeQuery { + hero(episode: EMPIRE) { + __typename + name } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "hero": [ @@ -381,20 +375,20 @@ class StarWarsQueryTests: XCTestCase { ) } - func testSecretBackstoryQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query SecretBackstoryQuery { - hero { - name - secretBackstory - } + func testSecretBackstoryQuery() async throws { + let result = try await api.execute( + request: """ + query SecretBackstoryQuery { + hero { + name + secretBackstory } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "hero": [ @@ -413,23 +407,23 @@ class StarWarsQueryTests: XCTestCase { ) } - func testSecretBackstoryListQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query SecretBackstoryListQuery { - hero { + func testSecretBackstoryListQuery() async throws { + let result = try await api.execute( + request: """ + query SecretBackstoryListQuery { + hero { + name + friends { name - friends { - name - secretBackstory - } + secretBackstory } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "hero": [ @@ -471,20 +465,20 @@ class StarWarsQueryTests: XCTestCase { ) } - func testSecretBackstoryAliasQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query SecretBackstoryAliasQuery { - mainHero: hero { - name - story: secretBackstory - } + func testSecretBackstoryAliasQuery() async throws { + let result = try await api.execute( + request: """ + query SecretBackstoryAliasQuery { + mainHero: hero { + name + story: secretBackstory } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "mainHero": [ @@ -503,7 +497,7 @@ class StarWarsQueryTests: XCTestCase { ) } - func testNonNullableFieldsQuery() throws { + func testNonNullableFieldsQuery() async throws { struct A { func nullableA(context _: NoContext, arguments _: NoArguments) -> A? { return A() @@ -545,24 +539,24 @@ class StarWarsQueryTests: XCTestCase { } let api = MyAPI() - XCTAssertEqual( - try api.execute( - request: """ - query { + let result = try await api.execute( + request: """ + query { + nullableA { nullableA { - nullableA { + nonNullA { nonNullA { - nonNullA { - throws - } + throws } } } } - """, - context: NoContext(), - on: group - ).wait(), + } + """, + context: NoContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "nullableA": [ @@ -580,29 +574,29 @@ class StarWarsQueryTests: XCTestCase { ) } - func testSearchQuery() throws { - XCTAssertEqual( - try api.execute( - request: """ - query { - search(query: "o") { - ... on Planet { - name - diameter - } - ... on Human { - name - } - ... on Droid { - name - primaryFunction - } + func testSearchQuery() async throws { + let result = try await api.execute( + request: """ + query { + search(query: "o") { + ... on Planet { + name + diameter + } + ... on Human { + name + } + ... on Droid { + name + primaryFunction } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "search": [ @@ -616,23 +610,23 @@ class StarWarsQueryTests: XCTestCase { ) } - func testDirective() throws { - XCTAssertEqual( - try api.execute( - request: """ - query Hero { - hero { - name + func testDirective() async throws { + var result = try await api.execute( + request: """ + query Hero { + hero { + name - friends @include(if: false) { - name - } + friends @include(if: false) { + name } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "hero": [ @@ -642,22 +636,22 @@ class StarWarsQueryTests: XCTestCase { ) ) - XCTAssertEqual( - try api.execute( - request: """ - query Hero { - hero { - name + result = try await api.execute( + request: """ + query Hero { + hero { + name - friends @include(if: true) { - name - } + friends @include(if: true) { + name } } - """, - context: StarWarsContext(), - on: group - ).wait(), + } + """, + context: StarWarsContext() + ) + XCTAssertEqual( + result, GraphQLResult( data: [ "hero": [ diff --git a/Tests/GraphitiTests/UnionTests.swift b/Tests/GraphitiTests/UnionTests.swift index 33209c20..4a832515 100644 --- a/Tests/GraphitiTests/UnionTests.swift +++ b/Tests/GraphitiTests/UnionTests.swift @@ -1,7 +1,6 @@ import Foundation @testable import Graphiti import GraphQL -import NIO import XCTest class UnionTests: XCTestCase { diff --git a/Tests/GraphitiTests/ValidationRulesTests.swift b/Tests/GraphitiTests/ValidationRulesTests.swift index 8648ad0c..497288fd 100644 --- a/Tests/GraphitiTests/ValidationRulesTests.swift +++ b/Tests/GraphitiTests/ValidationRulesTests.swift @@ -1,12 +1,11 @@ import Foundation @testable import Graphiti import GraphQL -import NIO import XCTest class ValidationRulesTests: XCTestCase { // Test registering custom validation rules - func testRegisteringCustomValidationRule() throws { + func testRegisteringCustomValidationRule() async throws { struct TestResolver { var helloWorld: String { "Hellow World" } } @@ -21,23 +20,20 @@ class ValidationRulesTests: XCTestCase { schema: testSchema ) - let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - defer { try? group.syncShutdownGracefully() } - - XCTAssertEqual( - try api.execute( - request: """ - query { - __type(name: "Query") { - name - description - } + let result = try await api.execute( + request: """ + query { + __type(name: "Query") { + name + description } - """, - context: NoContext(), - on: group, - validationRules: [NoIntrospectionRule] - ).wait(), + } + """, + context: NoContext(), + validationRules: [NoIntrospectionRule] + ) + XCTAssertEqual( + result, GraphQLResult(errors: [ .init( message: "GraphQL introspection is not allowed, but the query contained __schema or __type", diff --git a/UsageGuide.md b/UsageGuide.md index be295f25..0eb08ac9 100644 --- a/UsageGuide.md +++ b/UsageGuide.md @@ -9,9 +9,6 @@ Here is an example of a basic `"Hello world"` GraphQL schema: ```swift import Graphiti -import NIO - -let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) struct HelloResolver { func hello(context: NoContext, arguments: NoArguments) -> String { @@ -35,8 +32,7 @@ This schema can be queried in Swift using the `execute` function. : ```swift let result = try await HelloAPI().execute( request: "{ hello }", - context: NoContext(), - on: eventLoopGroup + context: NoContext() ) print(result) ``` @@ -53,9 +49,6 @@ Graphiti includes support for using Swift types in the schema itself. To connect ```swift import Graphiti -import NIO - -let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) struct Person: Codable { let name: String @@ -98,8 +91,7 @@ let result = try await PointBreakAPI().execute( } } """, - context: NoContext(), - on: eventLoopGroup + context: NoContext() ) ``` @@ -382,8 +374,7 @@ This schema can be subscribed to in Swift using the `subscribe` function. The ex let api = PointBreakAPI() let stream = try await api.subscribe( request: "subscription { fiftyYearStormAlert }", - context: NoContext(), - on: eventLoopGroup + context: NoContext() ).stream! let resultStream = stream.map { result in try print(result.wait()) @@ -504,7 +495,7 @@ The result of this query is a `GraphQLResult` that encodes to the following JSON Federation allows you split your GraphQL API into smaller services and link them back together so clients see a single larger API. More information can be found [here](https://www.apollographql.com/docs/federation). To enable federation you must: 1. Define `Keys` on the entity types, which specify the primary key fields and the resolver function used to load an entity from that key. -2. Provide the schema SDL to the schema itself. +2. Provide the schema SDL to the schema itself. Here's an example for the following schema: @@ -532,7 +523,6 @@ extend type User @key(fields: "email") { ```swift import Foundation import Graphiti -import NIO struct Product: Codable { let id: String @@ -555,7 +545,7 @@ struct ProductResolver { struct UserArguments: Codable { let email: String } - + func user(context: ProductContext, arguments: UserArguments) -> User? { context.getUser(email: arguments.email) } @@ -569,7 +559,7 @@ final class ProductSchema: PartialSchema { Field("sku", at: \.sku) Field("createdBy", at: \.createdBy) } - + Type( User.self, keys: { @@ -597,7 +587,6 @@ let schema = try SchemaBuilder(ProductResolver.self, ProductContext.self) .build() let api = ProductAPI(resolver: ProductResolver(), schema: schema) -let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) api.execute( request: """ @@ -610,7 +599,6 @@ api.execute( } } """, - context: ProductContext(), - on: group + context: ProductContext() ) ```