1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace GraphQL\Tests\Type\ValidatedFieldDefinition; |
6
|
|
|
|
7
|
|
|
use GraphQL\GraphQL; |
8
|
|
|
use GraphQL\Tests\Utils; |
9
|
|
|
use GraphQL\Type\Definition\InputObjectType; |
10
|
|
|
use GraphQL\Type\Definition\ObjectType; |
11
|
|
|
use GraphQL\Type\Definition\Type; |
12
|
|
|
use GraphQL\Type\Definition\UserErrorsType; |
13
|
|
|
use GraphQL\Type\Definition\ValidatedFieldDefinition; |
14
|
|
|
use GraphQL\Type\Schema; |
15
|
|
|
use PHPUnit\Framework\TestCase; |
16
|
|
|
use function strlen; |
17
|
|
|
|
18
|
|
|
final class InputObjectValidationTest extends TestCase |
19
|
|
|
{ |
20
|
|
|
/** @var Type */ |
21
|
|
|
protected $bookType; |
22
|
|
|
|
23
|
|
|
/** @var InputObjectType */ |
24
|
|
|
protected $bookAttributesInputType; |
25
|
|
|
|
26
|
|
|
/** @var Type */ |
27
|
|
|
protected $personType; |
28
|
|
|
|
29
|
|
|
/** @var mixed[] */ |
30
|
|
|
protected $data = [ |
31
|
|
|
'people' => [ |
32
|
|
|
1 => ['firstName' => 'Wilson'], |
33
|
|
|
2 => ['firstName' => 'J.D.'], |
34
|
|
|
3 => ['firstName' => 'Diana'], |
35
|
|
|
], |
36
|
|
|
]; |
37
|
|
|
|
38
|
|
|
/** @var ObjectType */ |
39
|
|
|
protected $query; |
40
|
|
|
|
41
|
|
|
/** @var Schema */ |
42
|
|
|
protected $schema; |
43
|
|
|
|
44
|
|
|
protected function setUp() |
45
|
|
|
{ |
46
|
|
|
$this->personType = new ObjectType([ |
47
|
|
|
'name' => 'Person', |
48
|
|
|
'fields' => [ |
49
|
|
|
'firstName' => [ |
50
|
|
|
'type' => Type::string(), |
51
|
|
|
'phoneNumbers' => [ |
52
|
|
|
'type' => Type::listOf(Type::string()), |
53
|
|
|
], |
54
|
|
|
], |
55
|
|
|
], |
56
|
|
|
]); |
57
|
|
|
|
58
|
|
|
$this->bookAttributesInputType = new InputObjectType([ |
59
|
|
|
'name' => 'BookAttributes', |
60
|
|
|
'fields' => [ |
61
|
|
|
'title' => [ |
62
|
|
|
'type' => Type::string(), |
63
|
|
|
'description' => 'Enter a book title, no more than 10 characters in length', |
64
|
|
|
'validate' => static function (string $title) { |
65
|
|
|
if (strlen($title) > 10) { |
66
|
|
|
return [1, 'book title must be less than 10 chaacters']; |
67
|
|
|
} |
68
|
|
|
return 0; |
69
|
|
|
}, |
70
|
|
|
], |
71
|
|
|
'author' => [ |
72
|
|
|
'type' => Type::id(), |
73
|
|
|
'description' => 'Provide a valid author id', |
74
|
|
|
'errorCodes' => [ |
75
|
|
|
'unknownAuthor', |
76
|
|
|
'authorDeceased', |
77
|
|
|
], |
78
|
|
|
'validate' => function (string $authorId) { |
79
|
|
|
if (! isset($this->data['people'][$authorId])) { |
80
|
|
|
return ['unknownAuthor', 'We have no record of that author']; |
81
|
|
|
} |
82
|
|
|
return 0; |
83
|
|
|
}, |
84
|
|
|
], |
85
|
|
|
], |
86
|
|
|
]); |
87
|
|
|
|
88
|
|
|
$this->query = new ObjectType(['name' => 'Query']); |
89
|
|
|
|
90
|
|
|
$this->schema = new Schema([ |
91
|
|
|
'query' => $this->query, |
92
|
|
|
'mutation' => new ObjectType([ |
93
|
|
|
'name' => 'Mutation', |
94
|
|
|
'fields' => function () { |
95
|
|
|
return [ |
96
|
|
|
'updateBook' => new ValidatedFieldDefinition([ |
97
|
|
|
'name' => 'updateBook', |
98
|
|
|
'type' => Type::boolean(), |
99
|
|
|
'args' => [ |
100
|
|
|
'bookAttributes' => [ |
101
|
|
|
'type' => $this->bookAttributesInputType, |
102
|
|
|
'errorCodes' => ['titleOrIdRequired'], |
103
|
|
|
'validate' => static function (?array $bookAttributes) { |
104
|
|
|
if (!$bookAttributes) { |
|
|
|
|
105
|
|
|
return 0; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
return isset($bookAttributes['title']) || isset($bookAttributes['author']) ? 0 : [ |
109
|
|
|
'titleOrIdRequired', |
110
|
|
|
'You must supply at least one of title or author', |
111
|
|
|
]; |
112
|
|
|
}, |
113
|
|
|
], |
114
|
|
|
], |
115
|
|
|
'resolve' => static function ($value, $args) : bool { |
|
|
|
|
116
|
|
|
// ... |
117
|
|
|
// do update |
118
|
|
|
// ... |
119
|
|
|
|
120
|
|
|
return ! $value; |
121
|
|
|
}, |
122
|
|
|
]), |
123
|
|
|
]; |
124
|
|
|
}, |
125
|
|
|
]), |
126
|
|
|
]); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
public function testValidationInputObjectFieldFail() |
130
|
|
|
{ |
131
|
|
|
$res = GraphQL::executeQuery( |
132
|
|
|
$this->schema, |
133
|
|
|
Utils::nowdoc(' |
134
|
|
|
mutation UpdateBook( |
135
|
|
|
$bookAttributes: BookAttributes |
136
|
|
|
) { |
137
|
|
|
updateBook ( |
138
|
|
|
bookAttributes: $bookAttributes |
139
|
|
|
) { |
140
|
|
|
valid |
141
|
|
|
suberrors { |
142
|
|
|
bookAttributes { |
143
|
|
|
suberrors { |
144
|
|
|
title { |
145
|
|
|
code |
146
|
|
|
msg |
147
|
|
|
} |
148
|
|
|
author { |
149
|
|
|
code |
150
|
|
|
msg |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
result |
156
|
|
|
} |
157
|
|
|
} |
158
|
|
|
'), |
159
|
|
|
[], |
160
|
|
|
null, |
161
|
|
|
[ |
162
|
|
|
'bookAttributes' => [ |
163
|
|
|
'title' => 'The Catcher in the Rye', |
164
|
|
|
'author' => 4, |
165
|
|
|
], |
166
|
|
|
] |
167
|
|
|
); |
168
|
|
|
|
169
|
|
|
static::assertEquals( |
170
|
|
|
[ |
171
|
|
|
'valid' => false, |
172
|
|
|
'suberrors' => |
173
|
|
|
[ |
174
|
|
|
'bookAttributes' => |
175
|
|
|
[ |
176
|
|
|
'suberrors' => |
177
|
|
|
[ |
178
|
|
|
'title' => |
179
|
|
|
[ |
180
|
|
|
'code' => 1, |
181
|
|
|
'msg' => 'book title must be less than 10 chaacters', |
182
|
|
|
], |
183
|
|
|
'author' => |
184
|
|
|
[ |
185
|
|
|
'code' => 'unknownAuthor', |
186
|
|
|
'msg' => 'We have no record of that author', |
187
|
|
|
], |
188
|
|
|
], |
189
|
|
|
], |
190
|
|
|
], |
191
|
|
|
'result' => null, |
192
|
|
|
], |
193
|
|
|
$res->data['updateBook'] |
194
|
|
|
); |
195
|
|
|
|
196
|
|
|
static::assertFalse($res->data['updateBook']['valid']); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
public function testValidationInputObjectSelfFail() |
200
|
|
|
{ |
201
|
|
|
$res = GraphQL::executeQuery( |
202
|
|
|
$this->schema, |
203
|
|
|
Utils::nowdoc(' |
204
|
|
|
mutation UpdateBook( |
205
|
|
|
$bookAttributes: BookAttributes |
206
|
|
|
) { |
207
|
|
|
updateBook ( |
208
|
|
|
bookAttributes: $bookAttributes |
209
|
|
|
) { |
210
|
|
|
valid |
211
|
|
|
suberrors { |
212
|
|
|
bookAttributes { |
213
|
|
|
code |
214
|
|
|
msg |
215
|
|
|
suberrors { |
216
|
|
|
title { |
217
|
|
|
code |
218
|
|
|
msg |
219
|
|
|
} |
220
|
|
|
author { |
221
|
|
|
code |
222
|
|
|
msg |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
result |
228
|
|
|
} |
229
|
|
|
} |
230
|
|
|
'), |
231
|
|
|
[], |
232
|
|
|
null, |
233
|
|
|
[ |
234
|
|
|
'bookAttributes' => [ |
235
|
|
|
'title' => null, |
236
|
|
|
'author' => null, |
237
|
|
|
], |
238
|
|
|
] |
239
|
|
|
); |
240
|
|
|
|
241
|
|
|
static::assertEquals( |
242
|
|
|
[ |
243
|
|
|
'valid' => false, |
244
|
|
|
'suberrors' => |
245
|
|
|
[ |
246
|
|
|
'bookAttributes' => |
247
|
|
|
[ |
248
|
|
|
'code' => 'titleOrIdRequired', |
249
|
|
|
'msg' => 'You must supply at least one of title or author', |
250
|
|
|
'suberrors' => null, |
251
|
|
|
], |
252
|
|
|
], |
253
|
|
|
'result' => null, |
254
|
|
|
], |
255
|
|
|
$res->data['updateBook'] |
256
|
|
|
); |
257
|
|
|
|
258
|
|
|
static::assertFalse($res->data['updateBook']['valid']); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
public function testValidationSuccess() |
262
|
|
|
{ |
263
|
|
|
$res = GraphQL::executeQuery( |
264
|
|
|
$this->schema, |
265
|
|
|
Utils::nowdoc(' |
266
|
|
|
mutation UpdateBook( |
267
|
|
|
$bookAttributes: BookAttributes |
268
|
|
|
) { |
269
|
|
|
updateBook ( |
270
|
|
|
bookAttributes: $bookAttributes |
271
|
|
|
) { |
272
|
|
|
valid |
273
|
|
|
suberrors { |
274
|
|
|
bookAttributes { |
275
|
|
|
suberrors { |
276
|
|
|
title { |
277
|
|
|
code |
278
|
|
|
msg |
279
|
|
|
} |
280
|
|
|
author { |
281
|
|
|
code |
282
|
|
|
msg |
283
|
|
|
} |
284
|
|
|
} |
285
|
|
|
} |
286
|
|
|
} |
287
|
|
|
result |
288
|
|
|
} |
289
|
|
|
} |
290
|
|
|
'), |
291
|
|
|
[], |
292
|
|
|
null, |
293
|
|
|
[ |
294
|
|
|
'bookAttributes' => [ |
295
|
|
|
'title' => 'Dogsbody', |
296
|
|
|
'author' => 3, |
297
|
|
|
], |
298
|
|
|
] |
299
|
|
|
); |
300
|
|
|
|
301
|
|
|
static::assertEmpty($res->errors); |
302
|
|
|
static::assertEquals( |
303
|
|
|
[ |
304
|
|
|
'valid' => true, |
305
|
|
|
'suberrors' => null, |
306
|
|
|
'result' => true, |
307
|
|
|
], |
308
|
|
|
$res->data['updateBook'] |
309
|
|
|
); |
310
|
|
|
|
311
|
|
|
static::assertTrue($res->data['updateBook']['valid']); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
public function testValidationEmptyInput() |
315
|
|
|
{ |
316
|
|
|
$res = GraphQL::executeQuery( |
317
|
|
|
$this->schema, |
318
|
|
|
Utils::nowdoc(' |
319
|
|
|
mutation UpdateBook( |
320
|
|
|
$bookAttributes: BookAttributes |
321
|
|
|
) { |
322
|
|
|
updateBook ( |
323
|
|
|
bookAttributes: $bookAttributes |
324
|
|
|
) { |
325
|
|
|
valid |
326
|
|
|
suberrors { |
327
|
|
|
bookAttributes { |
328
|
|
|
suberrors { |
329
|
|
|
title { |
330
|
|
|
code |
331
|
|
|
msg |
332
|
|
|
} |
333
|
|
|
author { |
334
|
|
|
code |
335
|
|
|
msg |
336
|
|
|
} |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
} |
340
|
|
|
result |
341
|
|
|
} |
342
|
|
|
} |
343
|
|
|
'), |
344
|
|
|
[], |
345
|
|
|
null, |
346
|
|
|
['bookAttributes' => null] |
347
|
|
|
); |
348
|
|
|
|
349
|
|
|
static::assertEmpty($res->errors); |
350
|
|
|
static::assertEquals( |
351
|
|
|
[ |
352
|
|
|
'valid' => true, |
353
|
|
|
'suberrors' => null, |
354
|
|
|
'result' => true, |
355
|
|
|
], |
356
|
|
|
$res->data['updateBook'] |
357
|
|
|
); |
358
|
|
|
|
359
|
|
|
static::assertTrue($res->data['updateBook']['valid']); |
360
|
|
|
} |
361
|
|
|
} |
362
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.