Skip to content

Commit

Permalink
Merge pull request #154 from NeedleInAJayStack/experiment/SDL-utilities
Browse files Browse the repository at this point in the history
Schema Definition Language Support
  • Loading branch information
NeedleInAJayStack authored Oct 28, 2024
2 parents ec809df + 070e721 commit 5e098b3
Show file tree
Hide file tree
Showing 67 changed files with 12,847 additions and 1,226 deletions.
76 changes: 76 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Migration

## 2.0 to 3.0

### TypeReference removal

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.

To remove `GraphQLTypeReference`, you can typically just replace it with a reference to the `GraphQLObjectType` instance:

```swift
// Before
let object1 = try GraphQLObjectType(
name: "Object1"
)
let object2 = try GraphQLObjectType(
name: "Object2"
fields: ["object1": GraphQLField(type: GraphQLTypeReference("Object1"))]
)

// After
let object1 = try GraphQLObjectType(
name: "Object1"
)
let object2 = try GraphQLObjectType(
name: "Object2"
fields: ["object1": GraphQLField(type: object1)]
)
```

For more complex cyclic or recursive types, simply create the types first and assign the `fields` property afterward. Here's an example:

```swift
// Before
let object1 = try GraphQLObjectType(
name: "Object1"
fields: ["object2": GraphQLField(type: GraphQLTypeReference("Object2"))]
)
let object2 = try GraphQLObjectType(
name: "Object2"
fields: ["object1": GraphQLField(type: GraphQLTypeReference("Object1"))]
)

// After
let object1 = try GraphQLObjectType(name: "Object1")
let object2 = try GraphQLObjectType(name: "Object2")
object1.fields = { [weak object2] in
guard let object2 = object2 else { return [:] }
return ["object2": GraphQLField(type: object2)]
}
object2.fields = { [weak object1] in
guard let object1 = object1 else { return [:] }
return ["object1": GraphQLField(type: object1)]
}
```

Note that this also gives you the chance to explicitly handle the memory cycle that cyclic types cause as well.

### Type Definition Arrays

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()`):

- `GraphQLObjectType.fields`
- `GraphQLObjectType.interfaces`
- `GraphQLInterfaceType.fields`
- `GraphQLInterfaceType.interfaces`
- `GraphQLUnionType.types`
- `GraphQLInputObjectType.fields`

### Directive description is optional

`GraphQLDirective` has changed from a struct to a class, and its `description` property is now optional.

### GraphQL type codability

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.
18 changes: 18 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{
"pins" : [
{
"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",
Expand All @@ -17,6 +26,15 @@
"revision" : "4c4453b489cf76e6b3b0f300aba663eb78182fad",
"version" : "2.70.0"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "c8a44d836fe7913603e246acab7c528c2e780168",
"version" : "1.4.0"
}
}
],
"version" : 2
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# GraphQL

[![Swift][swift-badge]][swift-url]
[![SSWG][sswg-badge]][sswg-url]
[![License][mit-badge]][mit-url]
[![GitHub Actions][gh-actions-badge]][gh-actions-url]
[![Codebeat][codebeat-badge]][codebeat-url]


The Swift implementation for GraphQL, a query language for APIs created by Facebook.

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

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

For details on upgrading to new major versions, see [MIGRATION](MIGRATION.md).

## Contributing

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

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

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

[sswg-badge]: https://img.shields.io/badge/sswg-incubating-blue.svg?style=flat
[sswg-url]: https://swift.org/sswg/incubation-process.html#incubating-level

[mit-badge]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat
[mit-url]: https://tldrlegal.com/license/mit-license

Expand Down
23 changes: 19 additions & 4 deletions Sources/GraphQL/Execution/Execute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,14 @@ func getOperationRootType(
) throws -> GraphQLObjectType {
switch operation.operation {
case .query:
return schema.queryType
guard let queryType = schema.queryType else {
throw GraphQLError(
message: "Schema is not configured for queries",
nodes: [operation]
)
}

return queryType
case .mutation:
guard let mutationType = schema.mutationType else {
throw GraphQLError(
Expand Down Expand Up @@ -1191,6 +1198,14 @@ func defaultResolve(
let value = subscriptable[info.fieldName]
return eventLoopGroup.next().makeSucceededFuture(value)
}
if let subscriptable = source as? [String: Any] {
let value = subscriptable[info.fieldName]
return eventLoopGroup.next().makeSucceededFuture(value)
}
if let subscriptable = source as? OrderedDictionary<String, Any> {
let value = subscriptable[info.fieldName]
return eventLoopGroup.next().makeSucceededFuture(value)
}

let mirror = Mirror(reflecting: source)
guard let value = mirror.getValue(named: info.fieldName) else {
Expand All @@ -1213,16 +1228,16 @@ func getFieldDef(
parentType: GraphQLObjectType,
fieldName: String
) throws -> GraphQLFieldDefinition {
if fieldName == SchemaMetaFieldDef.name, schema.queryType.name == parentType.name {
if fieldName == SchemaMetaFieldDef.name, schema.queryType?.name == parentType.name {
return SchemaMetaFieldDef
} else if fieldName == TypeMetaFieldDef.name, schema.queryType.name == parentType.name {
} else if fieldName == TypeMetaFieldDef.name, schema.queryType?.name == parentType.name {
return TypeMetaFieldDef
} else if fieldName == TypeNameMetaFieldDef.name {
return TypeNameMetaFieldDef
}

// This field should exist because we passed validation before execution
guard let fieldDefinition = parentType.fields[fieldName] else {
guard let fieldDefinition = try parentType.getFields()[fieldName] else {
throw GraphQLError(
message: "Expected field definition not found: '\(fieldName)' on '\(parentType.name)'"
)
Expand Down
35 changes: 33 additions & 2 deletions Sources/GraphQL/Execution/Values.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func getVariableValue(
definitionAST: VariableDefinition,
input: Map
) throws -> Map {
let type = typeFromAST(schema: schema, inputTypeAST: definitionAST.type)
var type = typeFromAST(schema: schema, inputTypeAST: definitionAST.type)
let variable = definitionAST.variable

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

let fields = objectType.fields
let fields = try objectType.getFields()

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

throw GraphQLError(message: "Provided type is not an input type")
}

/**
* Prepares an object map of argument values given a directive definition
* and a AST node which may contain directives. Optionally also accepts a map
* of variable values.
*
* If the directive does not exist on the node, returns undefined.
*
* Note: The returned value is a plain Object with a prototype, since it is
* exposed to user code. Care should be taken to not pull values from the
* Object prototype.
*/
func getDirectiveValues(
directiveDef: GraphQLDirective,
directives: [Directive],
variableValues: [String: Map] = [:]
) throws -> Map? {
let directiveNode = directives.find { directive in
directive.name.value == directiveDef.name
}

if let directiveNode = directiveNode {
return try getArgumentValues(
argDefs: directiveDef.args,
argASTs: directiveNode.arguments,
variables: variableValues
)
}

return nil
}
Loading

0 comments on commit 5e098b3

Please sign in to comment.