Completed
Pull Request — master (#133)
by Adrien
04:00
created

Processor::getExecutionContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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 60
    public function __construct(AbstractSchema $schema)
61
    {
62 60
        if (empty($this->executionContext)) {
63 60
            $this->executionContext = new ExecutionContext($schema);
64 60
            $this->executionContext->setContainer(new Container());
65 60
        }
66
67 60
        $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 60
    }
69
70 58
    public function processPayload($payload, $variables = [], $reducers = [])
71
    {
72 58
        $this->data = [];
73
74
        try {
75 58
            $this->parseAndCreateRequest($payload, $variables);
76
77 57
            if ($this->maxComplexity) {
78 1
                $reducers[] = new MaxComplexityQueryVisitor($this->maxComplexity);
79 1
            }
80
81 57
            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 2
            }
85
86 57
            foreach ($this->executionContext->getRequest()->getAllOperations() as $query) {
87 57
                if ($operationResult = $this->resolveQuery($query)) {
88 57
                    $this->data = array_merge($this->data, $operationResult);
89 57
                };
90 57
            }
91 58
        } catch (\Exception $e) {
92 5
            $this->executionContext->addError($e);
93
        }
94
95 58
        return $this;
96
    }
97
98 57
    protected function resolveQuery(AstQuery $query)
99
    {
100 57
        $schema = $this->executionContext->getSchema();
101 57
        $type   = $query instanceof AstMutation ? $schema->getMutationType() : $schema->getQueryType();
102 57
        $field  = new Field([
103 57
            'name' => $query instanceof AstMutation ? 'mutation' : 'query',
104
            'type' => $type
105 57
        ]);
106
107 57
        if (self::TYPE_NAME_QUERY == $query->getName()) {
108 1
            return [$this->getAlias($query) => $type->getName()];
109
        }
110
111 57
        $this->resolveValidator->assetTypeHasField($type, $query);
112 57
        $value = $this->resolveField($field, $query);
113
114 57
        return [$this->getAlias($query) => $value];
115
    }
116
117 57
    protected function resolveField(FieldInterface $field, AstFieldInterface $ast, $parentValue = null, $fromObject = false)
118
    {
119
        try {
120
            /** @var AbstractObjectType $type */
121 57
            $type        = $field->getType();
122 57
            $nonNullType = $type->getNullableType();
123
124 57
            if (self::TYPE_NAME_QUERY == $ast->getName()) {
125 2
                return $nonNullType->getName();
126
            }
127
128 57
            $this->resolveValidator->assetTypeHasField($nonNullType, $ast);
129
130 57
            $targetField = $nonNullType->getField($ast->getName());
131
132 57
            $this->prepareAstArguments($targetField, $ast, $this->executionContext->getRequest());
133 56
            $this->resolveValidator->assertValidArguments($targetField, $ast, $this->executionContext->getRequest());
134
135 51
            switch ($kind = $targetField->getType()->getNullableType()->getKind()) {
136 51
                case TypeMap::KIND_ENUM:
137 51
                case TypeMap::KIND_SCALAR:
138 44
                    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 44
                    return $this->resolveScalar($targetField, $ast, $parentValue);
143
144 35 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 26
                    if (!$ast instanceof AstQuery) {
147 1
                        throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation());
148
                    }
149
150 26
                    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 6 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 57
    private function prepareAstArguments(FieldInterface $field, AstFieldInterface $query, Request $request)
178
    {
179 57
        foreach ($query->getArguments() as $astArgument) {
180 32
            if ($field->hasArgument($astArgument->getName())) {
181 32
                $argumentType = $field->getArgument($astArgument->getName())->getType()->getNullableType();
182
183 32
                $astArgument->setValue($this->prepareArgumentValue($astArgument->getValue(), $argumentType, $request));
184 31
            }
185 56
        }
186 56
    }
187
188 32
    private function prepareArgumentValue($argumentValue, AbstractType $argumentType, Request $request)
189
    {
190 32
        switch ($argumentType->getKind()) {
191 32
            case TypeMap::KIND_LIST:
192
                /** @var $argumentType AbstractListType */
193 6
                $result = [];
194 6
                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 5
                    }
199 5
                } else {
200 1
                    if ($argumentValue instanceof VariableReference) {
201 1
                        return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
202
                    }
203
                }
204
205 5
                return $result;
206
207 31
            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 1
                            $result[$field->getName()] = $field->getType()->getNullableType()->parseInputValue($field->getConfig()->get('defaultValue'));
215 1
                        }
216 5
                    }
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 5
                        } else {
221
                            $result[$key] = $item;
222
                        }
223 5
                    }
224 5
                } 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 30
            case TypeMap::KIND_SCALAR:
237 30
            case TypeMap::KIND_ENUM:
238
                /** @var $argumentValue AstLiteral|VariableReference */
239 30
                if ($argumentValue instanceof VariableReference) {
240 6
                    return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
241
                } else {
242 26
                    if ($argumentValue instanceof AstLiteral) {
243 18
                        return $argumentValue->getValue();
244
                    } else {
245 9
                        return $argumentValue;
246
                    }
247
                }
248
        }
249
250
        throw new ResolveException('Argument type not supported');
251
    }
252
253 7
    private function getVariableReferenceArgumentValue(VariableReference $variableReference, AbstractType $argumentType, Request $request)
254
    {
255 7
        $variable = $variableReference->getVariable();
256 7
        if ($argumentType->getKind() === TypeMap::KIND_LIST) {
257
            if (
258 1
                !$variable->isArray() ||
259 1
                ($variable->getTypeName() !== $argumentType->getNamedType()->getNullableType()->getName()) ||
260 1
                ($argumentType->getNamedType()->getKind() === TypeMap::KIND_NON_NULL && $variable->isArrayElementNullable())
261 1
            ) {
262 1
                throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getNamedType()->getNullableType()->getName()), $variable->getLocation());
263
            }
264 1
        } else {
265 6
            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 6
        $requestValue = $request->getVariable($variable->getName());
271 6
        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 6
        return $requestValue;
276
    }
277
278 31
    protected function resolveObject(FieldInterface $field, AstFieldInterface $ast, $parentValue, $fromUnion = false)
279
    {
280 31
        $resolvedValue = $parentValue;
281 31
        if (!$fromUnion) {
282 26
            $resolvedValue = $this->doResolve($field, $ast, $parentValue);
283 26
        }
284
285 31
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
286
287 31
        if (null === $resolvedValue) {
288 5
            return null;
289
        }
290
        /** @var AbstractObjectType $type */
291 30
        $type = $field->getType()->getNullableType();
292
293
        try {
294 30
            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 30
    private function collectResult(FieldInterface $field, AbstractObjectType $type, $ast, $resolvedValue)
308
    {
309 30
        $result = [];
310
311 30
        foreach ($ast->getFields() as $astField) {
312 30
            switch (true) {
313 30
                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
                                break;
323
                            }
324 2
                        }
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 30
                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 1
                        }
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 30
                default:
356 30
                    $result[$this->getAlias($astField)] = $this->resolveField($field, $astField, $resolvedValue, true);
357 30
            }
358 30
        }
359
360 30
        return $result;
361
    }
362
363 45
    protected function resolveScalar(FieldInterface $field, AstFieldInterface $ast, $parentValue)
364
    {
365 45
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
366
367 45
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
368
369
        /** @var AbstractScalarType $type */
370 44
        $type = $field->getType()->getNullableType();
371
372 44
        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 15
        }
394
395 16
        $fakeField = new Field([
396 16
            'name' => $field->getName(),
397 16
            'type' => $itemType,
398 16
            'args' => $field->getArguments(),
399 16
        ]);
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 15
                    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
                        $value = null;
429 14
                }
430 15
            } catch (\Exception $e) {
431 1
                $this->executionContext->addError($e);
432
433 1
                $value = null;
434
            }
435
436 15
            $result[] = $value;
437 16
        }
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
        $resolvedType = $type->resolveType($resolvedValue);
456
457 7
        if (!$resolvedType) {
458
            throw new ResolveException('Resolving function must return type');
459
        }
460
461 7
        if ($type instanceof AbstractInterfaceType) {
462 6
            $this->resolveValidator->assertTypeImplementsInterface($resolvedType, $type);
463 6
        } else {
464 1
            $this->resolveValidator->assertTypeInUnionTypes($resolvedType, $type);
465
        }
466
467 7
        $fakeField = new Field([
468 7
            'name' => $field->getName(),
469 7
            'type' => $resolvedType,
470 7
            'args' => $field->getArguments(),
471 7
        ]);
472
473 7
        return $this->resolveObject($fakeField, $ast, $resolvedValue, true);
474
    }
475
476 58
    protected function parseAndCreateRequest($payload, $variables = [])
477
    {
478 58
        if (empty($payload)) {
479 1
            throw new \InvalidArgumentException('Must provide an operation.');
480
        }
481
482 58
        $parser  = new Parser();
483 58
        $request = new Request($parser->parse($payload), $variables);
484
485 58
        (new RequestValidator())->validate($request);
486
487 57
        $this->executionContext->setRequest($request);
488 57
    }
489
490 51
    protected function doResolve(FieldInterface $field, AstFieldInterface $ast, $parentValue = null)
491
    {
492
        /** @var AstQuery|AstField $ast */
493 51
        $arguments = $this->parseArgumentsValues($field, $ast);
494 51
        $astFields = $ast instanceof AstQuery ? $ast->getFields() : [];
495
496 51
        return $field->resolve($parentValue, $arguments, $this->createResolveInfo($field, $astFields));
497
    }
498
499 51
    protected function parseArgumentsValues(FieldInterface $field, AstFieldInterface $ast)
500
    {
501 51
        $values   = [];
502 51
        $defaults = [];
503
504 51
        foreach ($field->getArguments() as $argument) {
505
            /** @var $argument InputField */
506 36
            if ($argument->getConfig()->has('defaultValue')) {
507 6
                $defaults[$argument->getName()] = $argument->getConfig()->getDefaultValue();
508 6
            }
509 51
        }
510
511 51
        foreach ($ast->getArguments() as $astArgument) {
512 28
            $argument     = $field->getArgument($astArgument->getName());
513 28
            $argumentType = $argument->getType()->getNullableType();
514
515 28
            $values[$argument->getName()] = $argumentType->parseValue($astArgument->getValue());
516
517 28
            if (isset($defaults[$argument->getName()])) {
518 3
                unset($defaults[$argument->getName()]);
519 3
            }
520 51
        }
521
522 51
        return array_merge($values, $defaults);
523
    }
524
525 57
    private function getAlias(AstFieldInterface $ast)
526
    {
527 57
        return $ast->getAlias() ?: $ast->getName();
528
    }
529
530 51
    protected function createResolveInfo(FieldInterface $field, array $astFields)
531
    {
532 51
        return new ResolveInfo($field, $astFields, $this->executionContext);
533
    }
534
535
536
    /**
537
     * You can access ExecutionContext to check errors and inject dependencies
538
     *
539
     * @return ExecutionContext
540
     */
541 11
    public function getExecutionContext()
542
    {
543 11
        return $this->executionContext;
544
    }
545
546 58
    public function getResponseData()
547
    {
548 58
        $result = [];
549
550 58
        if (!empty($this->data)) {
551 57
            $result['data'] = $this->data;
552 57
        }
553
554 58
        if ($this->executionContext->hasErrors()) {
555 19
            $result['errors'] = $this->executionContext->getErrorsArray();
556 19
        }
557
558 58
        return $result;
559
    }
560
561
    /**
562
     * @return int
563
     */
564
    public function getMaxComplexity()
565
    {
566
        return $this->maxComplexity;
567
    }
568
569
    /**
570
     * @param int $maxComplexity
571
     */
572 1
    public function setMaxComplexity($maxComplexity)
573
    {
574 1
        $this->maxComplexity = $maxComplexity;
575 1
    }
576
577
}
578