File tree

67 files changed

+12847
-1226
lines changed

Some content is hidden

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

67 files changed

+12847
-1226
lines changed
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://.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.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on .
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-
[![ 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://.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

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
)
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 , since it is
196+
* exposed to user code. Care should be taken to not pull values from the
197+
* Object .
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)