Skip to content

Commit e26bb85

Browse files
Adds type validation to arguments
Previously it was only on variables
1 parent 300dbd7 commit e26bb85

File tree

3 files changed

+62
-52
lines changed

3 files changed

+62
-52
lines changed

Sources/GraphQL/Execution/Values.swift

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,32 @@ func getArgumentValues(argDefs: [GraphQLArgumentDefinition], argASTs: [Argument]
4343
var args = OrderedDictionary<String, Map>()
4444
for argDef in argDefs {
4545
let argName = argDef.name
46+
let argValue: Map
47+
4648
if let argAST = argASTMap[argName] {
47-
args[argName] = try valueFromAST(
49+
argValue = try valueFromAST(
4850
valueAST: argAST.value,
4951
type: argDef.type,
5052
variables: variables
5153
)
5254
} else {
5355
// If AST doesn't contain field, it is undefined
5456
if let defaultValue = argDef.defaultValue {
55-
args[argName] = defaultValue
57+
argValue = defaultValue
5658
} else {
57-
args[argName] = .undefined
59+
argValue = .undefined
5860
}
5961
}
62+
63+
let errors = try validate(value: argValue, forType: argDef.type)
64+
guard errors.isEmpty else {
65+
let message = "\n" + errors.joined(separator: "\n")
66+
throw GraphQLError(
67+
message:
68+
"Argument \"\(argName)\" got invalid value \(argValue).\(message)" // TODO: "\(JSON.stringify(input)).\(message)",
69+
)
70+
}
71+
args[argName] = argValue
6072
}
6173
return .dictionary(args)
6274
}
@@ -79,30 +91,22 @@ func getVariableValue(schema: GraphQLSchema, definitionAST: VariableDefinition,
7991
)
8092
}
8193

82-
if input == .undefined {
83-
if let defaultValue = definitionAST.defaultValue {
84-
return try valueFromAST(valueAST: defaultValue, type: inputType)
85-
} else {
86-
if inputType is GraphQLNonNull {
87-
throw GraphQLError(message: "Non-nullable type \(inputType) must be provided.")
88-
} else {
89-
return .undefined
90-
}
91-
}
94+
var toCoerce = input
95+
if input == .undefined, let defaultValue = definitionAST.defaultValue {
96+
toCoerce = try valueFromAST(valueAST: defaultValue, type: inputType)
9297
}
9398

94-
let errors = try isValidValue(value: input, type: inputType)
99+
let errors = try validate(value: toCoerce, forType: inputType)
95100
guard errors.isEmpty else {
96101
let message = !errors.isEmpty ? "\n" + errors.joined(separator: "\n") : ""
97102
throw GraphQLError(
98103
message:
99-
"Variable \"$\(variable.name.value)\" got invalid value " +
100-
"\(input).\(message)", // TODO: "\(JSON.stringify(input)).\(message)",
104+
"Variable \"$\(variable.name.value)\" got invalid value \"\(toCoerce)\".\(message)", // TODO: "\(JSON.stringify(input)).\(message)",
101105
nodes: [definitionAST]
102106
)
103107
}
104108

105-
return try coerceValue(value: input, type: inputType)
109+
return try coerceValue(value: toCoerce, type: inputType)
106110
}
107111

108112
/**
@@ -111,7 +115,7 @@ func getVariableValue(schema: GraphQLSchema, definitionAST: VariableDefinition,
111115
func coerceValue(value: Map, type: GraphQLInputType) throws -> Map {
112116
if let nonNull = type as? GraphQLNonNull {
113117
// Note: we're not checking that the result of coerceValue is non-null.
114-
// We only call this function after calling isValidValue.
118+
// We only call this function after calling validate.
115119
guard let nonNullType = nonNull.ofType as? GraphQLInputType else {
116120
throw GraphQLError(message: "NonNull must wrap an input type")
117121
}
@@ -168,5 +172,5 @@ func coerceValue(value: Map, type: GraphQLInputType) throws -> Map {
168172
return try leafType.parseValue(value: value)
169173
}
170174

171-
throw GraphQLError(message: "Must be input type")
175+
throw GraphQLError(message: "Provided type is not an input type")
172176
}

Sources/GraphQL/Utilities/IsValidValue.swift

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,39 @@
33
* accepted for that type. This is primarily useful for validating the
44
* runtime values of query variables.
55
*/
6-
func isValidValue(value: Map, type: GraphQLInputType) throws -> [String] {
6+
func validate(value: Map, forType type: GraphQLInputType) throws -> [String] {
77
// A value must be provided if the type is non-null.
8-
if let type = type as? GraphQLNonNull {
9-
if value == .null {
10-
if let namedType = type.ofType as? GraphQLNamedType {
11-
return ["Expected \"\(namedType.name)!\", found null."]
12-
}
13-
8+
if let nonNullType = type as? GraphQLNonNull {
9+
guard let wrappedType = nonNullType.ofType as? GraphQLInputType else {
10+
throw GraphQLError(message: "Input non-null type must wrap another input type")
11+
}
12+
13+
if value == .null{
1414
return ["Expected non-null value, found null."]
1515
}
16+
if value == .undefined {
17+
return ["Expected non-null value was not provided."]
18+
}
1619

17-
return try isValidValue(value: value, type: type.ofType as! GraphQLInputType)
20+
return try validate(value: value, forType: wrappedType)
1821
}
19-
20-
guard value != .null else {
22+
23+
// If nullable, either null or undefined are allowed
24+
guard value != .null && value != .undefined else {
2125
return []
2226
}
2327

2428
// Lists accept a non-list value as a list of one.
25-
if let type = type as? GraphQLList {
26-
let itemType = type.ofType
29+
if let listType = type as? GraphQLList {
30+
guard let itemType = listType.ofType as? GraphQLInputType else {
31+
throw GraphQLError(message: "Input list type must wrap another input type")
32+
}
2733

2834
if case .array(let values) = value {
2935
var errors: [String] = []
3036

3137
for (index, item) in values.enumerated() {
32-
let e = try isValidValue(value: item, type: itemType as! GraphQLInputType).map {
38+
let e = try validate(value: item, forType: itemType).map {
3339
"In element #\(index): \($0)"
3440
}
3541
errors.append(contentsOf: e)
@@ -38,16 +44,16 @@ func isValidValue(value: Map, type: GraphQLInputType) throws -> [String] {
3844
return errors
3945
}
4046

41-
return try isValidValue(value: value, type: itemType as! GraphQLInputType)
47+
return try validate(value: value, forType: itemType)
4248
}
4349

4450
// Input objects check each defined field.
45-
if let type = type as? GraphQLInputObjectType {
51+
if let objectType = type as? GraphQLInputObjectType {
4652
guard case .dictionary(let dictionary) = value else {
47-
return ["Expected \"\(type.name)\", found not an object."]
53+
return ["Expected \"\(objectType.name)\", found not an object."]
4854
}
4955

50-
let fields = type.fields
56+
let fields = objectType.fields
5157
var errors: [String] = []
5258

5359
// Ensure every provided field is defined.
@@ -59,7 +65,7 @@ func isValidValue(value: Map, type: GraphQLInputType) throws -> [String] {
5965

6066
// Ensure every defined field is valid.
6167
for (fieldName, field) in fields {
62-
let newErrors = try isValidValue(value: value[fieldName], type: field.type).map {
68+
let newErrors = try validate(value: value[fieldName], forType: field.type).map {
6369
"In field \"\(fieldName)\": \($0)"
6470
}
6571

@@ -69,20 +75,20 @@ func isValidValue(value: Map, type: GraphQLInputType) throws -> [String] {
6975
return errors
7076
}
7177

72-
guard let type = type as? GraphQLLeafType else {
73-
fatalError("Must be input type")
74-
}
75-
76-
// Scalar/Enum input checks to ensure the type can parse the value to
77-
// a non-null value.
78-
do {
79-
let parseResult = try type.parseValue(value: value)
80-
if parseResult == .null {
81-
return ["Expected type \"\(type.name)\", found \(value)."]
78+
if let leafType = type as? GraphQLLeafType {
79+
// Scalar/Enum input checks to ensure the type can parse the value to
80+
// a non-null value.
81+
do {
82+
let parseResult = try leafType.parseValue(value: value)
83+
if parseResult == .null || parseResult == .undefined {
84+
return ["Expected type \"\(leafType.name)\", found \(value)."]
85+
}
86+
} catch {
87+
return ["Expected type \"\(leafType.name)\", found \(value)."]
8288
}
83-
} catch {
84-
return ["Expected type \"\(type.name)\", found \(value)."]
89+
90+
return []
8591
}
8692

87-
return []
93+
throw GraphQLError(message: "Provided type was not provided")
8894
}

Sources/GraphQL/Utilities/ValueFromAST.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func valueFromAST(valueAST: Value, type: GraphQLInputType, variables: [String: M
7272

7373
if let objectType = type as? GraphQLInputObjectType {
7474
guard let objectValue = valueAST as? ObjectValue else {
75-
throw GraphQLError(message: "Must be object type")
75+
throw GraphQLError(message: "Input object must be object type")
7676
}
7777

7878
let fields = objectType.fields
@@ -102,5 +102,5 @@ func valueFromAST(valueAST: Value, type: GraphQLInputType, variables: [String: M
102102
return try leafType.parseLiteral(valueAST: valueAST)
103103
}
104104

105-
throw GraphQLError(message: "Must be input type")
105+
throw GraphQLError(message: "Provided type is not an input type")
106106
}

0 commit comments

Comments
 (0)