Skip to content

Schema Definition Language Support #154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
ad736da
chore: Adds missing resolved packages
NeedleInAJayStack Oct 28, 2024
9937a7a
feat!: Adds SDL validation context
NeedleInAJayStack Mar 16, 2024
5d9aecf
feat: Adds specified rules
NeedleInAJayStack Mar 16, 2024
7d91bd0
refactor: Adds SDL validation assertions
NeedleInAJayStack Mar 16, 2024
3e5a81e
feat!: Schema query is optional
NeedleInAJayStack Jan 18, 2024
8b9a38a
refactor: IntrospectionTypes are actual types
NeedleInAJayStack Jun 10, 2024
600357f
feat: Adds ExtendSchema functionality
NeedleInAJayStack Jun 10, 2024
1a8dc8a
fix: Default execute supports dictionaries
NeedleInAJayStack Jul 20, 2024
6aa6a6b
fix: AST from value handles Booleans
NeedleInAJayStack Jul 22, 2024
bc36a09
fix: Types are ordered
NeedleInAJayStack Jul 22, 2024
a8bca06
fix: Allow empty field map
NeedleInAJayStack Jul 22, 2024
a14329e
fix: Removes empty definition checks
NeedleInAJayStack Sep 21, 2024
91eb501
refactor: Add types before operations
NeedleInAJayStack Sep 21, 2024
0168729
feat!: Adds closure support to Field definitions
NeedleInAJayStack Jul 24, 2024
761b0b0
fix: TypeReference support
NeedleInAJayStack Oct 21, 2024
e073ade
feat: Adds isPrintableAsBlockString
NeedleInAJayStack Sep 22, 2024
c91a342
feat: Aligns introspection to javascript
NeedleInAJayStack Sep 22, 2024
3158558
feat!: Directive has nullable description
NeedleInAJayStack Sep 22, 2024
c59603f
feat: Converts Directive to class
NeedleInAJayStack Sep 22, 2024
c43d273
fix: Schema type map respects input type order
NeedleInAJayStack Oct 8, 2024
ec74172
fix: Adjusts schema init to js reference
NeedleInAJayStack Oct 9, 2024
19dfcb0
test: Fixes schema to not use type reference
NeedleInAJayStack Oct 9, 2024
4e25e56
feat: Deprecates GraphQLTypeReference
NeedleInAJayStack Oct 9, 2024
cc44dfd
fix: default deprecation reason is string
NeedleInAJayStack Oct 14, 2024
4e0c879
feat: Adds concatAST
NeedleInAJayStack Oct 2, 2024
53afe99
feat: Adds printSchema
NeedleInAJayStack Jul 21, 2024
a5478ac
feat: Adds extendSchema
NeedleInAJayStack Sep 24, 2024
d8a1bff
feat: Adds buildASTSchema
NeedleInAJayStack Jun 10, 2024
5f4c6c0
feat: Adds validateSchema
NeedleInAJayStack Oct 9, 2024
ff92fd2
fix: Adds 'get' support to definition node types
NeedleInAJayStack Oct 14, 2024
70b7dbd
fix: Updates visitor traversal paths
NeedleInAJayStack Oct 20, 2024
e5b0ff8
test: Adds SDL validation test utilities
NeedleInAJayStack Oct 15, 2024
3cecede
fix: Fixes KnownDirectivesRule for SDL
NeedleInAJayStack Oct 20, 2024
4544619
feat: Adds KnownTypeNamesRule SDL support
NeedleInAJayStack Oct 14, 2024
062a848
feat: Adds LoneSchemaDefinitionRule
NeedleInAJayStack Oct 15, 2024
e459369
feat: Adds UniqueOperationTypesRule
NeedleInAJayStack Oct 17, 2024
136ac28
feat: Adds UniqueTypeNamesRule
NeedleInAJayStack Oct 18, 2024
69042cd
feat: Adds UniqueEnumValueNamesRule
NeedleInAJayStack Oct 18, 2024
45d9b15
feat: Adds UniqueFieldDefinitionNamesRule
NeedleInAJayStack Oct 18, 2024
27e4657
feat: Adds UniqueArgumentDefinitionNamesRule
NeedleInAJayStack Oct 18, 2024
b67bf99
feat: Adds UniqueDirectiveNamesRule
NeedleInAJayStack Oct 19, 2024
fc00f70
feat: Adds TypeExtension protocol
NeedleInAJayStack Oct 20, 2024
6fda256
feat: Adds PossibleTypeExtensionsRule
NeedleInAJayStack Oct 20, 2024
c5161e8
feat: Adds KnownArgumentNamesOnDirectivesRule
NeedleInAJayStack Oct 20, 2024
f8709f4
feat: Adds ProvidedRequiredArgumentsOnDirectivesRule
NeedleInAJayStack Oct 20, 2024
42591cb
feat!: Deletes GraphQLTypeReference
NeedleInAJayStack Oct 28, 2024
cd3db36
docs: Adds sswg badge & updates Swift version
NeedleInAJayStack Oct 28, 2024
070e721
docs: Adds migration guide
NeedleInAJayStack Oct 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading