Skip to content

Commit c275f65

Browse files
Merge pull request #113 from samisuteria/extend-schema
Extend Schema Support
2 parents ad4a715 + bf127f4 commit c275f65

File tree

4 files changed

+112
-1
lines changed

4 files changed

+112
-1
lines changed

Sources/GraphQL/Language/AST.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ extension EnumTypeDefinition: Node {}
213213
extension EnumValueDefinition: Node {}
214214
extension InputObjectTypeDefinition: Node {}
215215
extension TypeExtensionDefinition: Node {}
216+
extension SchemaExtensionDefinition: Node {}
216217
extension DirectiveDefinition: Node {}
217218

218219
public final class Name {
@@ -1099,6 +1100,7 @@ extension NonNullType: Equatable {
10991100
public protocol TypeSystemDefinition: Definition {}
11001101
extension SchemaDefinition: TypeSystemDefinition {}
11011102
extension TypeExtensionDefinition: TypeSystemDefinition {}
1103+
extension SchemaExtensionDefinition: TypeSystemDefinition {}
11021104
extension DirectiveDefinition: TypeSystemDefinition {}
11031105

11041106
public func == (lhs: TypeSystemDefinition, rhs: TypeSystemDefinition) -> Bool {
@@ -1119,6 +1121,10 @@ public func == (lhs: TypeSystemDefinition, rhs: TypeSystemDefinition) -> Bool {
11191121
if let r = rhs as? TypeDefinition {
11201122
return l == r
11211123
}
1124+
case let l as SchemaExtensionDefinition:
1125+
if let r = rhs as? SchemaExtensionDefinition {
1126+
return l == r
1127+
}
11221128
default:
11231129
return false
11241130
}
@@ -1543,6 +1549,23 @@ extension TypeExtensionDefinition: Equatable {
15431549
}
15441550
}
15451551

1552+
public final class SchemaExtensionDefinition {
1553+
public let kind: Kind = .schemaExtensionDefinition
1554+
public let loc: Location?
1555+
public let definition: SchemaDefinition
1556+
1557+
init(loc: Location? = nil, definition: SchemaDefinition) {
1558+
self.loc = loc
1559+
self.definition = definition
1560+
}
1561+
}
1562+
1563+
extension SchemaExtensionDefinition: Equatable {
1564+
public static func == (lhs: SchemaExtensionDefinition, rhs: SchemaExtensionDefinition) -> Bool {
1565+
return lhs.definition == rhs.definition
1566+
}
1567+
}
1568+
15461569
public final class DirectiveDefinition {
15471570
public let kind: Kind = .directiveDefinition
15481571
public let loc: Location?

Sources/GraphQL/Language/Kinds.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,5 @@ public enum Kind {
3636
case inputObjectTypeDefinition
3737
case typeExtensionDefinition
3838
case directiveDefinition
39+
case schemaExtensionDefinition
3940
}

Sources/GraphQL/Language/Parser.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@ func parseTypeSystemDefinition(lexer: Lexer) throws -> TypeSystemDefinition {
706706
case "union": return try parseUnionTypeDefinition(lexer: lexer)
707707
case "enum": return try parseEnumTypeDefinition(lexer: lexer)
708708
case "input": return try parseInputObjectTypeDefinition(lexer: lexer)
709-
case "extend": return try parseTypeExtensionDefinition(lexer: lexer)
709+
case "extend": return try parseExtensionDefinition(lexer: lexer)
710710
case "directive": return try parseDirectiveDefinition(lexer: lexer)
711711
default: break
712712
}
@@ -1002,6 +1002,16 @@ func parseInputObjectTypeDefinition(lexer: Lexer) throws -> InputObjectTypeDefin
10021002
)
10031003
}
10041004

1005+
func parseExtensionDefinition(lexer: Lexer) throws -> TypeSystemDefinition {
1006+
let token = try lexer.lookahead()
1007+
switch token.value {
1008+
case "type": return try parseTypeExtensionDefinition(lexer: lexer)
1009+
case "schema": return try parseSchemaExtensionDefinition(lexer: lexer)
1010+
default:
1011+
throw syntaxError(source: lexer.source, position: token.start, description: "expected schema or type after extend")
1012+
}
1013+
}
1014+
10051015
/**
10061016
* TypeExtensionDefinition : extend ObjectTypeDefinition
10071017
*/
@@ -1015,6 +1025,25 @@ func parseTypeExtensionDefinition(lexer: Lexer) throws -> TypeExtensionDefinitio
10151025
)
10161026
}
10171027

1028+
/**
1029+
* SchemaExtensionDefinition: extend SchemaExtensionDefinition
1030+
*/
1031+
func parseSchemaExtensionDefinition(lexer: Lexer) throws -> SchemaExtensionDefinition {
1032+
let start = lexer.token
1033+
try expectKeyword(lexer: lexer, value: "extend")
1034+
let description = try parseDescription(lexer: lexer)
1035+
try expectKeyword(lexer: lexer, value: "schema")
1036+
let directives = try parseDirectives(lexer: lexer)
1037+
return SchemaExtensionDefinition(
1038+
loc: loc(lexer: lexer, startToken: start),
1039+
definition: SchemaDefinition(
1040+
loc: loc(lexer: lexer, startToken: start),
1041+
description: description,
1042+
directives: directives,
1043+
operationTypes: []
1044+
))
1045+
}
1046+
10181047
/**
10191048
* DirectiveDefinition :
10201049
* - directive @ Name ArgumentsDefinition? on DirectiveLocations

Tests/GraphQLTests/LanguageTests/SchemaParserTests.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,64 @@ class SchemaParserTests: XCTestCase {
111111
XCTAssert(result == expected)
112112
}
113113

114+
func testSchemeExtension() throws {
115+
// Based on Apollo Federation example schema: https://github.com/apollographql/apollo-federation-subgraph-compatibility/blob/main/COMPATIBILITY.md#products-schema-to-be-implemented-by-library-maintainers
116+
let source =
117+
"""
118+
extend schema
119+
@link(
120+
url: "https://specs.apollo.dev/federation/v2.0",
121+
import: [
122+
"@extends",
123+
"@external",
124+
"@key",
125+
"@inaccessible",
126+
"@override",
127+
"@provides",
128+
"@requires",
129+
"@shareable",
130+
"@tag"
131+
]
132+
)
133+
"""
134+
135+
let expected = Document(
136+
definitions: [
137+
SchemaExtensionDefinition(
138+
definition: SchemaDefinition(
139+
directives: [
140+
Directive(
141+
name: nameNode("link"),
142+
arguments: [
143+
Argument(
144+
name: nameNode("url"),
145+
value: StringValue(
146+
value: "https://specs.apollo.dev/federation/v2.0",
147+
block: false
148+
)
149+
),
150+
Argument(
151+
name: nameNode("import"),
152+
value: ListValue(values: [
153+
StringValue(value: "@extends", block: false),
154+
StringValue(value: "@external", block: false),
155+
StringValue(value: "@key", block: false),
156+
StringValue(value: "@inaccessible", block: false),
157+
StringValue(value: "@override", block: false),
158+
StringValue(value: "@provides", block: false),
159+
StringValue(value: "@requires", block: false),
160+
StringValue(value: "@shareable", block: false),
161+
StringValue(value: "@tag", block: false),
162+
]))
163+
])
164+
],
165+
operationTypes: []))
166+
])
167+
168+
let result = try parse(source: source)
169+
XCTAssert(result == expected)
170+
}
171+
114172
func testSimpleNonNullType() throws {
115173
let source = "type Hello { world: String! }"
116174

0 commit comments

Comments
 (0)