1
1
import Foundation
2
+ import OrderedCollections
2
3
3
4
/**
4
5
* Prepares an object map of variableValues of the correct type based on the
5
6
* provided variable definitions and arbitrary input. If the input cannot be
6
7
* parsed to match the variable definitions, a GraphQLError will be thrown.
7
8
*/
8
9
func getVariableValues( schema: GraphQLSchema , definitionASTs: [ VariableDefinition ] , inputs: [ String : Map ] ) throws -> [ String : Map ] {
9
- return try definitionASTs. reduce ( [ : ] ) { values, defAST in
10
- var valuesCopy = values
10
+
11
+ var vars = [ String: Map] ( )
12
+ for defAST in definitionASTs {
11
13
let varName = defAST. variable. name. value
12
-
13
- valuesCopy [ varName] = try getVariableValue (
14
+
15
+ let input : Map
16
+ if let nonNilInput = inputs [ varName] {
17
+ input = nonNilInput
18
+ } else {
19
+ // If variable is not in inputs it is undefined
20
+ input = . undefined
21
+ }
22
+ vars [ varName] = try getVariableValue (
14
23
schema: schema,
15
24
definitionAST: defAST,
16
- input: inputs [ varName ] ?? . null
25
+ input: input
17
26
)
18
-
19
- return valuesCopy
20
27
}
28
+ return vars
21
29
}
22
30
23
31
24
32
/**
25
33
* Prepares an object map of argument values given a list of argument
26
34
* definitions and list of argument AST nodes.
27
35
*/
28
- func getArgumentValues( argDefs: [ GraphQLArgumentDefinition ] , argASTs: [ Argument ] ? , variableValues : [ String : Map ] = [ : ] ) throws -> Map {
36
+ func getArgumentValues( argDefs: [ GraphQLArgumentDefinition ] , argASTs: [ Argument ] ? , variables : [ String : Map ] = [ : ] ) throws -> Map {
29
37
guard let argASTs = argASTs else {
30
38
return [ : ]
31
39
}
32
40
33
41
let argASTMap = argASTs. keyMap ( { $0. name. value } )
34
-
35
- return try argDefs . reduce ( [ : ] ) { result , argDef in
36
- var result = result
37
- let name = argDef. name
38
- let argAST = argASTMap [ name ]
42
+
43
+ var args = OrderedDictionary < String , Map > ( )
44
+ for argDef in argDefs {
45
+ let argName = argDef. name
46
+ let argValue : Map
39
47
40
- if let argAST = argAST {
41
- let valueAST = argAST. value
42
-
43
- let value = try valueFromAST (
44
- valueAST: valueAST,
48
+ if let argAST = argASTMap [ argName] {
49
+ argValue = try valueFromAST (
50
+ valueAST: argAST. value,
45
51
type: argDef. type,
46
- variables: variableValues
52
+ variables: variables
47
53
)
48
-
49
- result [ name] = value
50
54
} else {
51
- result [ name] = . null
55
+ // If AST doesn't contain field, it is undefined
56
+ if let defaultValue = argDef. defaultValue {
57
+ argValue = defaultValue
58
+ } else {
59
+ argValue = . undefined
60
+ }
52
61
}
53
-
54
- return result
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
55
72
}
73
+ return . dictionary( args)
56
74
}
57
75
58
76
@@ -64,112 +82,95 @@ func getVariableValue(schema: GraphQLSchema, definitionAST: VariableDefinition,
64
82
let type = typeFromAST ( schema: schema, inputTypeAST: definitionAST. type)
65
83
let variable = definitionAST. variable
66
84
67
- if type == nil || !isInputType ( type : type ) {
85
+ guard let inputType = type as? GraphQLInputType else {
68
86
throw GraphQLError (
69
87
message:
70
88
" Variable \" $ \( variable. name. value) \" expected value of type " +
71
89
" \" \( definitionAST. type) \" which cannot be used as an input type. " ,
72
90
nodes: [ definitionAST]
73
91
)
74
92
}
75
-
76
- let inputType = type as! GraphQLInputType
77
- let errors = try isValidValue ( value: input, type: inputType)
78
-
79
- if errors. isEmpty {
80
- if input == . null {
81
- if let defaultValue = definitionAST. defaultValue {
82
- return try valueFromAST ( valueAST: defaultValue, type: inputType)
83
- }
84
- else if !( inputType is GraphQLNonNull ) {
85
- return . null
86
- }
87
- }
88
-
89
- return try coerceValue ( type: inputType, value: input) !
93
+
94
+ var toCoerce = input
95
+ if input == . undefined, let defaultValue = definitionAST. defaultValue {
96
+ toCoerce = try valueFromAST ( valueAST: defaultValue, type: inputType)
90
97
}
91
-
92
- guard input != . null else {
98
+
99
+ let errors = try validate ( value: toCoerce, forType: inputType)
100
+ guard errors. isEmpty else {
101
+ let message = !errors. isEmpty ? " \n " + errors. joined ( separator: " \n " ) : " "
93
102
throw GraphQLError (
94
103
message:
95
- " Variable \" $ \( variable. name. value) \" of required type " +
96
- " \" \( definitionAST. type) \" was not provided. " ,
104
+ " Variable \" $ \( variable. name. value) \" got invalid value \" \( toCoerce) \" . \( message) " , // TODO: "\(JSON.stringify(input)).\(message)",
97
105
nodes: [ definitionAST]
98
106
)
99
107
}
100
-
101
- let message = !errors. isEmpty ? " \n " + errors. joined ( separator: " \n " ) : " "
102
-
103
- throw GraphQLError (
104
- message:
105
- " Variable \" $ \( variable. name. value) \" got invalid value " +
106
- " \( input) . \( message) " , // TODO: "\(JSON.stringify(input)).\(message)",
107
- nodes: [ definitionAST]
108
- )
108
+
109
+ return try coerceValue ( value: toCoerce, type: inputType)
109
110
}
110
111
111
112
/**
112
113
* Given a type and any value, return a runtime value coerced to match the type.
113
114
*/
114
- func coerceValue( type : GraphQLInputType , value : Map ) throws -> Map ? {
115
+ func coerceValue( value : Map , type : GraphQLInputType ) throws -> Map {
115
116
if let nonNull = type as? GraphQLNonNull {
116
117
// Note: we're not checking that the result of coerceValue is non-null.
117
- // We only call this function after calling isValidValue.
118
- return try coerceValue ( type: nonNull. ofType as! GraphQLInputType , value: value) !
118
+ // We only call this function after calling validate.
119
+ guard let nonNullType = nonNull. ofType as? GraphQLInputType else {
120
+ throw GraphQLError ( message: " NonNull must wrap an input type " )
121
+ }
122
+ return try coerceValue ( value: value, type: nonNullType)
119
123
}
120
-
121
- guard value != . null else {
122
- return nil
124
+
125
+ guard value != . undefined && value != . null else {
126
+ return value
123
127
}
124
128
125
129
if let list = type as? GraphQLList {
126
- let itemType = list. ofType
130
+ guard let itemType = list. ofType as? GraphQLInputType else {
131
+ throw GraphQLError ( message: " Input list must wrap an input type " )
132
+ }
127
133
128
134
if case . array( let value) = value {
129
- var coercedValues : [ Map ] = [ ]
130
-
131
- for item in value {
132
- coercedValues. append ( try coerceValue ( type: itemType as! GraphQLInputType , value: item) !)
135
+ let coercedValues = try value. map { item in
136
+ try coerceValue ( value: item, type: itemType)
133
137
}
134
-
135
138
return . array( coercedValues)
136
139
}
137
-
138
- return . array( [ try coerceValue ( type: itemType as! GraphQLInputType , value: value) !] )
140
+
141
+ // Convert solitary value into single-value array
142
+ return . array( [ try coerceValue ( value: value, type: itemType) ] )
139
143
}
140
144
141
- if let type = type as? GraphQLInputObjectType {
145
+ if let objectType = type as? GraphQLInputObjectType {
142
146
guard case . dictionary( let value) = value else {
143
- return nil
147
+ throw GraphQLError ( message : " Must be dictionary to extract to an input type " )
144
148
}
145
149
146
- let fields = type. fields
147
-
148
- return try . dictionary( fields. keys. reduce ( [ : ] ) { obj, fieldName in
149
- var objCopy = obj
150
- let field = fields [ fieldName]
151
-
152
- var fieldValue = try coerceValue ( type: field!. type, value: value [ fieldName] ?? . null)
153
-
154
- if fieldValue == . null {
155
- fieldValue = field. flatMap ( { $0. defaultValue } )
150
+ let fields = objectType. fields
151
+
152
+ var object = OrderedDictionary < String , Map > ( )
153
+ for (fieldName, field) in fields {
154
+ if let fieldValueMap = value [ fieldName] , fieldValueMap != . undefined {
155
+ object [ fieldName] = try coerceValue (
156
+ value: fieldValueMap,
157
+ type: field. type
158
+ )
156
159
} else {
157
- objCopy [ fieldName] = fieldValue
160
+ // If AST doesn't contain field, it is undefined
161
+ if let defaultValue = field. defaultValue {
162
+ object [ fieldName] = defaultValue
163
+ } else {
164
+ object [ fieldName] = . undefined
165
+ }
158
166
}
159
-
160
- return objCopy
161
- } )
167
+ }
168
+ return . dictionary( object)
162
169
}
163
170
164
- guard let type = type as? GraphQLLeafType else {
165
- throw GraphQLError ( message : " Must be input type " )
171
+ if let leafType = type as? GraphQLLeafType {
172
+ return try leafType . parseValue ( value : value )
166
173
}
167
174
168
- let parsed = try type. parseValue ( value: value)
169
-
170
- guard parsed != . null else {
171
- return nil
172
- }
173
-
174
- return parsed
175
+ throw GraphQLError ( message: " Provided type is not an input type " )
175
176
}
0 commit comments