Skip to content

Commit 834e2cf

Browse files
authored
Merge pull request #87 from NeedleInAJayStack/inputNulls
2 parents e5de315 + 0a47a82 commit 834e2cf

File tree

8 files changed

+615
-52
lines changed

8 files changed

+615
-52
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ${{ matrix.os }}
1414
strategy:
1515
matrix:
16-
os: [macos-10.15, macos-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04]
16+
os: [macos-10.15, macos-latest, ubuntu-18.04, ubuntu-20.04]
1717
steps:
1818
- uses: actions/checkout@v2
1919
- name: Set code coverage path

Sources/GraphQL/Execution/Values.swift

+13-9
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,20 @@ func getArgumentValues(argDefs: [GraphQLArgumentDefinition], argASTs: [Argument]
3535
return try argDefs.reduce([:]) { result, argDef in
3636
var result = result
3737
let name = argDef.name
38-
let valueAST = argASTMap[name]?.value
38+
let argAST = argASTMap[name]
39+
40+
if let argAST = argAST {
41+
let valueAST = argAST.value
3942

40-
let value = try valueFromAST(
41-
valueAST: valueAST,
42-
type: argDef.type,
43-
variables: variableValues
44-
) ?? argDef.defaultValue
43+
let value = try valueFromAST(
44+
valueAST: valueAST,
45+
type: argDef.type,
46+
variables: variableValues
47+
)
4548

46-
if let value = value {
4749
result[name] = value
50+
} else {
51+
result[name] = .null
4852
}
4953

5054
return result
@@ -75,7 +79,7 @@ func getVariableValue(schema: GraphQLSchema, definitionAST: VariableDefinition,
7579
if errors.isEmpty {
7680
if input == .null {
7781
if let defaultValue = definitionAST.defaultValue {
78-
return try valueFromAST(valueAST: defaultValue, type: inputType)!
82+
return try valueFromAST(valueAST: defaultValue, type: inputType)
7983
}
8084
else if !(inputType is GraphQLNonNull) {
8185
return .null
@@ -148,7 +152,7 @@ func coerceValue(type: GraphQLInputType, value: Map) throws -> Map? {
148152
var fieldValue = try coerceValue(type: field!.type, value: value[fieldName] ?? .null)
149153

150154
if fieldValue == .null {
151-
fieldValue = field.flatMap({ $0.defaultValue.map({ .string($0) }) })
155+
fieldValue = field.flatMap({ $0.defaultValue })
152156
} else {
153157
objCopy[fieldName] = fieldValue
154158
}

Sources/GraphQL/Map/Map.swift

+32-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public enum MapError : Error {
1111
// MARK: Map
1212

1313
public enum Map {
14+
case undefined
1415
case null
1516
case bool(Bool)
1617
case number(Number)
@@ -165,6 +166,13 @@ extension Map {
165166
// MARK: is<Type>
166167

167168
extension Map {
169+
public var isUndefined: Bool {
170+
if case .undefined = self {
171+
return true
172+
}
173+
return false
174+
}
175+
168176
public var isNull: Bool {
169177
if case .null = self {
170178
return true
@@ -206,6 +214,8 @@ extension Map {
206214
extension Map {
207215
public var typeDescription: String {
208216
switch self {
217+
case .undefined:
218+
return "undefined"
209219
case .null:
210220
return "null"
211221
case .bool:
@@ -259,6 +269,9 @@ extension Map {
259269
}
260270

261271
switch self {
272+
case .undefined:
273+
return false
274+
262275
case .null:
263276
return false
264277

@@ -337,6 +350,9 @@ extension Map {
337350
}
338351

339352
switch self {
353+
case .undefined:
354+
return "undefined"
355+
340356
case .null:
341357
return "null"
342358

@@ -645,6 +661,8 @@ extension Map : Codable {
645661
var container = encoder.singleValueContainer()
646662

647663
switch self {
664+
case .undefined:
665+
fatalError("undefined values should have been excluded from encoding")
648666
case .null:
649667
try container.encodeNil()
650668
case let .bool(value):
@@ -660,8 +678,10 @@ extension Map : Codable {
660678
// Instead decode as a keyed container (like normal Dictionary) in the order of our OrderedDictionary
661679
var container = encoder.container(keyedBy: _DictionaryCodingKey.self)
662680
for (key, value) in dictionary {
663-
let codingKey = _DictionaryCodingKey(stringValue: key)!
664-
try container.encode(value, forKey: codingKey)
681+
if !value.isUndefined {
682+
let codingKey = _DictionaryCodingKey(stringValue: key)!
683+
try container.encode(value, forKey: codingKey)
684+
}
665685
}
666686
}
667687
}
@@ -711,6 +731,8 @@ public func == (lhs: Map, rhs: Map) -> Bool {
711731
extension Map : Hashable {
712732
public func hash(into hasher: inout Hasher) {
713733
switch self {
734+
case .undefined:
735+
hasher.combine(0)
714736
case .null:
715737
hasher.combine(0)
716738
case let .bool(value):
@@ -837,6 +859,8 @@ extension Map {
837859

838860
func serialize(map: Map) -> String {
839861
switch map {
862+
case .undefined:
863+
return "undefined"
840864
case .null:
841865
return "null"
842866
case let .bool(value):
@@ -891,8 +915,12 @@ extension Map {
891915
if debug {
892916
indentLevel += 1
893917
}
918+
919+
let filtered = dictionary.filter({ item in
920+
!item.value.isUndefined
921+
})
894922

895-
for (key, value) in dictionary.sorted(by: {$0.0 < $1.0}) {
923+
for (key, value) in filtered.sorted(by: {$0.0 < $1.0}) {
896924
if debug {
897925
string += "\n"
898926
string += indent()
@@ -901,7 +929,7 @@ extension Map {
901929
string += escape(key) + ":" + serialize(map: value)
902930
}
903931

904-
if index != dictionary.count - 1 {
932+
if index != filtered.count - 1 {
905933
if debug {
906934
string += ", "
907935
} else {

Sources/GraphQL/Map/MapSerialization.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public struct MapSerialization {
3434

3535
static func object(with map: Map) throws -> NSObject {
3636
switch map {
37+
case .undefined:
38+
fatalError("undefined values should have been excluded from serialization")
3739
case .null:
3840
return NSNull()
3941
case let .bool(value):
@@ -48,7 +50,9 @@ public struct MapSerialization {
4850
// Coerce to an unordered dictionary
4951
var unorderedDictionary: [String: NSObject] = [:]
5052
for (key, value) in dictionary {
51-
try unorderedDictionary[key] = object(with: value)
53+
if !value.isUndefined {
54+
try unorderedDictionary[key] = object(with: value)
55+
}
5256
}
5357
return unorderedDictionary as NSDictionary
5458
}

Sources/GraphQL/Type/Definition.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -1274,12 +1274,12 @@ func defineInputObjectFieldMap(
12741274

12751275
public struct InputObjectField {
12761276
public let type: GraphQLInputType
1277-
public let defaultValue: String?
1277+
public let defaultValue: Map?
12781278
public let description: String?
12791279

12801280
public init(type: GraphQLInputType, defaultValue: Map? = nil, description: String? = nil) {
12811281
self.type = type
1282-
self.defaultValue = defaultValue?.description
1282+
self.defaultValue = defaultValue
12831283
self.description = description
12841284
}
12851285
}
@@ -1290,13 +1290,13 @@ public final class InputObjectFieldDefinition {
12901290
public let name: String
12911291
public internal(set) var type: GraphQLInputType
12921292
public let description: String?
1293-
public let defaultValue: String?
1293+
public let defaultValue: Map?
12941294

12951295
init(
12961296
name: String,
12971297
type: GraphQLInputType,
12981298
description: String? = nil,
1299-
defaultValue: String? = nil
1299+
defaultValue: Map? = nil
13001300
) {
13011301
self.name = name
13021302
self.type = type

Sources/GraphQL/Utilities/ValueFromAST.swift

+26-29
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,14 @@ import OrderedCollections
1717
* | Enum Value | .string |
1818
*
1919
*/
20-
func valueFromAST(valueAST: Value?, type: GraphQLInputType, variables: [String: Map] = [:]) throws -> Map? {
20+
func valueFromAST(valueAST: Value, type: GraphQLInputType, variables: [String: Map] = [:]) throws -> Map {
2121
if let nonNullType = type as? GraphQLNonNull {
2222
// Note: we're not checking that the result of valueFromAST is non-null.
2323
// We're assuming that this query has been validated and the value used
2424
// here is of the correct type.
2525
return try valueFromAST(valueAST: valueAST, type: nonNullType.ofType as! GraphQLInputType, variables: variables)
2626
}
2727

28-
guard let valueAST = valueAST else {
29-
return nil
30-
}
31-
3228
if let variable = valueAST as? Variable {
3329
let variableName = variable.name.value
3430

@@ -38,7 +34,11 @@ func valueFromAST(valueAST: Value?, type: GraphQLInputType, variables: [String:
3834
// Note: we're not doing any checking that this variable is correct. We're
3935
// assuming that this query has been validated and the variable usage here
4036
// is of the correct type.
41-
return variables[variableName]
37+
if let variable = variables[variableName] {
38+
return variable
39+
} else {
40+
return .null
41+
}
4242
}
4343

4444
if let list = type as? GraphQLList {
@@ -50,36 +50,38 @@ func valueFromAST(valueAST: Value?, type: GraphQLInputType, variables: [String:
5050
valueAST: $0,
5151
type: itemType as! GraphQLInputType,
5252
variables: variables
53-
)!
53+
)
5454
}))
5555
}
5656

57-
return try [valueFromAST(valueAST: valueAST, type: itemType as! GraphQLInputType, variables: variables)!]
57+
return try [valueFromAST(valueAST: valueAST, type: itemType as! GraphQLInputType, variables: variables)]
5858
}
5959

6060
if let objectType = type as? GraphQLInputObjectType {
6161
guard let objectValue = valueAST as? ObjectValue else {
62-
return nil
62+
throw GraphQLError(message: "Must be object type")
6363
}
6464

6565
let fields = objectType.fields
66-
6766
let fieldASTs = objectValue.fields.keyMap({ $0.name.value })
6867

69-
return try .dictionary(fields.keys.reduce([:] as OrderedDictionary<String, Map>) { obj, fieldName in
68+
return try .dictionary(fields.keys.reduce(OrderedDictionary<String, Map>()) { obj, fieldName in
7069
var obj = obj
71-
let field = fields[fieldName]
72-
let fieldAST = fieldASTs[fieldName]
73-
var fieldValue = try valueFromAST(
74-
valueAST: fieldAST?.value,
75-
type: field!.type,
76-
variables: variables
77-
)
78-
79-
if fieldValue == .null {
80-
fieldValue = field.flatMap({ $0.defaultValue.map({ .string($0) }) })
81-
} else {
70+
let field = fields[fieldName]!
71+
if let fieldAST = fieldASTs[fieldName] {
72+
let fieldValue = try valueFromAST(
73+
valueAST: fieldAST.value,
74+
type: field.type,
75+
variables: variables
76+
)
8277
obj[fieldName] = fieldValue
78+
} else {
79+
// If AST doesn't contain field, it is undefined
80+
if let defaultValue = field.defaultValue {
81+
obj[fieldName] = defaultValue
82+
} else {
83+
obj[fieldName] = .undefined
84+
}
8385
}
8486

8587
return obj
@@ -90,11 +92,6 @@ func valueFromAST(valueAST: Value?, type: GraphQLInputType, variables: [String:
9092
throw GraphQLError(message: "Must be leaf type")
9193
}
9294

93-
let parsed = try type.parseLiteral(valueAST: valueAST)
94-
95-
guard parsed != .null else {
96-
return nil
97-
}
98-
99-
return parsed
95+
// If we've made it this far, it should be a literal
96+
return try type.parseLiteral(valueAST: valueAST)
10097
}

0 commit comments

Comments
 (0)