Skip to content

Commit ca8b57f

Browse files
Merge pull request #135 from NeedleInAJayStack/feature/validation
Adds Validation Rules
2 parents db0d3fe + a4063a1 commit ca8b57f

File tree

57 files changed

+6392
-172
lines changed

Some content is hidden

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

57 files changed

+6392
-172
lines changed

Sources/GraphQL/Language/AST.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ public final class OperationDefinition {
401401
}
402402
return .array(variableDefinitions)
403403
case "directives":
404-
guard !variableDefinitions.isEmpty else {
404+
guard !directives.isEmpty else {
405405
return nil
406406
}
407407
return .array(directives)
@@ -475,12 +475,20 @@ public final class VariableDefinition {
475475
public private(set) var variable: Variable
476476
public private(set) var type: Type
477477
public private(set) var defaultValue: Value?
478+
public private(set) var directives: [Directive]
478479

479-
init(loc: Location? = nil, variable: Variable, type: Type, defaultValue: Value? = nil) {
480+
init(
481+
loc: Location? = nil,
482+
variable: Variable,
483+
type: Type,
484+
defaultValue: Value? = nil,
485+
directives: [Directive] = []
486+
) {
480487
self.loc = loc
481488
self.variable = variable
482489
self.type = type
483490
self.defaultValue = defaultValue
491+
self.directives = directives
484492
}
485493

486494
public func get(key: String) -> NodeResult? {
@@ -491,6 +499,11 @@ public final class VariableDefinition {
491499
return .node(type)
492500
case "defaultValue":
493501
return defaultValue.map { .node($0) }
502+
case "directives":
503+
guard !directives.isEmpty else {
504+
return nil
505+
}
506+
return .array(directives)
494507
default:
495508
return nil
496509
}
@@ -525,6 +538,14 @@ public final class VariableDefinition {
525538
return
526539
}
527540
self.defaultValue = defaultValue
541+
case "directives":
542+
guard
543+
case let .array(values) = value,
544+
let directives = values as? [Directive]
545+
else {
546+
return
547+
}
548+
self.directives = directives
528549
default:
529550
return
530551
}
@@ -1748,7 +1769,10 @@ extension OperationTypeDefinition: Equatable {
17481769
}
17491770
}
17501771

1751-
public protocol TypeDefinition: TypeSystemDefinition {}
1772+
public protocol TypeDefinition: TypeSystemDefinition {
1773+
var name: Name { get }
1774+
}
1775+
17521776
extension ScalarTypeDefinition: TypeDefinition {}
17531777
extension ObjectTypeDefinition: TypeDefinition {}
17541778
extension InterfaceTypeDefinition: TypeDefinition {}

Sources/GraphQL/Language/Parser.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ func parseVariableDefinition(lexer: Lexer) throws -> VariableDefinition {
276276
variable: parseVariable(lexer: lexer),
277277
type: (expect(lexer: lexer, kind: .colon), parseTypeReference(lexer: lexer)).1,
278278
defaultValue: skip(lexer: lexer, kind: .equals) ?
279-
parseValueLiteral(lexer: lexer, isConst: true) : nil
279+
parseValueLiteral(lexer: lexer, isConst: true) : nil,
280+
directives: parseDirectives(lexer: lexer)
280281
)
281282
}
282283

Sources/GraphQL/Language/Printer.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ extension OperationDefinition: Printable {
4848
extension VariableDefinition: Printable {
4949
var printed: String {
5050
variable + ": " + type.printed + wrap(" = ", defaultValue?.printed)
51-
// + wrap(" ", join(directives, " "))
52-
// TODO: variable directives are currently not supported
51+
+ wrap(" ", join(directives, " "))
5352
}
5453
}
5554

Sources/GraphQL/Language/Visitor.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ let QueryDocumentKeys: [Kind: [String]] = [
33

44
.document: ["definitions"],
55
.operationDefinition: ["name", "variableDefinitions", "directives", "selectionSet"],
6-
.variableDefinition: ["variable", "type", "defaultValue"],
6+
.variableDefinition: ["variable", "type", "defaultValue", "directives"],
77
.variable: ["name"],
88
.selectionSet: ["selections"],
99
.field: ["alias", "name", "arguments", "directives", "selectionSet"],
@@ -304,9 +304,14 @@ func visitInParallel(visitors: [Visitor]) -> Visitor {
304304
} else if case .node = result {
305305
return result
306306
}
307-
} // else if case let .node(skippedNode) = skipping[i], skippedNode == node {
308-
// skipping[i] = nil
309-
// }
307+
} else if
308+
case let .node(skippedNodeValue) = skipping[i],
309+
let skippedNode = skippedNodeValue,
310+
skippedNode.kind == node.kind,
311+
skippedNode.loc == node.loc
312+
{
313+
skipping[i] = nil
314+
}
310315
}
311316

312317
return .continue

Sources/GraphQL/SwiftUtilities/SuggestionList.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ func suggestionList(
2020
}
2121
return optionsByDistance.keys.sorted {
2222
// Values are guaranteed non-nil since the keys come from the object itself
23-
optionsByDistance[$0]! - optionsByDistance[$1]! != 0
23+
let distanceDiff = optionsByDistance[$0]! - optionsByDistance[$1]!
24+
if distanceDiff != 0 {
25+
return distanceDiff < 0
26+
} else {
27+
return $0.lexicographicallyPrecedes($1)
28+
}
2429
}
2530
}
2631

Sources/GraphQL/Type/Definition.swift

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Foundation
22
import NIO
3+
import OrderedCollections
34

45
/**
56
* These are all of the possible kinds of types.
@@ -171,35 +172,22 @@ public final class GraphQLScalarType {
171172
public let kind: TypeKind = .scalar
172173

173174
let serialize: (Any) throws -> Map
174-
let parseValue: ((Map) throws -> Map)?
175-
let parseLiteral: ((Value) throws -> Map)?
176-
177-
public init(
178-
name: String,
179-
description: String? = nil,
180-
serialize: @escaping (Any) throws -> Map
181-
) throws {
182-
try assertValid(name: name)
183-
self.name = name
184-
self.description = description
185-
self.serialize = serialize
186-
parseValue = nil
187-
parseLiteral = nil
188-
}
175+
let parseValue: (Map) throws -> Map
176+
let parseLiteral: (Value) throws -> Map
189177

190178
public init(
191179
name: String,
192180
description: String? = nil,
193181
serialize: @escaping (Any) throws -> Map,
194-
parseValue: @escaping (Map) throws -> Map,
195-
parseLiteral: @escaping (Value) throws -> Map
182+
parseValue: ((Map) throws -> Map)? = nil,
183+
parseLiteral: ((Value) throws -> Map)? = nil
196184
) throws {
197185
try assertValid(name: name)
198186
self.name = name
199187
self.description = description
200188
self.serialize = serialize
201-
self.parseValue = parseValue
202-
self.parseLiteral = parseLiteral
189+
self.parseValue = parseValue ?? defaultParseValue
190+
self.parseLiteral = parseLiteral ?? defaultParseLiteral
203191
}
204192

205193
// Serializes an internal value to include in a response.
@@ -209,15 +197,23 @@ public final class GraphQLScalarType {
209197

210198
// Parses an externally provided value to use as an input.
211199
public func parseValue(value: Map) throws -> Map {
212-
return try parseValue?(value) ?? Map.null
200+
return try parseValue(value)
213201
}
214202

215203
// Parses an externally provided literal value to use as an input.
216204
public func parseLiteral(valueAST: Value) throws -> Map {
217-
return try parseLiteral?(valueAST) ?? Map.null
205+
return try parseLiteral(valueAST)
218206
}
219207
}
220208

209+
let defaultParseValue: ((Map) throws -> Map) = { value in
210+
value
211+
}
212+
213+
let defaultParseLiteral: ((Value) throws -> Map) = { value in
214+
try valueFromASTUntyped(valueAST: value)
215+
}
216+
221217
extension GraphQLScalarType: Encodable {
222218
private enum CodingKeys: String, CodingKey {
223219
case name
@@ -513,7 +509,7 @@ public struct GraphQLResolveInfo {
513509
public let variableValues: [String: Any]
514510
}
515511

516-
public typealias GraphQLFieldMap = [String: GraphQLField]
512+
public typealias GraphQLFieldMap = OrderedDictionary<String, GraphQLField>
517513

518514
public struct GraphQLField {
519515
public let type: GraphQLOutputType
@@ -573,7 +569,7 @@ public struct GraphQLField {
573569
}
574570
}
575571

576-
public typealias GraphQLFieldDefinitionMap = [String: GraphQLFieldDefinition]
572+
public typealias GraphQLFieldDefinitionMap = OrderedDictionary<String, GraphQLFieldDefinition>
577573

578574
public final class GraphQLFieldDefinition {
579575
public let name: String
@@ -659,7 +655,7 @@ extension GraphQLFieldDefinition: KeySubscriptable {
659655
}
660656
}
661657

662-
public typealias GraphQLArgumentConfigMap = [String: GraphQLArgument]
658+
public typealias GraphQLArgumentConfigMap = OrderedDictionary<String, GraphQLArgument>
663659

664660
public struct GraphQLArgument {
665661
public let type: GraphQLInputType
@@ -1018,7 +1014,7 @@ public final class GraphQLEnumType {
10181014
let mapValue = try map(from: value)
10191015
guard let enumValue = valueLookup[mapValue] else {
10201016
throw GraphQLError(
1021-
message: "Enum '\(name)' cannot represent value '\(mapValue)'."
1017+
message: "Enum \"\(name)\" cannot represent value: \(mapValue)."
10221018
)
10231019
}
10241020
return .string(enumValue.name)
@@ -1027,13 +1023,13 @@ public final class GraphQLEnumType {
10271023
public func parseValue(value: Map) throws -> Map {
10281024
guard let valueStr = value.string else {
10291025
throw GraphQLError(
1030-
message: "Enum '\(name)' cannot represent non-string value '\(value)'." +
1026+
message: "Enum \"\(name)\" cannot represent non-string value: \(value)." +
10311027
didYouMeanEnumValue(unknownValueStr: value.description)
10321028
)
10331029
}
10341030
guard let enumValue = nameLookup[valueStr] else {
10351031
throw GraphQLError(
1036-
message: "Value '\(valueStr)' does not exist in '\(name)' enum." +
1032+
message: "Value \"\(valueStr)\" does not exist in \"\(name)\" enum." +
10371033
didYouMeanEnumValue(unknownValueStr: valueStr)
10381034
)
10391035
}
@@ -1043,14 +1039,14 @@ public final class GraphQLEnumType {
10431039
public func parseLiteral(valueAST: Value) throws -> Map {
10441040
guard let enumNode = valueAST as? EnumValue else {
10451041
throw GraphQLError(
1046-
message: "Enum '\(name)' cannot represent non-enum value '\(valueAST)'." +
1047-
didYouMeanEnumValue(unknownValueStr: "\(valueAST)"),
1042+
message: "Enum \"\(name)\" cannot represent non-enum value: \(print(ast: valueAST))." +
1043+
didYouMeanEnumValue(unknownValueStr: print(ast: valueAST)),
10481044
nodes: [valueAST]
10491045
)
10501046
}
10511047
guard let enumValue = nameLookup[enumNode.value] else {
10521048
throw GraphQLError(
1053-
message: "Value '\(enumNode)' does not exist in '\(name)' enum." +
1049+
message: "Value \"\(enumNode.value)\" does not exist in \"\(name)\" enum." +
10541050
didYouMeanEnumValue(unknownValueStr: enumNode.value),
10551051
nodes: [valueAST]
10561052
)
@@ -1136,7 +1132,7 @@ func defineEnumValues(
11361132
return definitions
11371133
}
11381134

1139-
public typealias GraphQLEnumValueMap = [String: GraphQLEnumValue]
1135+
public typealias GraphQLEnumValueMap = OrderedDictionary<String, GraphQLEnumValue>
11401136

11411137
public struct GraphQLEnumValue {
11421138
public let value: Map
@@ -1317,7 +1313,7 @@ public struct InputObjectField {
13171313
}
13181314
}
13191315

1320-
public typealias InputObjectFieldMap = [String: InputObjectField]
1316+
public typealias InputObjectFieldMap = OrderedDictionary<String, InputObjectField>
13211317

13221318
public final class InputObjectFieldDefinition {
13231319
public let name: String
@@ -1384,7 +1380,14 @@ extension InputObjectFieldDefinition: KeySubscriptable {
13841380
}
13851381
}
13861382

1387-
public typealias InputObjectFieldDefinitionMap = [String: InputObjectFieldDefinition]
1383+
public func isRequiredInputField(_ field: InputObjectFieldDefinition) -> Bool {
1384+
return field.type is GraphQLNonNull && field.defaultValue == nil
1385+
}
1386+
1387+
public typealias InputObjectFieldDefinitionMap = OrderedDictionary<
1388+
String,
1389+
InputObjectFieldDefinition
1390+
>
13881391

13891392
/**
13901393
* List Modifier

Sources/GraphQL/Type/Directives.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public enum DirectiveLocation: String, Encodable {
77
case fragmentDefinition = "FRAGMENT_DEFINITION"
88
case fragmentSpread = "FRAGMENT_SPREAD"
99
case inlineFragment = "INLINE_FRAGMENT"
10+
case variableDefinition = "VARIABLE_DEFINITION"
1011
// Schema Definitions
1112
case schema = "SCHEMA"
1213
case scalar = "SCALAR"
@@ -30,18 +31,21 @@ public struct GraphQLDirective: Encodable {
3031
public let description: String
3132
public let locations: [DirectiveLocation]
3233
public let args: [GraphQLArgumentDefinition]
34+
public let isRepeatable: Bool
3335

3436
public init(
3537
name: String,
36-
description: String,
38+
description: String = "",
3739
locations: [DirectiveLocation],
38-
args: GraphQLArgumentConfigMap = [:]
40+
args: GraphQLArgumentConfigMap = [:],
41+
isRepeatable: Bool = false
3942
) throws {
4043
try assertValid(name: name)
4144
self.name = name
4245
self.description = description
4346
self.locations = locations
4447
self.args = try defineArgumentMap(args: args)
48+
self.isRepeatable = isRepeatable
4549
}
4650
}
4751

Sources/GraphQL/Type/Introspection.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ let __Directive = try! GraphQLObjectType(
8282
fields: [
8383
"name": GraphQLField(type: GraphQLNonNull(GraphQLString)),
8484
"description": GraphQLField(type: GraphQLString),
85+
"isRepeatable": GraphQLField(type: GraphQLNonNull(GraphQLBoolean)),
8586
"locations": GraphQLField(type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__DirectiveLocation)))),
8687
"args": GraphQLField(
8788
type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue))),

0 commit comments

Comments
 (0)