Skip to content

Commit 5b15e16

Browse files
Merge pull request #84 from NeedleInAJayStack/feature/type-reference-improvements
TypeReference Improvements
2 parents 3338389 + f6e9bff commit 5b15e16

File tree

14 files changed

+129
-22
lines changed

14 files changed

+129
-22
lines changed

.swiftformat

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
--wrapcollections before-first
66
--wrapconditions before-first
77
--wrapparameters before-first
8+
9+
--disable wrapSingleLineComments

Package.resolved

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,35 @@
66
"repositoryURL": "https://github.com/GraphQLSwift/GraphQL.git",
77
"state": {
88
"branch": null,
9-
"revision": "ebd2ea40676f8bcbdfd6088c408f3ed321c1a905",
10-
"version": "2.4.0"
9+
"revision": "17b96ed859072fca79dd562da2a79e1bc752756f",
10+
"version": "2.4.1"
11+
}
12+
},
13+
{
14+
"package": "swift-atomics",
15+
"repositoryURL": "https://github.com/apple/swift-atomics.git",
16+
"state": {
17+
"branch": null,
18+
"revision": "919eb1d83e02121cdb434c7bfc1f0c66ef17febe",
19+
"version": "1.0.2"
1120
}
1221
},
1322
{
1423
"package": "swift-collections",
1524
"repositoryURL": "https://github.com/apple/swift-collections",
1625
"state": {
1726
"branch": null,
18-
"revision": "48254824bb4248676bf7ce56014ff57b142b77eb",
19-
"version": "1.0.2"
27+
"revision": "f504716c27d2e5d4144fa4794b12129301d17729",
28+
"version": "1.0.3"
2029
}
2130
},
2231
{
2332
"package": "swift-nio",
2433
"repositoryURL": "https://github.com/apple/swift-nio.git",
2534
"state": {
2635
"branch": null,
27-
"revision": "124119f0bb12384cef35aa041d7c3a686108722d",
28-
"version": "2.40.0"
36+
"revision": "a16e2f54a25b2af217044e5168997009a505930f",
37+
"version": "2.42.0"
2938
}
3039
}
3140
]

Sources/Graphiti/Definition/TypeProvider.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,15 @@ extension TypeProvider {
5656
return try getGraphQLOptionalType(from: referenceType, isOptional: isOptional)
5757
}
5858
} else {
59-
guard let graphQLType = graphQLTypeMap[AnyType(type)] else {
60-
throw GraphQLError(message: "Type \"\(type)\" is not registered.")
61-
}
59+
if let graphQLType = graphQLTypeMap[AnyType(type)] {
60+
return try getGraphQLOptionalType(from: graphQLType, isOptional: isOptional)
61+
} else {
62+
// If we haven't seen this type yet, just store it as a type reference and resolve later.
63+
let name = Reflection.name(for: type)
64+
let referenceType = GraphQLTypeReference(name)
6265

63-
return try getGraphQLOptionalType(from: graphQLType, isOptional: isOptional)
66+
return try getGraphQLOptionalType(from: referenceType, isOptional: isOptional)
67+
}
6468
}
6569
}
6670

Sources/Graphiti/Enum/Enum.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public final class Enum<
2424
)
2525

2626
try typeProvider.map(EnumType.self, to: enumType)
27+
typeProvider.types.append(enumType)
2728
}
2829

2930
private init(

Sources/Graphiti/Input/Input.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public final class Input<
1818
)
1919

2020
try typeProvider.map(InputObjectType.self, to: inputObjectType)
21+
typeProvider.types.append(inputObjectType)
2122
}
2223

2324
func fields(typeProvider: TypeProvider) throws -> InputObjectFieldMap {

Sources/Graphiti/Interface/Interface.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public final class Interface<Resolver, Context, InterfaceType>: Component<Resolv
1212
)
1313

1414
try typeProvider.map(InterfaceType.self, to: interfaceType)
15+
typeProvider.types.append(interfaceType)
1516
}
1617

1718
func fields(typeProvider: TypeProvider, coders: Coders) throws -> GraphQLFieldMap {

Sources/Graphiti/Scalar/Scalar.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ open class Scalar<Resolver, Context, ScalarType: Codable>: Component<Resolver, C
3434
)
3535

3636
try typeProvider.map(ScalarType.self, to: scalarType)
37+
typeProvider.types.append(scalarType)
3738
}
3839

3940
open func serialize(scalar: ScalarType, encoder: MapEncoder) throws -> Map {
@@ -92,6 +93,12 @@ extension GraphQL.Value {
9293
return .string(value.value)
9394
}
9495

96+
if
97+
let value = self as? EnumValue
98+
{
99+
return .string(value.value)
100+
}
101+
95102
if
96103
let value = self as? ListValue
97104
{

Sources/Graphiti/Type/Type.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public final class Type<Resolver, Context, ObjectType: Encodable>: Component<Res
2020
)
2121

2222
try typeProvider.map(ObjectType.self, to: objectType)
23+
typeProvider.types.append(objectType)
2324
}
2425

2526
func fields(typeProvider: TypeProvider, coders: Coders) throws -> GraphQLFieldMap {

Sources/Graphiti/Types/Types.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import GraphQL
22

3+
@available(*, deprecated, message: "No longer use this. Instead define types using `Type`.")
34
public final class Types<Resolver, Context>: Component<Resolver, Context> {
45
let types: [Any.Type]
56

@@ -13,10 +14,8 @@ public final class Types<Resolver, Context>: Component<Resolver, Context> {
1314
self.types = types
1415
super.init(name: "")
1516
}
16-
}
1717

18-
public extension Types {
19-
convenience init(_ types: Any.Type...) {
18+
public convenience init(_ types: Any.Type...) {
2019
self.init(types: types)
2120
}
2221
}

Tests/GraphitiTests/HelloWorldTests/HelloWorldAsyncTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,13 @@ import XCTest
6161
Type(User.self) {
6262
Field("id", at: \.id)
6363
Field("name", at: \.name)
64-
Field("friends", at: \.friends, as: [TypeReference<User>]?.self)
64+
Field("friends", at: \.friends)
6565
}
6666

6767
Input(UserInput.self) {
6868
InputField("id", at: \.id)
6969
InputField("name", at: \.name)
70-
InputField("friends", at: \.friends, as: [TypeReference<UserInput>]?.self)
70+
InputField("friends", at: \.friends)
7171
}
7272

7373
Type(UserEvent.self) {

Tests/GraphitiTests/HelloWorldTests/HelloWorldTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,13 @@ struct HelloAPI: API {
121121
Type(User.self) {
122122
Field("id", at: \.id)
123123
Field("name", at: \.name)
124-
Field("friends", at: \.friends, as: [TypeReference<User>]?.self)
124+
Field("friends", at: \.friends)
125125
}
126126

127127
Input(UserInput.self) {
128128
InputField("id", at: \.id)
129129
InputField("name", as: String?.self)
130-
InputField("friends", at: \.friends, as: [TypeReference<UserInput>]?.self)
130+
InputField("friends", at: \.friends)
131131
}
132132

133133
Type(UserEvent.self) {

Tests/GraphitiTests/SchemaTests.swift

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import Foundation
2+
@testable import Graphiti
3+
import GraphQL
4+
import NIO
5+
import XCTest
6+
7+
class SchemaTests: XCTestCase {
8+
// Tests that circularly dependent objects can be used in schema and resolved correctly
9+
func testCircularDependencies() throws {
10+
struct A: Codable {
11+
let name: String
12+
var b: B {
13+
B(name: name)
14+
}
15+
}
16+
17+
struct B: Codable {
18+
let name: String
19+
var a: A {
20+
A(name: name)
21+
}
22+
}
23+
24+
struct TestResolver {
25+
func a(context _: NoContext, arguments _: NoArguments) -> A {
26+
return A(name: "Circular")
27+
}
28+
}
29+
30+
let testSchema = try Schema<TestResolver, NoContext> {
31+
Type(A.self) {
32+
Field("name", at: \.name)
33+
Field("b", at: \.b)
34+
}
35+
Type(B.self) {
36+
Field("name", at: \.name)
37+
Field("a", at: \.a)
38+
}
39+
Query {
40+
Field("a", at: TestResolver.a)
41+
}
42+
}
43+
let api = TestAPI<TestResolver, NoContext>(
44+
resolver: TestResolver(),
45+
schema: testSchema
46+
)
47+
48+
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
49+
defer { try? group.syncShutdownGracefully() }
50+
51+
XCTAssertEqual(
52+
try api.execute(
53+
request: """
54+
query {
55+
a {
56+
b {
57+
name
58+
}
59+
}
60+
}
61+
""",
62+
context: NoContext(),
63+
on: group
64+
).wait(),
65+
GraphQLResult(data: [
66+
"a": [
67+
"b": [
68+
"name": "Circular",
69+
],
70+
],
71+
])
72+
)
73+
}
74+
}
75+
76+
private class TestAPI<Resolver, ContextType>: API {
77+
public let resolver: Resolver
78+
public let schema: Schema<Resolver, ContextType>
79+
80+
init(resolver: Resolver, schema: Schema<Resolver, ContextType>) {
81+
self.resolver = resolver
82+
self.schema = schema
83+
}
84+
}

Tests/GraphitiTests/StarWarsAPI/StarWarsAPI.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public struct StarWarsAPI: API {
4040
Field("diameter", at: \.diameter)
4141
Field("rotationPeriod", at: \.rotationPeriod)
4242
Field("orbitalPeriod", at: \.orbitalPeriod)
43-
Field("residents", at: \.residents, as: [TypeReference<Human>].self)
43+
Field("residents", at: \.residents)
4444
}
4545
.description(
4646
"A large mass, planet or planetoid in the Star Wars Universe, at the time of 0 ABY."
@@ -100,7 +100,5 @@ public struct StarWarsAPI: API {
100100
.defaultValue("R2-D2")
101101
}
102102
}
103-
104-
Types(Human.self, Droid.self)
105103
}
106104
}

Tests/GraphitiTests/StarWarsTests/StarWarsQueryTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,8 +655,8 @@ class StarWarsQueryTests: XCTestCase {
655655

656656
let schema = try! Schema<TestResolver, NoContext> {
657657
Type(A.self) {
658-
Field("nullableA", at: A.nullableA, as: (TypeReference<A>?).self)
659-
Field("nonNullA", at: A.nonNullA, as: TypeReference<A>.self)
658+
Field("nullableA", at: A.nullableA)
659+
Field("nonNullA", at: A.nonNullA)
660660
Field("throws", at: A.throws)
661661
}
662662

0 commit comments

Comments
 (0)