Completed
Pull Request — master (#153)
by Portey
04:20
created

Processor::processPayload()   B

Complexity

Conditions 6
Paths 46

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 14
cts 14
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 15
nc 46
nop 3
crap 6
1
<?php
2
/**
3
 * Date: 03.11.16
4
 *
5
 * @author Portey Vasil <[email protected]>
6
 */
7
8
namespace Youshido\GraphQL\Execution;
9
10
11
use Youshido\GraphQL\Exception\ResolveException;
12
use Youshido\GraphQL\Execution\Container\Container;
13
use Youshido\GraphQL\Execution\Context\ExecutionContext;
14
use Youshido\GraphQL\Execution\Visitor\MaxComplexityQueryVisitor;
15
use Youshido\GraphQL\Field\Field;
16
use Youshido\GraphQL\Field\FieldInterface;
17
use Youshido\GraphQL\Field\InputField;
18
use Youshido\GraphQL\Parser\Ast\ArgumentValue\InputList as AstInputList;
19
use Youshido\GraphQL\Parser\Ast\ArgumentValue\InputObject as AstInputObject;
20
use Youshido\GraphQL\Parser\Ast\ArgumentValue\Literal as AstLiteral;
21
use Youshido\GraphQL\Parser\Ast\ArgumentValue\VariableReference;
22
use Youshido\GraphQL\Parser\Ast\Field as AstField;
23
use Youshido\GraphQL\Parser\Ast\FragmentReference;
24
use Youshido\GraphQL\Parser\Ast\Interfaces\FieldInterface as AstFieldInterface;
25
use Youshido\GraphQL\Parser\Ast\Mutation as AstMutation;
26
use Youshido\GraphQL\Parser\Ast\Query as AstQuery;
27
use Youshido\GraphQL\Parser\Ast\Query;
28
use Youshido\GraphQL\Parser\Ast\TypedFragmentReference;
29
use Youshido\GraphQL\Parser\Parser;
30
use Youshido\GraphQL\Schema\AbstractSchema;
31
use Youshido\GraphQL\Type\AbstractType;
32
use Youshido\GraphQL\Type\InputObject\AbstractInputObjectType;
33
use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType;
34
use Youshido\GraphQL\Type\ListType\AbstractListType;
35
use Youshido\GraphQL\Type\Object\AbstractObjectType;
36
use Youshido\GraphQL\Type\Scalar\AbstractScalarType;
37
use Youshido\GraphQL\Type\TypeMap;
38
use Youshido\GraphQL\Type\Union\AbstractUnionType;
39
use Youshido\GraphQL\Validator\RequestValidator\RequestValidator;
40
use Youshido\GraphQL\Validator\ResolveValidator\ResolveValidator;
41
use Youshido\GraphQL\Validator\ResolveValidator\ResolveValidatorInterface;
42
43
class Processor
44
{
45
46
    const TYPE_NAME_QUERY = '__typename';
47
48
    /** @var ExecutionContext */
49
    protected $executionContext;
50
51
    /** @var ResolveValidatorInterface */
52
    protected $resolveValidator;
53
54
    /** @var  array */
55
    protected $data;
56
57
    /** @var int */
58
    protected $maxComplexity;
59
60 62
    public function __construct(AbstractSchema $schema)
61
    {
62 62
        if (empty($this->executionContext)) {
63 62
            $this->executionContext = new ExecutionContext($schema);
64 62
            $this->executionContext->setContainer(new Container());
65
        }
66
67 62
        $this->resolveValidator = new ResolveValidator($this->executionContext);
0 ignored issues
show
Unused Code introduced by
The call to ResolveValidator::__construct() has too many arguments starting with $this->executionContext.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
68 62
    }
69
70 60
    public function processPayload($payload, $variables = [], $reducers = [])
71
    {
72 60
        $this->data = [];
73
74
        try {
75 60
            $this->parseAndCreateRequest($payload, $variables);
76
77 59
            if ($this->maxComplexity) {
78 1
                $reducers[] = new MaxComplexityQueryVisitor($this->maxComplexity);
79
            }
80
81 59
            if ($reducers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $reducers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
82 2
                $reducer = new Reducer();
83 2
                $reducer->reduceQuery($this->executionContext, $reducers);
84
            }
85
86 59
            foreach ($this->executionContext->getRequest()->getAllOperations() as $query) {
87 59
                if ($operationResult = $this->resolveQuery($query)) {
88 59
                    $this->data = array_merge($this->data, $operationResult);
89
                };
90
            }
91 5
        } catch (\Exception $e) {
92 5
            $this->executionContext->addError($e);
93
        }
94
95 60
        return $this;
96
    }
97
98 59
    protected function resolveQuery(AstQuery $query)
99
    {
100 59
        $schema = $this->executionContext->getSchema();
101 59
        $type   = $query instanceof AstMutation ? $schema->getMutationType() : $schema->getQueryType();
102 59
        $field  = new Field([
103 59
            'name' => $query instanceof AstMutation ? 'mutation' : 'query',
104 59
            'type' => $type
105
        ]);
106
107 59
        if (self::TYPE_NAME_QUERY == $query->getName()) {
108 1
            return [$this->getAlias($query) => $type->getName()];
109
        }
110
111 59
        $this->resolveValidator->assetTypeHasField($type, $query);
112 59
        $value = $this->resolveField($field, $query);
113
114 59
        return [$this->getAlias($query) => $value];
115
    }
116
117 59
    protected function resolveField(FieldInterface $field, AstFieldInterface $ast, $parentValue = null, $fromObject = false)
118
    {
119
        try {
120
            /** @var AbstractObjectType $type */
121 59
            $type        = $field->getType();
122 59
            $nonNullType = $type->getNullableType();
123
124 59
            if (self::TYPE_NAME_QUERY == $ast->getName()) {
125 2
                return $nonNullType->getName();
126
            }
127
128 59
            $this->resolveValidator->assetTypeHasField($nonNullType, $ast);
129
130 59
            $targetField = $nonNullType->getField($ast->getName());
131
132 59
            $this->prepareAstArguments($targetField, $ast, $this->executionContext->getRequest());
133 58
            $this->resolveValidator->assertValidArguments($targetField, $ast, $this->executionContext->getRequest());
134
135 53
            switch ($kind = $targetField->getType()->getNullableType()->getKind()) {
136 53
                case TypeMap::KIND_ENUM:
137 52
                case TypeMap::KIND_SCALAR:
138 46
                    if ($ast instanceof AstQuery && $ast->hasFields()) {
139 2
                        throw new ResolveException(sprintf('You can\'t specify fields for scalar type "%s"', $targetField->getType()->getNullableType()->getName()), $ast->getLocation());
140
                    }
141
142 46
                    return $this->resolveScalar($targetField, $ast, $parentValue);
143
144 36 View Code Duplication
                case TypeMap::KIND_OBJECT:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145
                    /** @var $type AbstractObjectType */
146 27
                    if (!$ast instanceof AstQuery) {
147 1
                        throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation());
148
                    }
149
150 27
                    return $this->resolveObject($targetField, $ast, $parentValue);
151
152 22
                case TypeMap::KIND_LIST:
153 19
                    return $this->resolveList($targetField, $ast, $parentValue);
154
155 6
                case TypeMap::KIND_UNION:
156 5 View Code Duplication
                case TypeMap::KIND_INTERFACE:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
157 6
                    if (!$ast instanceof AstQuery) {
158
                        throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation());
159
                    }
160
161 6
                    return $this->resolveComposite($targetField, $ast, $parentValue);
162
163
                default:
164
                    throw new ResolveException(sprintf('Resolving type with kind "%s" not supported', $kind));
165
            }
166 17
        } catch (\Exception $e) {
167 17
            $this->executionContext->addError($e);
168
169 17
            if ($fromObject) {
170 4
                throw $e;
171
            }
172
173 15
            return null;
174
        }
175
    }
176
177 59
    private function prepareAstArguments(FieldInterface $field, AstFieldInterface $query, Request $request)
178
    {
179 59
        foreach ($query->getArguments() as $astArgument) {
180 34
            if ($field->hasArgument($astArgument->getName())) {
181 34
                $argumentType = $field->getArgument($astArgument->getName())->getType()->getNullableType();
182
183 34
                $astArgument->setValue($this->prepareArgumentValue($astArgument->getValue(), $argumentType, $request));
184
            }
185
        }
186 58
    }
187
188 34
    private function prepareArgumentValue($argumentValue, AbstractType $argumentType, Request $request)
189
    {
190 34
        switch ($argumentType->getKind()) {
191 34
            case TypeMap::KIND_LIST:
192
                /** @var $argumentType AbstractListType */
193 8
                $result = [];
194 8
                if ($argumentValue instanceof AstInputList || is_array($argumentValue)) {
195 5
                    $list = is_array($argumentValue) ? $argumentValue : $argumentValue->getValue();
196 5
                    foreach ($list as $item) {
197 5
                        $result[] = $this->prepareArgumentValue($item, $argumentType->getItemType()->getNullableType(), $request);
198
                    }
199
                } else {
200 3
                    if ($argumentValue instanceof VariableReference) {
201 3
                        return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
202
                    }
203
                }
204
205 5
                return $result;
206
207 33
            case TypeMap::KIND_INPUT_OBJECT:
208
                /** @var $argumentType AbstractInputObjectType */
209 6
                $result = [];
210 6
                if ($argumentValue instanceof AstInputObject) {
211 5
                    foreach ($argumentType->getFields() as $field) {
212
                        /** @var $field Field */
213 5
                        if ($field->getConfig()->has('defaultValue')) {
214 5
                            $result[$field->getName()] = $field->getType()->getNullableType()->parseInputValue($field->getConfig()->get('defaultValue'));
215
                        }
216
                    }
217 5
                    foreach ($argumentValue->getValue() as $key => $item) {
218 5
                        if ($argumentType->hasField($key)) {
219 5
                            $result[$key] = $this->prepareArgumentValue($item, $argumentType->getField($key)->getType()->getNullableType(), $request);
220
                        } else {
221 5
                            $result[$key] = $item;
222
                        }
223
                    }
224
                } else {
225 2
                    if ($argumentValue instanceof VariableReference) {
226
                        return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
227
                    } else {
228 2
                        if (is_array($argumentValue)) {
229 1
                            return $argumentValue;
230
                        }
231
                    }
232
                }
233
234 6
                return $result;
235
236 32
            case TypeMap::KIND_SCALAR:
237 4
            case TypeMap::KIND_ENUM:
238
                /** @var $argumentValue AstLiteral|VariableReference */
239 32
                if ($argumentValue instanceof VariableReference) {
240 7
                    return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
241
                } else {
242 27
                    if ($argumentValue instanceof AstLiteral) {
243 19
                        return $argumentValue->getValue();
244
                    } else {
245 9
                        return $argumentValue;
246
                    }
247
                }
248
        }
249
250
        throw new ResolveException('Argument type not supported');
251
    }
252
253 9
    private function getVariableReferenceArgumentValue(VariableReference $variableReference, AbstractType $argumentType, Request $request)
254
    {
255 9
        $variable = $variableReference->getVariable();
256 9
        if ($argumentType->getKind() === TypeMap::KIND_LIST) {
257
            if (
258 3
                (!$variable->isArray() && !is_array($variable->getValue())) ||
259 3
                ($variable->getTypeName() !== $argumentType->getNamedType()->getNullableType()->getName()) ||
260 3
                ($argumentType->getNamedType()->getKind() === TypeMap::KIND_NON_NULL && $variable->isArrayElementNullable())
261
            ) {
262 3
                throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getNamedType()->getNullableType()->getName()), $variable->getLocation());
263
            }
264
        } else {
265 7
            if ($variable->getTypeName() !== $argumentType->getName()) {
266 1
                throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getName()), $variable->getLocation());
267
            }
268
        }
269
270 8
        $requestValue = $request->getVariable($variable->getName());
271 8
        if ((null === $requestValue && $variable->isNullable()) && !$request->hasVariable($variable->getName())) {
272
            throw new ResolveException(sprintf('Variable "%s" does not exist in request', $variable->getName()), $variable->getLocation());
273
        }
274
275 8
        return $requestValue;
276
    }
277
278 32
    protected function resolveObject(FieldInterface $field, AstFieldInterface $ast, $parentValue, $fromUnion = false)
279
    {
280 32
        $resolvedValue = $parentValue;
281 32
        if (!$fromUnion) {
282 27
            $resolvedValue = $this->doResolve($field, $ast, $parentValue);
283
        }
284
285 32
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
286
287 32
        if (null === $resolvedValue) {
288 5
            return null;
289
        }
290
        /** @var AbstractObjectType $type */
291 31
        $type = $field->getType()->getNullableType();
292
293
        try {
294 31
            return $this->collectResult($field, $type, $ast, $resolvedValue);
295 4
        } catch (\Exception $e) {
296 4
            return null;
297
        }
298
    }
299
300
    /**
301
     * @param FieldInterface     $field
302
     * @param AbstractObjectType $type
303
     * @param AstFieldInterface  $ast
304
     * @param                    $resolvedValue
305
     * @return array
306
     */
307 31
    private function collectResult(FieldInterface $field, AbstractObjectType $type, $ast, $resolvedValue)
308
    {
309 31
        $result = [];
310
311 31
        foreach ($ast->getFields() as $astField) {
312
            switch (true) {
313 31
                case $astField instanceof TypedFragmentReference:
314 2
                    $astName  = $astField->getTypeName();
315 2
                    $typeName = $type->getName();
316
317 2 View Code Duplication
                    if ($typeName !== $astName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
318 2
                        foreach ($type->getInterfaces() as $interface) {
319 1
                            if ($interface->getName() === $astName) {
320
                                $result = array_merge($result, $this->collectResult($field, $type, $astField, $resolvedValue));
0 ignored issues
show
Documentation introduced by
$astField is of type object<Youshido\GraphQL\...TypedFragmentReference>, but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
321
322 1
                                break;
323
                            }
324
                        }
325
326 2
                        continue;
327
                    }
328
329 2
                    $result = array_merge($result, $this->collectResult($field, $type, $astField, $resolvedValue));
0 ignored issues
show
Documentation introduced by
$astField is of type object<Youshido\GraphQL\...TypedFragmentReference>, but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
330
331 2
                    break;
332
333 31
                case $astField instanceof FragmentReference:
334 4
                    $astFragment      = $this->executionContext->getRequest()->getFragment($astField->getName());
335 4
                    $astFragmentModel = $astFragment->getModel();
336 4
                    $typeName         = $type->getName();
337
338 4 View Code Duplication
                    if ($typeName !== $astFragmentModel) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
339 1
                        foreach ($type->getInterfaces() as $interface) {
340 1
                            if ($interface->getName() === $astFragmentModel) {
341 1
                                $result = array_merge($result, $this->collectResult($field, $type, $astFragment, $resolvedValue));
0 ignored issues
show
Documentation introduced by
$astFragment is of type object<Youshido\GraphQL\Parser\Ast\Fragment>|null, but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
342
343 1
                                break;
344
                            }
345
346
                        }
347
348 1
                        continue;
349
                    }
350
351 4
                    $result = array_merge($result, $this->collectResult($field, $type, $astFragment, $resolvedValue));
0 ignored issues
show
Documentation introduced by
$astFragment is of type object<Youshido\GraphQL\Parser\Ast\Fragment>|null, but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
352
353 4
                    break;
354
355
                default:
356 31
                    $result[$this->getAlias($astField)] = $this->resolveField($field, $astField, $resolvedValue, true);
357
            }
358
        }
359
360 31
        return $result;
361
    }
362
363 47
    protected function resolveScalar(FieldInterface $field, AstFieldInterface $ast, $parentValue)
364
    {
365 47
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
366
367 47
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
368
369
        /** @var AbstractScalarType $type */
370 46
        $type = $field->getType()->getNullableType();
371
372 46
        return $type->serialize($resolvedValue);
373
    }
374
375 19
    protected function resolveList(FieldInterface $field, AstFieldInterface $ast, $parentValue)
376
    {
377
        /** @var AstQuery $ast */
378 19
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
379
380 19
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
381
382 17
        if (null === $resolvedValue) {
383 5
            return null;
384
        }
385
386
        /** @var AbstractListType $type */
387 16
        $type     = $field->getType()->getNullableType();
388 16
        $itemType = $type->getNamedType();
389
390 16
        $fakeAst = clone $ast;
391 16
        if ($fakeAst instanceof AstQuery) {
392 15
            $fakeAst->setArguments([]);
393
        }
394
395 16
        $fakeField = new Field([
396 16
            'name' => $field->getName(),
397 16
            'type' => $itemType,
398 16
            'args' => $field->getArguments(),
399
        ]);
400
401 16
        $result = [];
402 16
        foreach ($resolvedValue as $resolvedValueItem) {
403
            try {
404 15
                $fakeField->getConfig()->set('resolve', function () use ($resolvedValueItem) {
405 15
                    return $resolvedValueItem;
406 15
                });
407
408 15
                switch ($itemType->getNullableType()->getKind()) {
409 15
                    case TypeMap::KIND_ENUM:
410 14
                    case TypeMap::KIND_SCALAR:
411 5
                        $value = $this->resolveScalar($fakeField, $fakeAst, $resolvedValueItem);
412
413 4
                        break;
414
415
416 11
                    case TypeMap::KIND_OBJECT:
417 9
                        $value = $this->resolveObject($fakeField, $fakeAst, $resolvedValueItem);
418
419 9
                        break;
420
421 3
                    case TypeMap::KIND_UNION:
422 3
                    case TypeMap::KIND_INTERFACE:
423 3
                        $value = $this->resolveComposite($fakeField, $fakeAst, $resolvedValueItem);
424
425 3
                        break;
426
427
                    default:
428 14
                        $value = null;
429
                }
430 1
            } catch (\Exception $e) {
431 1
                $this->executionContext->addError($e);
432
433 1
                $value = null;
434
            }
435
436 15
            $result[] = $value;
437
        }
438
439 16
        return $result;
440
    }
441
442 7
    protected function resolveComposite(FieldInterface $field, AstFieldInterface $ast, $parentValue)
443
    {
444
        /** @var AstQuery $ast */
445 7
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
446
447 7
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
448
449 7
        if (null === $resolvedValue) {
450
            return null;
451
        }
452
453
        /** @var AbstractUnionType $type */
454 7
        $type         = $field->getType()->getNullableType();
455 7
        $resolveInfo = new ResolveInfo(
456
            $field,
457 7
            $ast instanceof AstQuery ? $ast->getFields() : [],
458 7
            $this->executionContext
459
        );
460 7
        $resolvedType = $type->resolveType($resolvedValue, $resolveInfo);
0 ignored issues
show
Unused Code introduced by
The call to AbstractUnionType::resolveType() has too many arguments starting with $resolveInfo.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
461
462 7
        if (!$resolvedType) {
463
            throw new ResolveException('Resolving function must return type');
464
        }
465
466 7
        if ($type instanceof AbstractInterfaceType) {
467 6
            $this->resolveValidator->assertTypeImplementsInterface($resolvedType, $type);
468
        } else {
469 1
            $this->resolveValidator->assertTypeInUnionTypes($resolvedType, $type);
470
        }
471
472 7
        $fakeField = new Field([
473 7
            'name' => $field->getName(),
474 7
            'type' => $resolvedType,
475 7
            'args' => $field->getArguments(),
476
        ]);
477
478 7
        return $this->resolveObject($fakeField, $ast, $resolvedValue, true);
479
    }
480
481 60
    protected function parseAndCreateRequest($payload, $variables = [])
482
    {
483 60
        if (empty($payload)) {
484 1
            throw new \InvalidArgumentException('Must provide an operation.');
485
        }
486
487 60
        $parser  = new Parser();
488 60
        $request = new Request($parser->parse($payload), $variables);
489
490 60
        (new RequestValidator())->validate($request);
491
492 59
        $this->executionContext->setRequest($request);
493 59
    }
494
495 53
    protected function doResolve(FieldInterface $field, AstFieldInterface $ast, $parentValue = null)
496
    {
497
        /** @var AstQuery|AstField $ast */
498 53
        $arguments = $this->parseArgumentsValues($field, $ast);
499 53
        $astFields = $ast instanceof AstQuery ? $ast->getFields() : [];
500
501 53
        return $field->resolve($parentValue, $arguments, $this->createResolveInfo($field, $astFields));
502
    }
503
504 53
    protected function parseArgumentsValues(FieldInterface $field, AstFieldInterface $ast)
505
    {
506 53
        $values   = [];
507 53
        $defaults = [];
508
509 53
        foreach ($field->getArguments() as $argument) {
510
            /** @var $argument InputField */
511 38
            if ($argument->getConfig()->has('defaultValue')) {
512 38
                $defaults[$argument->getName()] = $argument->getConfig()->getDefaultValue();
513
            }
514
        }
515
516 53
        foreach ($ast->getArguments() as $astArgument) {
517 30
            $argument     = $field->getArgument($astArgument->getName());
518 30
            $argumentType = $argument->getType()->getNullableType();
519
520 30
            $values[$argument->getName()] = $argumentType->parseValue($astArgument->getValue());
521
522 30
            if (isset($defaults[$argument->getName()])) {
523 30
                unset($defaults[$argument->getName()]);
524
            }
525
        }
526
527 53
        return array_merge($values, $defaults);
528
    }
529
530 59
    private function getAlias(AstFieldInterface $ast)
531
    {
532 59
        return $ast->getAlias() ?: $ast->getName();
533
    }
534
535 53
    protected function createResolveInfo(FieldInterface $field, array $astFields)
536
    {
537 53
        return new ResolveInfo($field, $astFields, $this->executionContext);
538
    }
539
540
541
    /**
542
     * You can access ExecutionContext to check errors and inject dependencies
543
     *
544
     * @return ExecutionContext
545
     */
546 11
    public function getExecutionContext()
547
    {
548 11
        return $this->executionContext;
549
    }
550
551 60
    public function getResponseData()
552
    {
553 60
        $result = [];
554
555 60
        if (!empty($this->data)) {
556 59
            $result['data'] = $this->data;
557
        }
558
559 60
        if ($this->executionContext->hasErrors()) {
560 19
            $result['errors'] = $this->executionContext->getErrorsArray();
561
        }
562
563 60
        return $result;
564
    }
565
566
    /**
567
     * @return int
568
     */
569
    public function getMaxComplexity()
570
    {
571
        return $this->maxComplexity;
572
    }
573
574
    /**
575
     * @param int $maxComplexity
576
     */
577 1
    public function setMaxComplexity($maxComplexity)
578
    {
579 1
        $this->maxComplexity = $maxComplexity;
580 1
    }
581
582
}
583