From 8d18f67a6dc0e68baeb2b64aa6116dd13a4f94d7 Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Mon, 21 Oct 2024 20:35:52 -0600 Subject: [PATCH 01/12] chore: Updates to GraphQL 3.0 --- Package.resolved | 96 ++++++++++++++++++++++++------------------------ Package.swift | 2 +- 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/Package.resolved b/Package.resolved index 253fc02..d84a2e5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,52 +1,50 @@ { - "object": { - "pins": [ - { - "package": "GraphQL", - "repositoryURL": "https://github.com/GraphQLSwift/GraphQL.git", - "state": { - "branch": null, - "revision": "ec809df8cce95d6aea820f70f04067abc08080f2", - "version": "2.10.3" - } - }, - { - "package": "swift-atomics", - "repositoryURL": "https://github.com/apple/swift-atomics.git", - "state": { - "branch": null, - "revision": "cd142fd2f64be2100422d658e7411e39489da985", - "version": "1.2.0" - } - }, - { - "package": "swift-collections", - "repositoryURL": "https://github.com/apple/swift-collections", - "state": { - "branch": null, - "revision": "671108c96644956dddcd89dd59c203dcdb36cec7", - "version": "1.1.4" - } - }, - { - "package": "swift-nio", - "repositoryURL": "https://github.com/apple/swift-nio.git", - "state": { - "branch": null, - "revision": "914081701062b11e3bb9e21accc379822621995e", - "version": "2.76.1" - } - }, - { - "package": "swift-system", - "repositoryURL": "https://github.com/apple/swift-system.git", - "state": { - "branch": null, - "revision": "c8a44d836fe7913603e246acab7c528c2e780168", - "version": "1.4.0" - } + "pins" : [ + { + "identity" : "graphql", + "kind" : "remoteSourceControl", + "location" : "https://github.com/GraphQLSwift/GraphQL.git", + "state" : { + "revision" : "5e098b3b1789169dded5116c171f716d584225ea", + "version" : "3.0.0" } - ] - }, - "version": 1 + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "cd142fd2f64be2100422d658e7411e39489da985", + "version" : "1.2.0" + } + }, + { + "identity" : "swift-collections", + "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" + } + } + ], + "version" : 2 } diff --git a/Package.swift b/Package.swift index 08fb00e..26346f1 100644 --- a/Package.swift +++ b/Package.swift @@ -7,7 +7,7 @@ let package = Package( .library(name: "Graphiti", targets: ["Graphiti"]), ], dependencies: [ - .package(url: "https://github.com/GraphQLSwift/GraphQL.git", from: "2.10.0"), + .package(url: "https://github.com/GraphQLSwift/GraphQL.git", from: "3.0.0"), ], targets: [ .target(name: "Graphiti", dependencies: ["GraphQL"]), From afd1160ee887b1347e99b335eae54d35473fdeaa Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Mon, 21 Oct 2024 19:22:30 -0600 Subject: [PATCH 02/12] feat: Converts to closure API --- Sources/Graphiti/Input/Input.swift | 4 +- Sources/Graphiti/Interface/Interface.swift | 4 +- Sources/Graphiti/Mutation/Mutation.swift | 4 +- Sources/Graphiti/Query/Query.swift | 51 ++++++++++--------- .../Graphiti/Subscription/Subscription.swift | 4 +- Sources/Graphiti/Type/Type.swift | 31 ++++++----- Sources/Graphiti/Union/Union.swift | 6 ++- 7 files changed, 59 insertions(+), 45 deletions(-) diff --git a/Sources/Graphiti/Input/Input.swift b/Sources/Graphiti/Input/Input.swift index 8930210..28eefc9 100644 --- a/Sources/Graphiti/Input/Input.swift +++ b/Sources/Graphiti/Input/Input.swift @@ -15,7 +15,9 @@ public final class Input< let inputObjectType = try GraphQLInputObjectType( name: name, description: description, - fields: fields(typeProvider: typeProvider), + fields: { + try self.fields(typeProvider: typeProvider) + }, isOneOf: isOneOf ) diff --git a/Sources/Graphiti/Interface/Interface.swift b/Sources/Graphiti/Interface/Interface.swift index d6601d0..aad7e97 100644 --- a/Sources/Graphiti/Interface/Interface.swift +++ b/Sources/Graphiti/Interface/Interface.swift @@ -10,7 +10,9 @@ public final class Interface: TypeComponent< let interfaceType = try GraphQLInterfaceType( name: name, description: description, - fields: fields(typeProvider: typeProvider, coders: coders), + fields: { + try self.fields(typeProvider: typeProvider, coders: coders) + }, resolveType: nil ) diff --git a/Sources/Graphiti/Mutation/Mutation.swift b/Sources/Graphiti/Mutation/Mutation.swift index 42890e8..51f8773 100644 --- a/Sources/Graphiti/Mutation/Mutation.swift +++ b/Sources/Graphiti/Mutation/Mutation.swift @@ -11,7 +11,9 @@ public final class Mutation: Component { typeProvider.mutation = try GraphQLObjectType( name: name, description: description, - fields: fields(typeProvider: typeProvider, coders: coders), + fields: { + try self.fields(typeProvider: typeProvider, coders: coders) + }, isTypeOf: isTypeOf ) } diff --git a/Sources/Graphiti/Query/Query.swift b/Sources/Graphiti/Query/Query.swift index 727fc94..e9f3237 100644 --- a/Sources/Graphiti/Query/Query.swift +++ b/Sources/Graphiti/Query/Query.swift @@ -8,34 +8,35 @@ public final class Query: Component { } override func update(typeProvider: SchemaTypeProvider, coders: Coders) throws { - var queryFields = try fields(typeProvider: typeProvider, coders: coders) - - // Add federated types and queries if they exist - if !typeProvider.federatedTypes.isEmpty { - let federatedTypes = typeProvider.federatedTypes - guard let sdl = typeProvider.federatedSDL else { - throw GraphQLError(message: "If federated types are included, SDL must be provided") - } - - // Add subgraph types to provider (_Service, _Any, _Entity) - let entity = entityType(federatedTypes) - typeProvider.types.append(serviceType) - typeProvider.types.append(anyType) - typeProvider.types.append(entity) - - // Add subgraph queries (_entities, _service) - queryFields["_entities"] = entitiesQuery( - for: typeProvider.federatedResolvers, - entityType: entity, - coders: coders - ) - queryFields["_service"] = serviceQuery(for: sdl) - } - typeProvider.query = try GraphQLObjectType( name: name, description: description, - fields: queryFields, + fields: { + var queryFields = try self.fields(typeProvider: typeProvider, coders: coders) + + // Add federated types and queries if they exist + if !typeProvider.federatedTypes.isEmpty { + let federatedTypes = typeProvider.federatedTypes + guard let sdl = typeProvider.federatedSDL else { + throw GraphQLError(message: "If federated types are included, SDL must be provided") + } + + // Add subgraph types to provider (_Service, _Any, _Entity) + let entity = entityType(federatedTypes) + typeProvider.types.append(serviceType) + typeProvider.types.append(anyType) + typeProvider.types.append(entity) + + // Add subgraph queries (_entities, _service) + queryFields["_entities"] = entitiesQuery( + for: typeProvider.federatedResolvers, + entityType: entity, + coders: coders + ) + queryFields["_service"] = serviceQuery(for: sdl) + } + return queryFields + }, isTypeOf: isTypeOf ) } diff --git a/Sources/Graphiti/Subscription/Subscription.swift b/Sources/Graphiti/Subscription/Subscription.swift index 2a9a0bf..e775205 100644 --- a/Sources/Graphiti/Subscription/Subscription.swift +++ b/Sources/Graphiti/Subscription/Subscription.swift @@ -11,7 +11,9 @@ public final class Subscription: Component typeProvider.subscription = try GraphQLObjectType( name: name, description: description, - fields: fields(typeProvider: typeProvider, coders: coders), + fields: { + try self.fields(typeProvider: typeProvider, coders: coders) + }, isTypeOf: isTypeOf ) } diff --git a/Sources/Graphiti/Type/Type.swift b/Sources/Graphiti/Type/Type.swift index 4f9157d..a8df744 100644 --- a/Sources/Graphiti/Type/Type.swift +++ b/Sources/Graphiti/Type/Type.swift @@ -13,30 +13,33 @@ public final class Type: TypeComponent< } override func update(typeProvider: SchemaTypeProvider, coders: Coders) throws { - let fieldDefs = try fields(typeProvider: typeProvider, coders: coders) let objectType = try GraphQLObjectType( name: name, description: description, - fields: fieldDefs, - interfaces: interfaces.map { - try typeProvider.getInterfaceType(from: $0) + fields: { + let fields = try self.fields(typeProvider: typeProvider, coders: coders) + // Validate federation keys, if present + for key in self.keys { + try key.validate( + againstFields: Array(fields.keys), + typeProvider: typeProvider, + coders: coders + ) + } + return fields + }, + interfaces: { + try self.interfaces.map { + try typeProvider.getInterfaceType(from: $0) + } }, isTypeOf: isTypeOf ) try typeProvider.add(type: ObjectType.self, as: objectType) - // If federation keys are included, validate and create resolver closure + // If federation keys are included, create resolver closure if !keys.isEmpty { - let fieldNames = Array(fieldDefs.keys) - for key in keys { - try key.validate( - againstFields: fieldNames, - typeProvider: typeProvider, - coders: coders - ) - } - let resolve: GraphQLFieldResolve = { source, args, context, eventLoopGroup, _ in guard let s = source as? Resolver else { throw GraphQLError( diff --git a/Sources/Graphiti/Union/Union.swift b/Sources/Graphiti/Union/Union.swift index 6d8d0c3..d4babf5 100644 --- a/Sources/Graphiti/Union/Union.swift +++ b/Sources/Graphiti/Union/Union.swift @@ -8,8 +8,10 @@ public final class Union: TypeComponent Date: Mon, 21 Oct 2024 20:23:57 -0600 Subject: [PATCH 03/12] test: Removes TypeReferences --- Tests/GraphitiTests/PartialSchemaTests.swift | 4 ++-- Tests/GraphitiTests/SchemaBuilderTests.swift | 2 +- Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/GraphitiTests/PartialSchemaTests.swift b/Tests/GraphitiTests/PartialSchemaTests.swift index 8004314..737c4de 100644 --- a/Tests/GraphitiTests/PartialSchemaTests.swift +++ b/Tests/GraphitiTests/PartialSchemaTests.swift @@ -12,7 +12,7 @@ class PartialSchemaTests: XCTestCase { .description("The id of the character.") Field("name", at: \.name) .description("The name of the character.") - Field("friends", at: \.friends, as: [TypeReference].self) + Field("friends", at: \.friends) .description( "The friends of the character, or an empty list if they have none." ) @@ -218,7 +218,7 @@ class PartialSchemaTests: XCTestCase { .description("The id of the character.") Field("name", at: \.name) .description("The name of the character.") - Field("friends", at: \.friends, as: [TypeReference].self) + Field("friends", at: \.friends) .description( "The friends of the character, or an empty list if they have none." ) diff --git a/Tests/GraphitiTests/SchemaBuilderTests.swift b/Tests/GraphitiTests/SchemaBuilderTests.swift index 3510682..043f93b 100644 --- a/Tests/GraphitiTests/SchemaBuilderTests.swift +++ b/Tests/GraphitiTests/SchemaBuilderTests.swift @@ -70,7 +70,7 @@ class SchemaBuilderTests: XCTestCase { .description("The id of the character.") Field("name", at: \.name) .description("The name of the character.") - Field("friends", at: \.friends, as: [TypeReference].self) + Field("friends", at: \.friends) .description( "The friends of the character, or an empty list if they have none." ) diff --git a/Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift b/Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift index 4b11077..26e4d00 100644 --- a/Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift +++ b/Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift @@ -23,7 +23,7 @@ public struct StarWarsAPI: API { Field("name", at: \.name) .description("The name of the character.") - Field("friends", at: \.friends, as: [TypeReference].self) + Field("friends", at: Character.getFriends) .description("The friends of the character, or an empty list if they have none.") Field("appearsIn", at: \.appearsIn) From 4351e1ecf512e667962bc1f3a1b93009ef80146b Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Mon, 21 Oct 2024 20:29:35 -0600 Subject: [PATCH 04/12] feat: Removes build ordering --- Sources/Graphiti/Component/Component.swift | 21 --------------------- Sources/Graphiti/Schema/Schema.swift | 8 -------- 2 files changed, 29 deletions(-) diff --git a/Sources/Graphiti/Component/Component.swift b/Sources/Graphiti/Component/Component.swift index 18694b6..3aca66f 100644 --- a/Sources/Graphiti/Component/Component.swift +++ b/Sources/Graphiti/Component/Component.swift @@ -36,25 +36,4 @@ enum ComponentType { case type case types case union - - /// An index used to sort components into the correct schema build order. This order goes - /// from "least other type references" to "most". By building in this order we are able to satisfy - /// hard ordering requirements (interfaces MUST be built before inheriting types), as well as - /// reduce unnecessary TypeReferences. - var buildOrder: Int { - switch self { - case .none: return 0 - case .scalar: return 1 - case .enum: return 2 - case .interface: return 3 - case .input: return 4 - case .connection: return 5 - case .type: return 6 - case .types: return 7 - case .union: return 8 - case .query: return 9 - case .mutation: return 10 - case .subscription: return 11 - } - } } diff --git a/Sources/Graphiti/Schema/Schema.swift b/Sources/Graphiti/Schema/Schema.swift index 75fd701..e02ce65 100644 --- a/Sources/Graphiti/Schema/Schema.swift +++ b/Sources/Graphiti/Schema/Schema.swift @@ -12,16 +12,8 @@ public final class Schema { let typeProvider = SchemaTypeProvider() typeProvider.federatedSDL = federatedSDL - // Collect types mappings first for component in components { try component.setGraphQLName(typeProvider: typeProvider) - } - - // Order component by componentType build order - let sortedComponents = components.sorted { - $0.componentType.buildOrder <= $1.componentType.buildOrder - } - for component in sortedComponents { try component.update(typeProvider: typeProvider, coders: coders) } From e953b7c42496407a20fb12aab8d4f6a92ac08624 Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Mon, 21 Oct 2024 20:31:57 -0600 Subject: [PATCH 05/12] feat: Removes unnecessary setGraphQLName --- Sources/Graphiti/Component/Component.swift | 1 - Sources/Graphiti/Enum/Enum.swift | 4 ---- Sources/Graphiti/Input/Input.swift | 4 ---- Sources/Graphiti/Interface/Interface.swift | 4 ---- Sources/Graphiti/Scalar/Scalar.swift | 4 ---- Sources/Graphiti/Schema/Schema.swift | 1 - Sources/Graphiti/Type/Type.swift | 4 ---- Sources/Graphiti/Union/Union.swift | 4 ---- 8 files changed, 26 deletions(-) diff --git a/Sources/Graphiti/Component/Component.swift b/Sources/Graphiti/Component/Component.swift index 3aca66f..2cb031a 100644 --- a/Sources/Graphiti/Component/Component.swift +++ b/Sources/Graphiti/Component/Component.swift @@ -11,7 +11,6 @@ open class Component { } func update(typeProvider _: SchemaTypeProvider, coders _: Coders) throws {} - func setGraphQLName(typeProvider _: SchemaTypeProvider) throws {} } public extension Component { diff --git a/Sources/Graphiti/Enum/Enum.swift b/Sources/Graphiti/Enum/Enum.swift index 22b2ff4..f32fc98 100644 --- a/Sources/Graphiti/Enum/Enum.swift +++ b/Sources/Graphiti/Enum/Enum.swift @@ -26,10 +26,6 @@ public final class Enum< try typeProvider.add(type: EnumType.self, as: enumType) } - override func setGraphQLName(typeProvider: SchemaTypeProvider) throws { - try typeProvider.mapName(EnumType.self, to: name) - } - private init( type _: EnumType.Type, name: String?, diff --git a/Sources/Graphiti/Input/Input.swift b/Sources/Graphiti/Input/Input.swift index 28eefc9..3b3e21c 100644 --- a/Sources/Graphiti/Input/Input.swift +++ b/Sources/Graphiti/Input/Input.swift @@ -24,10 +24,6 @@ public final class Input< try typeProvider.add(type: InputObjectType.self, as: inputObjectType) } - override func setGraphQLName(typeProvider: SchemaTypeProvider) throws { - try typeProvider.mapName(InputObjectType.self, to: name) - } - func fields(typeProvider: TypeProvider) throws -> InputObjectFieldMap { var map: InputObjectFieldMap = [:] diff --git a/Sources/Graphiti/Interface/Interface.swift b/Sources/Graphiti/Interface/Interface.swift index aad7e97..a6446da 100644 --- a/Sources/Graphiti/Interface/Interface.swift +++ b/Sources/Graphiti/Interface/Interface.swift @@ -19,10 +19,6 @@ public final class Interface: TypeComponent< try typeProvider.add(type: InterfaceType.self, as: interfaceType) } - override func setGraphQLName(typeProvider: SchemaTypeProvider) throws { - try typeProvider.mapName(InterfaceType.self, to: name) - } - func fields(typeProvider: TypeProvider, coders: Coders) throws -> GraphQLFieldMap { var map: GraphQLFieldMap = [:] diff --git a/Sources/Graphiti/Scalar/Scalar.swift b/Sources/Graphiti/Scalar/Scalar.swift index d62720b..3de7bed 100644 --- a/Sources/Graphiti/Scalar/Scalar.swift +++ b/Sources/Graphiti/Scalar/Scalar.swift @@ -51,10 +51,6 @@ open class Scalar: TypeComponent Map { try encoder.encode(scalar) diff --git a/Sources/Graphiti/Schema/Schema.swift b/Sources/Graphiti/Schema/Schema.swift index e02ce65..959b297 100644 --- a/Sources/Graphiti/Schema/Schema.swift +++ b/Sources/Graphiti/Schema/Schema.swift @@ -13,7 +13,6 @@ public final class Schema { typeProvider.federatedSDL = federatedSDL for component in components { - try component.setGraphQLName(typeProvider: typeProvider) try component.update(typeProvider: typeProvider, coders: coders) } diff --git a/Sources/Graphiti/Type/Type.swift b/Sources/Graphiti/Type/Type.swift index a8df744..d305dc8 100644 --- a/Sources/Graphiti/Type/Type.swift +++ b/Sources/Graphiti/Type/Type.swift @@ -76,10 +76,6 @@ public final class Type: TypeComponent< } } - override func setGraphQLName(typeProvider: SchemaTypeProvider) throws { - try typeProvider.mapName(ObjectType.self, to: name) - } - func fields(typeProvider: TypeProvider, coders: Coders) throws -> GraphQLFieldMap { var map: GraphQLFieldMap = [:] diff --git a/Sources/Graphiti/Union/Union.swift b/Sources/Graphiti/Union/Union.swift index d4babf5..791d495 100644 --- a/Sources/Graphiti/Union/Union.swift +++ b/Sources/Graphiti/Union/Union.swift @@ -18,10 +18,6 @@ public final class Union: TypeComponent Date: Thu, 31 Oct 2024 22:58:47 -0600 Subject: [PATCH 06/12] feat!: Remove Graphiti TypeReference --- Sources/Graphiti/Definition/TypeProvider.swift | 16 +++------------- Sources/Graphiti/Definition/TypeReference.swift | 11 ----------- Sources/Graphiti/Definition/Wrappers.swift | 1 - 3 files changed, 3 insertions(+), 25 deletions(-) delete mode 100644 Sources/Graphiti/Definition/TypeReference.swift diff --git a/Sources/Graphiti/Definition/TypeProvider.swift b/Sources/Graphiti/Definition/TypeProvider.swift index 164b188..6be0f8c 100644 --- a/Sources/Graphiti/Definition/TypeProvider.swift +++ b/Sources/Graphiti/Definition/TypeProvider.swift @@ -66,22 +66,12 @@ extension TypeProvider { from: GraphQLList(graphQLType), isOptional: isOptional ) - case .reference: - let name = getGraphQLName(of: type.wrappedType) - let referenceType = GraphQLTypeReference(name) - - return try getGraphQLOptionalType(from: referenceType, isOptional: isOptional) } } else { - if let graphQLType = graphQLTypeMap[AnyType(type)] { - return try getGraphQLOptionalType(from: graphQLType, isOptional: isOptional) - } else { - // If we haven't seen this type yet, just store it as a type reference and resolve later. - let name = getGraphQLName(of: type) - let referenceType = GraphQLTypeReference(name) - - return try getGraphQLOptionalType(from: referenceType, isOptional: isOptional) + guard let graphQLType = graphQLTypeMap[AnyType(type)] else { + throw GraphQLError(message: "Type not found \(type).") } + return try getGraphQLOptionalType(from: graphQLType, isOptional: isOptional) } } diff --git a/Sources/Graphiti/Definition/TypeReference.swift b/Sources/Graphiti/Definition/TypeReference.swift deleted file mode 100644 index f2f4cba..0000000 --- a/Sources/Graphiti/Definition/TypeReference.swift +++ /dev/null @@ -1,11 +0,0 @@ -public enum TypeReference {} - -extension TypeReference: Wrapper { - static var wrappedType: Any.Type { - return Referent.self - } - - static var modifier: WrapperModifier { - return .reference - } -} diff --git a/Sources/Graphiti/Definition/Wrappers.swift b/Sources/Graphiti/Definition/Wrappers.swift index c5c7a59..3eae529 100644 --- a/Sources/Graphiti/Definition/Wrappers.swift +++ b/Sources/Graphiti/Definition/Wrappers.swift @@ -1,7 +1,6 @@ enum WrapperModifier { case optional case list - case reference } protocol Wrapper { From 520659669471162f98927efc2178abff36dbfd57 Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Thu, 31 Oct 2024 22:58:53 -0600 Subject: [PATCH 07/12] fix: Avoid SDL key build ordering issue --- Sources/Graphiti/Argument/Argument.swift | 4 ++++ Sources/Graphiti/Argument/ArgumentComponent.swift | 4 ++++ Sources/Graphiti/Federation/Key/Key.swift | 6 ++---- Sources/Graphiti/Federation/Key/KeyComponent.swift | 4 +--- Sources/Graphiti/Type/Type.swift | 6 +----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/Graphiti/Argument/Argument.swift b/Sources/Graphiti/Argument/Argument.swift index 31b8ac8..737e1f9 100644 --- a/Sources/Graphiti/Argument/Argument.swift +++ b/Sources/Graphiti/Argument/Argument.swift @@ -17,6 +17,10 @@ public class Argument: ArgumentComponent return (name, argument) } + override func getName() -> String { + return name + } + init(name: String) { self.name = name } diff --git a/Sources/Graphiti/Argument/ArgumentComponent.swift b/Sources/Graphiti/Argument/ArgumentComponent.swift index c0378b4..0d8163a 100644 --- a/Sources/Graphiti/Argument/ArgumentComponent.swift +++ b/Sources/Graphiti/Argument/ArgumentComponent.swift @@ -9,6 +9,10 @@ public class ArgumentComponent { ) throws -> (String, GraphQLArgument) { fatalError() } + + func getName() -> String { + fatalError() + } } public extension ArgumentComponent { diff --git a/Sources/Graphiti/Federation/Key/Key.swift b/Sources/Graphiti/Federation/Key/Key.swift index 0fe8799..e05c8e6 100644 --- a/Sources/Graphiti/Federation/Key/Key.swift +++ b/Sources/Graphiti/Federation/Key/Key.swift @@ -26,12 +26,10 @@ public class Key: KeyComponen } override func validate( - againstFields fieldNames: [String], - typeProvider: TypeProvider, - coders: Coders + againstFields fieldNames: [String] ) throws { // Ensure that every argument is included in the provided field list - for (name, _) in try arguments(typeProvider: typeProvider, coders: coders) { + for name in arguments.map({ $0.getName() }) { if !fieldNames.contains(name) { throw GraphQLError(message: "Argument name not found in type fields: \(name)") } diff --git a/Sources/Graphiti/Federation/Key/KeyComponent.swift b/Sources/Graphiti/Federation/Key/KeyComponent.swift index c897d1d..0bb51bc 100644 --- a/Sources/Graphiti/Federation/Key/KeyComponent.swift +++ b/Sources/Graphiti/Federation/Key/KeyComponent.swift @@ -17,9 +17,7 @@ public class KeyComponent { } func validate( - againstFields _: [String], - typeProvider _: TypeProvider, - coders _: Coders + againstFields _: [String] ) throws { fatalError() } diff --git a/Sources/Graphiti/Type/Type.swift b/Sources/Graphiti/Type/Type.swift index d305dc8..96c74bd 100644 --- a/Sources/Graphiti/Type/Type.swift +++ b/Sources/Graphiti/Type/Type.swift @@ -20,11 +20,7 @@ public final class Type: TypeComponent< let fields = try self.fields(typeProvider: typeProvider, coders: coders) // Validate federation keys, if present for key in self.keys { - try key.validate( - againstFields: Array(fields.keys), - typeProvider: typeProvider, - coders: coders - ) + try key.validate(againstFields: Array(fields.keys)) } return fields }, From e9853e2837ba9dcfabbb86312d77a7b6809364e3 Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Thu, 31 Oct 2024 23:33:13 -0600 Subject: [PATCH 08/12] chore: Formatting --- Sources/Graphiti/Query/Query.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/Graphiti/Query/Query.swift b/Sources/Graphiti/Query/Query.swift index e9f3237..7c257e1 100644 --- a/Sources/Graphiti/Query/Query.swift +++ b/Sources/Graphiti/Query/Query.swift @@ -18,7 +18,9 @@ public final class Query: Component { if !typeProvider.federatedTypes.isEmpty { let federatedTypes = typeProvider.federatedTypes guard let sdl = typeProvider.federatedSDL else { - throw GraphQLError(message: "If federated types are included, SDL must be provided") + throw GraphQLError( + message: "If federated types are included, SDL must be provided" + ) } // Add subgraph types to provider (_Service, _Any, _Entity) From 8811ad619ac16c5526ec8ac199bd862ff93118e0 Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Tue, 5 Nov 2024 23:46:27 -0700 Subject: [PATCH 09/12] feat!: Removes Field/InputField `as` instantiate arg This was typically used to override a resolver type into a TypeReference. With TypeReference no longer necessary, allowing this simply reduces type safety. --- Sources/Graphiti/Field/Field/Field.swift | 104 ------------------ Sources/Graphiti/InputField/InputField.swift | 19 ---- Tests/GraphitiTests/ConnectionTests.swift | 2 +- .../DirectiveTests/DirectiveTests.swift | 2 +- .../HelloWorldTests/HelloWorldTests.swift | 2 +- Tests/GraphitiTests/PartialSchemaTests.swift | 16 +-- Tests/GraphitiTests/SchemaBuilderTests.swift | 8 +- .../StarWarsAPI/StarWarsAPI.swift | 8 +- Tests/GraphitiTests/UnionTests.swift | 4 +- 9 files changed, 21 insertions(+), 144 deletions(-) diff --git a/Sources/Graphiti/Field/Field/Field.swift b/Sources/Graphiti/Field/Field/Field.swift index 22152bb..5ba7ea1 100644 --- a/Sources/Graphiti/Field/Field/Field.swift +++ b/Sources/Graphiti/Field/Field/Field.swift @@ -131,27 +131,6 @@ public extension Field { } } -public extension Field { - convenience init( - _ name: String, - at function: @escaping AsyncResolve, - as: FieldType.Type, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init(name: name, arguments: [argument()], asyncResolve: function) - } - - convenience init( - _ name: String, - at function: @escaping AsyncResolve, - as: FieldType.Type, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init(name: name, arguments: arguments(), asyncResolve: function) - } -} - // MARK: SimpleAsyncResolve Initializers public extension Field { @@ -173,27 +152,6 @@ public extension Field { } } -public extension Field { - convenience init( - _ name: String, - at function: @escaping SimpleAsyncResolve, - as: FieldType.Type, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init(name: name, arguments: [argument()], simpleAsyncResolve: function) - } - - convenience init( - _ name: String, - at function: @escaping SimpleAsyncResolve, - as: FieldType.Type, - @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 @@ -220,29 +178,6 @@ public extension Field { } } -public extension Field { - @_disfavoredOverload - convenience init( - _ name: String, - at function: @escaping SyncResolve, - as: FieldType.Type, - @ArgumentComponentBuilder _ argument: () -> ArgumentComponent - ) { - self.init(name: name, arguments: [argument()], syncResolve: function) - } - - @_disfavoredOverload - convenience init( - _ name: String, - at function: @escaping SyncResolve, - as: FieldType.Type, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init(name: name, arguments: arguments(), syncResolve: function) - } -} - // MARK: Keypath Initializers public extension Field where Arguments == NoArguments { @@ -260,22 +195,6 @@ public extension Field where Arguments == NoArguments { } } -public extension Field where Arguments == NoArguments { - convenience init( - _ name: String, - at keyPath: KeyPath, - as _: FieldType.Type - ) { - let syncResolve: SyncResolve = { type in - { _, _ in - type[keyPath: keyPath] - } - } - - self.init(name: name, arguments: [], syncResolve: syncResolve) - } -} - public extension Field { @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) convenience init( @@ -323,26 +242,3 @@ public extension Field { self.init(name: name, arguments: arguments(), concurrentResolve: function) } } - -public extension Field { - @available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *) - convenience init( - _ name: String, - at function: @escaping ConcurrentResolve, - as: FieldType.Type, - @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, - as: FieldType.Type, - @ArgumentComponentBuilder _ arguments: () - -> [ArgumentComponent] = { [] } - ) { - self.init(name: name, arguments: arguments(), concurrentResolve: function) - } -} diff --git a/Sources/Graphiti/InputField/InputField.swift b/Sources/Graphiti/InputField/InputField.swift index 46d4133..fb95ea8 100644 --- a/Sources/Graphiti/InputField/InputField.swift +++ b/Sources/Graphiti/InputField/InputField.swift @@ -39,25 +39,6 @@ public extension InputField { } } -public extension InputField { - convenience init( - _ name: String, - at _: KeyPath, - as _: FieldType.Type - ) { - self.init(name: name) - } -} - -public extension InputField { - convenience init( - _ name: String, - as _: FieldType.Type - ) { - self.init(name: name) - } -} - public extension InputField where FieldType: Encodable { func defaultValue(_ defaultValue: FieldType) -> Self { self.defaultValue = AnyEncodable(defaultValue) diff --git a/Tests/GraphitiTests/ConnectionTests.swift b/Tests/GraphitiTests/ConnectionTests.swift index 0b92114..96e3f43 100644 --- a/Tests/GraphitiTests/ConnectionTests.swift +++ b/Tests/GraphitiTests/ConnectionTests.swift @@ -489,7 +489,7 @@ class ConnectionTests: XCTestCase { let schema = try Schema { Type(ChatObject.self, as: "Chat") { - Field("messages", at: ChatObject.messages, as: Connection.self) { + Field("messages", at: ChatObject.messages) { Argument("first", at: \.first) Argument("after", at: \.after) Argument("last", at: \.last) diff --git a/Tests/GraphitiTests/DirectiveTests/DirectiveTests.swift b/Tests/GraphitiTests/DirectiveTests/DirectiveTests.swift index 2beb636..34db5c8 100644 --- a/Tests/GraphitiTests/DirectiveTests/DirectiveTests.swift +++ b/Tests/GraphitiTests/DirectiveTests/DirectiveTests.swift @@ -153,7 +153,7 @@ class DirectiveTests: XCTestCase { } Query { - Field("test", at: OneOfResolver.test, as: TestObject.self) { + Field("test", at: OneOfResolver.test) { Argument("input", at: \.input) } } diff --git a/Tests/GraphitiTests/HelloWorldTests/HelloWorldTests.swift b/Tests/GraphitiTests/HelloWorldTests/HelloWorldTests.swift index d80c051..eb316bc 100644 --- a/Tests/GraphitiTests/HelloWorldTests/HelloWorldTests.swift +++ b/Tests/GraphitiTests/HelloWorldTests/HelloWorldTests.swift @@ -126,7 +126,7 @@ struct HelloAPI: API { Input(UserInput.self) { InputField("id", at: \.id) - InputField("name", as: String?.self) + InputField("name", at: \.name) InputField("friends", at: \.friends) } diff --git a/Tests/GraphitiTests/PartialSchemaTests.swift b/Tests/GraphitiTests/PartialSchemaTests.swift index 737c4de..0a38c66 100644 --- a/Tests/GraphitiTests/PartialSchemaTests.swift +++ b/Tests/GraphitiTests/PartialSchemaTests.swift @@ -34,7 +34,7 @@ class PartialSchemaTests: XCTestCase { @FieldDefinitions override var query: Fields { - Field("hero", at: StarWarsResolver.hero, as: Character.self) { + Field("hero", at: StarWarsResolver.hero) { Argument("episode", at: \.episode) .description( "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode." @@ -61,7 +61,7 @@ class PartialSchemaTests: XCTestCase { Field("name", at: \.name) Field("appearsIn", at: \.appearsIn) Field("homePlanet", at: \.homePlanet) - Field("friends", at: Human.getFriends, as: [Character].self) + Field("friends", at: Human.getFriends) .description("The friends of the human, or an empty list if they have none.") Field("secretBackstory", at: Human.getSecretBackstory) .description("Where are they from and how they came to be who they are.") @@ -71,7 +71,7 @@ class PartialSchemaTests: XCTestCase { Field("name", at: \.name) Field("appearsIn", at: \.appearsIn) Field("primaryFunction", at: \.primaryFunction) - Field("friends", at: Droid.getFriends, as: [Character].self) + Field("friends", at: Droid.getFriends) .description("The friends of the droid, or an empty list if they have none.") Field("secretBackstory", at: Droid.getSecretBackstory) .description("Where are they from and how they came to be who they are.") @@ -89,7 +89,7 @@ class PartialSchemaTests: XCTestCase { Argument("id", at: \.id) .description("Id of the droid.") } - Field("search", at: StarWarsResolver.search, as: [SearchResult].self) { + Field("search", at: StarWarsResolver.search) { Argument("query", at: \.query) .defaultValue("R2-D2") } @@ -238,7 +238,7 @@ class PartialSchemaTests: XCTestCase { }.description("One of the films in the Star Wars Trilogy.") }, query: { - Field("hero", at: StarWarsResolver.hero, as: Character.self) { + Field("hero", at: StarWarsResolver.hero) { Argument("episode", at: \.episode) .description( "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode." @@ -264,7 +264,7 @@ class PartialSchemaTests: XCTestCase { Field("name", at: \.name) Field("appearsIn", at: \.appearsIn) Field("homePlanet", at: \.homePlanet) - Field("friends", at: Human.getFriends, as: [Character].self) + Field("friends", at: Human.getFriends) .description( "The friends of the human, or an empty list if they have none." ) @@ -276,7 +276,7 @@ class PartialSchemaTests: XCTestCase { Field("name", at: \.name) Field("appearsIn", at: \.appearsIn) Field("primaryFunction", at: \.primaryFunction) - Field("friends", at: Droid.getFriends, as: [Character].self) + Field("friends", at: Droid.getFriends) .description( "The friends of the droid, or an empty list if they have none." ) @@ -294,7 +294,7 @@ class PartialSchemaTests: XCTestCase { Argument("id", at: \.id) .description("Id of the droid.") } - Field("search", at: StarWarsResolver.search, as: [SearchResult].self) { + Field("search", at: StarWarsResolver.search) { Argument("query", at: \.query) .defaultValue("R2-D2") } diff --git a/Tests/GraphitiTests/SchemaBuilderTests.swift b/Tests/GraphitiTests/SchemaBuilderTests.swift index 043f93b..e531e57 100644 --- a/Tests/GraphitiTests/SchemaBuilderTests.swift +++ b/Tests/GraphitiTests/SchemaBuilderTests.swift @@ -11,7 +11,7 @@ class SchemaBuilderTests: XCTestCase { // Add assets slightly out of order builder.addQuery { - Field("hero", at: StarWarsResolver.hero, as: Character.self) { + Field("hero", at: StarWarsResolver.hero) { Argument("episode", at: \.episode) .description( "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode." @@ -49,7 +49,7 @@ class SchemaBuilderTests: XCTestCase { Field("name", at: \.name) Field("appearsIn", at: \.appearsIn) Field("homePlanet", at: \.homePlanet) - Field("friends", at: Human.getFriends, as: [Character].self) + Field("friends", at: Human.getFriends) .description("The friends of the human, or an empty list if they have none.") Field("secretBackstory", at: Human.getSecretBackstory) .description("Where are they from and how they came to be who they are.") @@ -59,7 +59,7 @@ class SchemaBuilderTests: XCTestCase { Field("name", at: \.name) Field("appearsIn", at: \.appearsIn) Field("primaryFunction", at: \.primaryFunction) - Field("friends", at: Droid.getFriends, as: [Character].self) + Field("friends", at: Droid.getFriends) .description("The friends of the droid, or an empty list if they have none.") Field("secretBackstory", at: Droid.getSecretBackstory) .description("Where are they from and how they came to be who they are.") @@ -89,7 +89,7 @@ class SchemaBuilderTests: XCTestCase { Argument("id", at: \.id) .description("Id of the droid.") } - Field("search", at: StarWarsResolver.search, as: [SearchResult].self) { + Field("search", at: StarWarsResolver.search) { Argument("query", at: \.query) .defaultValue("R2-D2") } diff --git a/Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift b/Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift index 26e4d00..fa751ba 100644 --- a/Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift +++ b/Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift @@ -52,7 +52,7 @@ public struct StarWarsAPI: API { Field("appearsIn", at: \.appearsIn) Field("homePlanet", at: \.homePlanet) - Field("friends", at: Human.getFriends, as: [Character].self) + Field("friends", at: Human.getFriends) .description("The friends of the human, or an empty list if they have none.") Field("secretBackstory", at: Human.getSecretBackstory) @@ -66,7 +66,7 @@ public struct StarWarsAPI: API { Field("appearsIn", at: \.appearsIn) Field("primaryFunction", at: \.primaryFunction) - Field("friends", at: Droid.getFriends, as: [Character].self) + Field("friends", at: Droid.getFriends) .description("The friends of the droid, or an empty list if they have none.") Field("secretBackstory", at: Droid.getSecretBackstory) @@ -77,7 +77,7 @@ public struct StarWarsAPI: API { Union(SearchResult.self, members: Planet.self, Human.self, Droid.self) Query { - Field("hero", at: StarWarsResolver.hero, as: Character.self) { + Field("hero", at: StarWarsResolver.hero) { Argument("episode", at: \.episode) .description( "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode." @@ -95,7 +95,7 @@ public struct StarWarsAPI: API { .description("Id of the droid.") } - Field("search", at: StarWarsResolver.search, as: [SearchResult].self) { + Field("search", at: StarWarsResolver.search) { Argument("query", at: \.query) .defaultValue("R2-D2") } diff --git a/Tests/GraphitiTests/UnionTests.swift b/Tests/GraphitiTests/UnionTests.swift index 6e93d8c..33209c2 100644 --- a/Tests/GraphitiTests/UnionTests.swift +++ b/Tests/GraphitiTests/UnionTests.swift @@ -22,7 +22,7 @@ class UnionTests: XCTestCase { Union(SearchResult.self, members: Planet.self, Human.self, Droid.self) Query { - Field("search", at: StarWarsResolver.search, as: [SearchResult].self) { + Field("search", at: StarWarsResolver.search) { Argument("query", at: \.query) } } @@ -48,7 +48,7 @@ class UnionTests: XCTestCase { ]) Query { - Field("search", at: StarWarsResolver.search, as: [SearchResult].self) { + Field("search", at: StarWarsResolver.search) { Argument("query", at: \.query) } } From c18bbc384112a3e8c07badda047d7891e0709a7d Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Wed, 6 Nov 2024 00:52:33 -0700 Subject: [PATCH 10/12] chore!: Deletes Reflection.isEncodable --- Sources/Graphiti/Definition/Reflection.swift | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Sources/Graphiti/Definition/Reflection.swift b/Sources/Graphiti/Definition/Reflection.swift index 618577c..46a016c 100644 --- a/Sources/Graphiti/Definition/Reflection.swift +++ b/Sources/Graphiti/Definition/Reflection.swift @@ -4,19 +4,6 @@ public enum Reflection { return description.hasSuffix("Protocol") } - @available(*, deprecated, message: "No longer used") - public static func isEncodable(type: Any.Type) -> Bool { - if isProtocol(type: type) { - return true - } - - if let type = type as? Wrapper.Type { - return isEncodable(type: type.wrappedType) - } - - return type is Encodable.Type - } - public static func name(for instance: Subject) -> String { var typeName: [Character] = [] var genericArgument: [Character] = [] From 8ef2aae31610a249827096d112eb1f8f02221dac Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Wed, 6 Nov 2024 00:52:45 -0700 Subject: [PATCH 11/12] chore!: Deletes Types --- Sources/Graphiti/Types/Types.swift | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 Sources/Graphiti/Types/Types.swift diff --git a/Sources/Graphiti/Types/Types.swift b/Sources/Graphiti/Types/Types.swift deleted file mode 100644 index d52dcd0..0000000 --- a/Sources/Graphiti/Types/Types.swift +++ /dev/null @@ -1,24 +0,0 @@ -import GraphQL - -@available(*, deprecated, message: "No longer use this. Instead define types using `Type`.") -public final class Types: Component { - let types: [Any.Type] - - override func update(typeProvider: SchemaTypeProvider, coders _: Coders) throws { - typeProvider.types = try types.map { - try typeProvider.getNamedType(from: $0) - } - } - - init(types: [Any.Type]) { - self.types = types - super.init( - name: "", - type: .types - ) - } - - public convenience init(_ types: Any.Type...) { - self.init(types: types) - } -} From 1f356743ccc9ebcf7cada4e1aeb390dd2db8abad Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Wed, 6 Nov 2024 00:52:55 -0700 Subject: [PATCH 12/12] doc: Adds migration guide --- MIGRATION.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 MIGRATION.md diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..584860b --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,39 @@ +# Migration + +## 1.0 to 2.0 + +### TypeReference removal + +The `TypeReference` type was removed in v2.0.0, since it was made unnecessary when using the [GraphQL](https://github.com/GraphQLSwift/GraphQL) closure-based `field` API. Simply replace any `TypeReference` usage with the actual type: + +```swift +// Before +let schema = try! Schema { + Type(Object1.self) { } + Type(Object2.self) { + Field("object1", at: \.object1, as: TypeReference.self) + } +} + +// After +let schema = try! Schema { + Type(Object1.self) { } + Type(Object2.self) { + Field("object1", at: \.object1) + } +} +``` + +### Field/InputField `as` argument removal + +Since TypeReference was removed, there is no longer a functional need for the `as` argument on `Field` and `InputField` +types. To remove it, simply omit it and let the compiler determine the type from the signature of the `at` argument. + +### `Types` removal + +The deprecated `Types` type has been removed. Instead define types individually using the `Type` initializers. + +### Reflection `isEncodable` + +The deprecated `Reflection.isEncodable(type: Any.Type) -> Bool` function has been removed. Please re-implement in your +package if this functionality is needed. \ No newline at end of file