Skip to content

Commit 5e098b3

Browse files
Merge pull request #154 from NeedleInAJayStack/experiment/SDL-utilities
Schema Definition Language Support
2 parents ec809df + 070e721 commit 5e098b3

File tree

67 files changed

+12847
-1226
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+12847
-1226
lines changed

Diff for: MIGRATION.md

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Migration
2+
3+
## 2.0 to 3.0
4+
5+
### TypeReference removal
6+
7+
The `GraphQLTypeReference` type was removed in v3.0.0, since it was made unnecessary by introducing closure-based `field` API that allows the package to better control the order of type resolution.
8+
9+
To remove `GraphQLTypeReference`, you can typically just replace it with a reference to the `GraphQLObjectType` instance:
10+
11+
```swift
12+
// Before
13+
let object1 = try GraphQLObjectType(
14+
name: "Object1"
15+
)
16+
let object2 = try GraphQLObjectType(
17+
name: "Object2"
18+
fields: ["object1": GraphQLField(type: GraphQLTypeReference("Object1"))]
19+
)
20+
21+
// After
22+
let object1 = try GraphQLObjectType(
23+
name: "Object1"
24+
)
25+
let object2 = try GraphQLObjectType(
26+
name: "Object2"
27+
fields: ["object1": GraphQLField(type: object1)]
28+
)
29+
```
30+
31+
For more complex cyclic or recursive types, simply create the types first and assign the `fields` property afterward. Here's an example:
32+
33+
```swift
34+
// Before
35+
let object1 = try GraphQLObjectType(
36+
name: "Object1"
37+
fields: ["object2": GraphQLField(type: GraphQLTypeReference("Object2"))]
38+
)
39+
let object2 = try GraphQLObjectType(
40+
name: "Object2"
41+
fields: ["object1": GraphQLField(type: GraphQLTypeReference("Object1"))]
42+
)
43+
44+
// After
45+
let object1 = try GraphQLObjectType(name: "Object1")
46+
let object2 = try GraphQLObjectType(name: "Object2")
47+
object1.fields = { [weak object2] in
48+
guard let object2 = object2 else { return [:] }
49+
return ["object2": GraphQLField(type: object2)]
50+
}
51+
object2.fields = { [weak object1] in
52+
guard let object1 = object1 else { return [:] }
53+
return ["object1": GraphQLField(type: object1)]
54+
}
55+
```
56+
57+
Note that this also gives you the chance to explicitly handle the memory cycle that cyclic types cause as well.
58+
59+
### Type Definition Arrays
60+
61+
The following type properties were changed from arrays to closures. To get the array version, in most cases you can just call the `get`-style function (i.e. for `GraphQLObject.fields`, use `GraphQLObject.getFields()`):
62+
63+
- `GraphQLObjectType.fields`
64+
- `GraphQLObjectType.interfaces`
65+
- `GraphQLInterfaceType.fields`
66+
- `GraphQLInterfaceType.interfaces`
67+
- `GraphQLUnionType.types`
68+
- `GraphQLInputObjectType.fields`
69+
70+
### Directive description is optional
71+
72+
`GraphQLDirective` has changed from a struct to a class, and its `description` property is now optional.
73+
74+
### GraphQL type codability
75+
76+
With GraphQL type definitions now including closures, many of the objects in [Definition](https://github.com/GraphQLSwift/GraphQL/blob/main/Sources/GraphQL/Type/Definition.swift) are no longer codable. If you are depending on codability, you can conform the type appropriately in your downstream package.

Diff for: Package.resolved

+18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
{
22
"pins" : [
3+
{
4+
"identity" : "swift-atomics",
5+
"kind" : "remoteSourceControl",
6+
"location" : "https://github.com/apple/swift-atomics.git",
7+
"state" : {
8+
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
9+
"version" : "1.2.0"
10+
}
11+
},
312
{
413
"identity" : "swift-collections",
514
"kind" : "remoteSourceControl",
@@ -17,6 +26,15 @@
1726
"revision" : "4c4453b489cf76e6b3b0f300aba663eb78182fad",
1827
"version" : "2.70.0"
1928
}
29+
},
30+
{
31+
"identity" : "swift-system",
32+
"kind" : "remoteSourceControl",
33+
"location" : "https://github.com/apple/swift-system.git",
34+
"state" : {
35+
"revision" : "c8a44d836fe7913603e246acab7c528c2e780168",
36+
"version" : "1.4.0"
37+
}
2038
}
2139
],
2240
"version" : 2

Diff for: README.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# GraphQL
22

33
[![Swift][swift-badge]][swift-url]
4+
[![SSWG][sswg-badge]][sswg-url]
45
[![License][mit-badge]][mit-url]
5-
[![GitHub Actions][gh-actions-badge]][gh-actions-url]
66
[![Codebeat][codebeat-badge]][codebeat-url]
77

8+
89
The Swift implementation for GraphQL, a query language for APIs created by Facebook.
910

1011
Looking for help? Find resources [from the community](http://graphql.org/community/).
@@ -123,6 +124,8 @@ should be encoded using the `GraphQLJSONEncoder` provided by this package.
123124

124125
This package supports Swift versions in [alignment with Swift NIO](https://github.com/apple/swift-nio?tab=readme-ov-file#swift-versions).
125126

127+
For details on upgrading to new major versions, see [MIGRATION](MIGRATION.md).
128+
126129
## Contributing
127130

128131
If you think you have found a security vulnerability, please follow the
@@ -156,9 +159,12 @@ missing, looking at the original code and "translating" it to Swift works, most
156159

157160
This project is released under the MIT license. See [LICENSE](LICENSE) for details.
158161

159-
[swift-badge]: https://img.shields.io/badge/Swift-5.5-orange.svg?style=flat
162+
[swift-badge]: https://img.shields.io/badge/Swift-5.10-orange.svg?style=flat
160163
[swift-url]: https://swift.org
161164

165+
[sswg-badge]: https://img.shields.io/badge/sswg-incubating-blue.svg?style=flat
166+
[sswg-url]: https://swift.org/sswg/incubation-process.html#incubating-level
167+
162168
[mit-badge]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat
163169
[mit-url]: https://tldrlegal.com/license/mit-license
164170

Diff for: Sources/GraphQL/Execution/Execute.swift

+19-4
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,14 @@ func getOperationRootType(
466466
) throws -> GraphQLObjectType {
467467
switch operation.operation {
468468
case .query:
469-
return schema.queryType
469+
guard let queryType = schema.queryType else {
470+
throw GraphQLError(
471+
message: "Schema is not configured for queries",
472+
nodes: [operation]
473+
)
474+
}
475+
476+
return queryType
470477
case .mutation:
471478
guard let mutationType = schema.mutationType else {
472479
throw GraphQLError(
@@ -1191,6 +1198,14 @@ func defaultResolve(
11911198
let value = subscriptable[info.fieldName]
11921199
return eventLoopGroup.next().makeSucceededFuture(value)
11931200
}
1201+
if let subscriptable = source as? [String: Any] {
1202+
let value = subscriptable[info.fieldName]
1203+
return eventLoopGroup.next().makeSucceededFuture(value)
1204+
}
1205+
if let subscriptable = source as? OrderedDictionary<String, Any> {
1206+
let value = subscriptable[info.fieldName]
1207+
return eventLoopGroup.next().makeSucceededFuture(value)
1208+
}
11941209

11951210
let mirror = Mirror(reflecting: source)
11961211
guard let value = mirror.getValue(named: info.fieldName) else {
@@ -1213,16 +1228,16 @@ func getFieldDef(
12131228
parentType: GraphQLObjectType,
12141229
fieldName: String
12151230
) throws -> GraphQLFieldDefinition {
1216-
if fieldName == SchemaMetaFieldDef.name, schema.queryType.name == parentType.name {
1231+
if fieldName == SchemaMetaFieldDef.name, schema.queryType?.name == parentType.name {
12171232
return SchemaMetaFieldDef
1218-
} else if fieldName == TypeMetaFieldDef.name, schema.queryType.name == parentType.name {
1233+
} else if fieldName == TypeMetaFieldDef.name, schema.queryType?.name == parentType.name {
12191234
return TypeMetaFieldDef
12201235
} else if fieldName == TypeNameMetaFieldDef.name {
12211236
return TypeNameMetaFieldDef
12221237
}
12231238

12241239
// This field should exist because we passed validation before execution
1225-
guard let fieldDefinition = parentType.fields[fieldName] else {
1240+
guard let fieldDefinition = try parentType.getFields()[fieldName] else {
12261241
throw GraphQLError(
12271242
message: "Expected field definition not found: '\(fieldName)' on '\(parentType.name)'"
12281243
)

Diff for: Sources/GraphQL/Execution/Values.swift

+33-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func getVariableValue(
8888
definitionAST: VariableDefinition,
8989
input: Map
9090
) throws -> Map {
91-
let type = typeFromAST(schema: schema, inputTypeAST: definitionAST.type)
91+
var type = typeFromAST(schema: schema, inputTypeAST: definitionAST.type)
9292
let variable = definitionAST.variable
9393

9494
guard let inputType = type as? GraphQLInputType else {
@@ -157,7 +157,7 @@ func coerceValue(value: Map, type: GraphQLInputType) throws -> Map {
157157
throw GraphQLError(message: "Must be dictionary to extract to an input type")
158158
}
159159

160-
let fields = objectType.fields
160+
let fields = try objectType.getFields()
161161

162162
var object = OrderedDictionary<String, Map>()
163163
for (fieldName, field) in fields {
@@ -184,3 +184,34 @@ func coerceValue(value: Map, type: GraphQLInputType) throws -> Map {
184184

185185
throw GraphQLError(message: "Provided type is not an input type")
186186
}
187+
188+
/**
189+
* Prepares an object map of argument values given a directive definition
190+
* and a AST node which may contain directives. Optionally also accepts a map
191+
* of variable values.
192+
*
193+
* If the directive does not exist on the node, returns undefined.
194+
*
195+
* Note: The returned value is a plain Object with a prototype, since it is
196+
* exposed to user code. Care should be taken to not pull values from the
197+
* Object prototype.
198+
*/
199+
func getDirectiveValues(
200+
directiveDef: GraphQLDirective,
201+
directives: [Directive],
202+
variableValues: [String: Map] = [:]
203+
) throws -> Map? {
204+
let directiveNode = directives.find { directive in
205+
directive.name.value == directiveDef.name
206+
}
207+
208+
if let directiveNode = directiveNode {
209+
return try getArgumentValues(
210+
argDefs: directiveDef.args,
211+
argASTs: directiveNode.arguments,
212+
variables: variableValues
213+
)
214+
}
215+
216+
return nil
217+
}

0 commit comments

Comments
 (0)