Completed
Push — master ( 786636...6c7957 )
by Alexandr
03:01
created

Processor::prepareAstArguments()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 3
nop 3
crap 3
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 58
    public function __construct(AbstractSchema $schema)
61
    {
62 58
        if (empty($this->executionContext)) {
63 58
            $this->executionContext = new ExecutionContext($schema);
64 58
            $this->executionContext->setContainer(new Container());
65 58
        }
66
67 58
        $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 58
    }
69
70 56
    public function processPayload($payload, $variables = [], $reducers = [])
71
    {
72 56
        $this->data = [];
73
74
        try {
75 56
            $this->parseAndCreateRequest($payload, $variables);
76
77 55
            if ($this->maxComplexity) {
78 1
                $reducers[] = new MaxComplexityQueryVisitor($this->maxComplexity);
79 1
            }
80
81 55
            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 55
            foreach ($this->executionContext->getRequest()->getAllOperations() as $query) {
87 55
                if ($operationResult = $this->resolveQuery($query)) {
88 55
                    $this->data = array_merge($this->data, $operationResult);
89 55
                };
90 55
            }
91 56
        } catch (\Exception $e) {
92 5
            $this->executionContext->addError($e);
93
        }
94
95 56
        return $this;
96
    }
97
98 55
    protected function resolveQuery(AstQuery $query)
99
    {
100 55
        $schema = $this->executionContext->getSchema();
101 55
        $type   = $query instanceof AstMutation ? $schema->getMutationType() : $schema->getQueryType();
102 55
        $field  = new Field([
103 55
            'name' => $query instanceof AstMutation ? 'mutation' : 'query',
104
            'type' => $type
105 55
        ]);
106
107 55
        if (self::TYPE_NAME_QUERY == $query->getName()) {
108 1
            return [$this->getAlias($query) => $type->getName()];
109
        }
110
111 55
        $this->resolveValidator->assetTypeHasField($type, $query);
112 55
        $value = $this->resolveField($field, $query);
113
114 55
        return [$this->getAlias($query) => $value];
115
    }
116
117 55
    protected function resolveField(FieldInterface $field, AstFieldInterface $ast, $parentValue = null, $fromObject = false)
118
    {
119
        try {
120
            /** @var AbstractObjectType $type */
121 55
            $type        = $field->getType();
122 55
            $nonNullType = $type->getNullableType();
123
124 55
            if (self::TYPE_NAME_QUERY == $ast->getName()) {
125 2
                return $nonNullType->getName();
126
            }
127
128 55
            $this->resolveValidator->assetTypeHasField($nonNullType, $ast);
129
130 55
            $targetField = $nonNullType->getField($ast->getName());
131
132 55
            $this->prepareAstArguments($targetField, $ast, $this->executionContext->getRequest());
133 54
            $this->resolveValidator->assertValidArguments($targetField, $ast, $this->executionContext->getRequest());
134
135 50
            switch ($kind = $targetField->getType()->getNullableType()->getKind()) {
136 50
                case TypeMap::KIND_ENUM:
137 50
                case TypeMap::KIND_SCALAR:
138 43
                    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 43
                    return $this->resolveScalar($targetField, $ast, $parentValue);
143
144 34 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 25
                    if (!$ast instanceof AstQuery) {
147 1
                        throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation());
148
                    }
149
150 25
                    return $this->resolveObject($targetField, $ast, $parentValue);
151
152 21
                case TypeMap::KIND_LIST:
153 18
                    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 16
        } catch (\Exception $e) {
167 16
            $this->executionContext->addError($e);
168
169 16
            if ($fromObject) {
170 4
                throw $e;
171
            }
172
173 14
            return null;
174
        }
175
    }
176
177 55
    private function prepareAstArguments(FieldInterface $field, AstFieldInterface $query, Request $request)
178
    {
179 55
        foreach ($query->getArguments() as $astArgument) {
180 30
            if ($field->hasArgument($astArgument->getName())) {
181 30
                $argumentType = $field->getArgument($astArgument->getName())->getType()->getNullableType();
182
183 30
                $astArgument->setValue($this->prepareArgumentValue($astArgument->getValue(), $argumentType, $request));
184 29
            }
185 54
        }
186 54
    }
187
188 30
    private function prepareArgumentValue($argumentValue, AbstractType $argumentType, Request $request)
189
    {
190 30
        switch ($argumentType->getKind()) {
191 30
            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 6
                } else if ($argumentValue instanceof VariableReference) {
200 1
                    return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
201
                }
202
203 5
                return $result;
204
205 29
            case TypeMap::KIND_INPUT_OBJECT:
206
                /** @var $argumentType AbstractInputObjectType */
207 5
                $result = [];
208 5
                if ($argumentValue instanceof AstInputObject) {
209 4
                    foreach ($argumentType->getFields() as $field) {
210
                        /** @var $field Field */
211 4
                        if ($field->getConfig()->has('defaultValue')) {
212 1
                            $result[$field->getName()] = $field->getType()->getNullableType()->parseInputValue($field->getConfig()->get('defaultValue'));
213 1
                        }
214 4
                    }
215 4
                    foreach ($argumentValue->getValue() as $key => $item) {
216 4
                        if ($argumentType->hasField($key)) {
217 4
                            $result[$key] = $this->prepareArgumentValue($item, $argumentType->getField($key)->getType()->getNullableType(), $request);
218 4
                        } else {
219
                            $result[$key] = $item;
220
                        }
221 4
                    }
222 5
                } else if ($argumentValue instanceof VariableReference) {
223
                    return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
224 2
                } else if (is_array($argumentValue)) {
225 1
                    return $argumentValue;
226
                }
227
228 5
                return $result;
229
230 28
            case TypeMap::KIND_SCALAR:
231 28
            case TypeMap::KIND_ENUM:
232
                /** @var $argumentValue AstLiteral|VariableReference */
233 28
                if ($argumentValue instanceof VariableReference) {
234 5
                    return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
235 24
                } else if ($argumentValue instanceof AstLiteral) {
236 17
                    return $argumentValue->getValue();
237
                } else {
238 8
                    return $argumentValue;
239
                }
240
        }
241
242
        throw new ResolveException('Argument type not supported');
243
    }
244
245 6
    private function getVariableReferenceArgumentValue(VariableReference $variableReference, AbstractType $argumentType, Request $request)
246
    {
247 6
        $variable = $variableReference->getVariable();
248 6
        if ($argumentType->getKind() === TypeMap::KIND_LIST) {
249
            if (
250 1
                !$variable->isArray() ||
251 1
                ($variable->getTypeName() !== $argumentType->getNamedType()->getNullableType()->getName()) ||
252 1
                ($argumentType->getNamedType()->getKind() === TypeMap::KIND_NON_NULL && $variable->isArrayElementNullable())
253 1
            ) {
254 1
                throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getNamedType()->getNullableType()->getName()), $variable->getLocation());
255
            }
256 1
        } else {
257 5
            if ($variable->getTypeName() !== $argumentType->getName()) {
258 1
                throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getName()), $variable->getLocation());
259
            }
260
        }
261
262 5
        $requestValue = $request->getVariable($variable->getName());
263 5
        if ((null === $requestValue && $variable->isNullable()) && !$request->hasVariable($variable->getName())) {
264
            throw new ResolveException(sprintf('Variable "%s" does not exist in request', $variable->getName()), $variable->getLocation());
265
        }
266
267 5
        return $requestValue;
268
    }
269
270 30
    protected function resolveObject(FieldInterface $field, AstFieldInterface $ast, $parentValue, $fromUnion = false)
271
    {
272 30
        $resolvedValue = $parentValue;
273 30
        if (!$fromUnion) {
274 25
            $resolvedValue = $this->doResolve($field, $ast, $parentValue);
275 25
        }
276
277 30
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
278
279 30
        if (null === $resolvedValue) {
280 5
            return null;
281
        }
282
        /** @var AbstractObjectType $type */
283 29
        $type = $field->getType()->getNullableType();
284
285
        try {
286 29
            return $this->collectResult($field, $type, $ast, $resolvedValue);
287 4
        } catch (\Exception $e) {
288 4
            return null;
289
        }
290
    }
291
292
    /**
293
     * @param FieldInterface     $field
294
     * @param AbstractObjectType $type
295
     * @param AstFieldInterface $ast
296
     * @param                    $resolvedValue
297
     * @return array
298
     */
299 29
    private function collectResult(FieldInterface $field, AbstractObjectType $type, $ast, $resolvedValue)
300
    {
301 29
        $result = [];
302
303 29
        foreach ($ast->getFields() as $astField) {
304 29
            switch (true) {
305 29
                case $astField instanceof TypedFragmentReference:
306 2
                    $astName  = $astField->getTypeName();
307 2
                    $typeName = $type->getName();
308
309 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...
310 2
                        foreach ($type->getInterfaces() as $interface) {
311 1
                            if ($interface->getName() === $astName) {
312
                                $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...
313
314
                                break;
315
                            }
316 2
                        }
317
318 2
                        continue;
319
                    }
320
321 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...
322
323 2
                    break;
324
325 29
                case $astField instanceof FragmentReference:
326 4
                    $astFragment      = $this->executionContext->getRequest()->getFragment($astField->getName());
327 4
                    $astFragmentModel = $astFragment->getModel();
328 4
                    $typeName         = $type->getName();
329
330 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...
331 1
                        foreach ($type->getInterfaces() as $interface) {
332 1
                            if ($interface->getName() === $astFragmentModel) {
333 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...
334
335 1
                                break;
336
                            }
337
338 1
                        }
339
340 1
                        continue;
341
                    }
342
343 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...
344
345 4
                    break;
346
347 29
                default:
348 29
                    $result[$this->getAlias($astField)] = $this->resolveField($field, $astField, $resolvedValue, true);
349 29
            }
350 29
        }
351
352 29
        return $result;
353
    }
354
355 44
    protected function resolveScalar(FieldInterface $field, AstFieldInterface $ast, $parentValue)
356
    {
357 44
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
358
359 44
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
360
361
        /** @var AbstractScalarType $type */
362 43
        $type = $field->getType()->getNullableType();
363
364 43
        return $type->serialize($resolvedValue);
365
    }
366
367 18
    protected function resolveList(FieldInterface $field, AstFieldInterface $ast, $parentValue)
368
    {
369
        /** @var AstQuery $ast */
370 18
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
371
372 18
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
373
374 16
        if (null === $resolvedValue) {
375 5
            return null;
376
        }
377
378
        /** @var AbstractListType $type */
379 15
        $type     = $field->getType()->getNullableType();
380 15
        $itemType = $type->getNamedType();
381
382 15
        $fakeAst = clone $ast;
383 15
        if ($fakeAst instanceof AstQuery) {
384 15
            $fakeAst->setArguments([]);
385 15
        }
386
387 15
        $fakeField = new Field([
388 15
            'name' => $field->getName(),
389 15
            'type' => $itemType,
390 15
        ]);
391
392 15
        $result = [];
393 15
        foreach ($resolvedValue as $resolvedValueItem) {
394
            try {
395 14
                $fakeField->getConfig()->set('resolve', function () use ($resolvedValueItem) {
396 14
                    return $resolvedValueItem;
397 14
                });
398
399 14
                switch ($itemType->getNullableType()->getKind()) {
400 14
                    case TypeMap::KIND_ENUM:
401 14
                    case TypeMap::KIND_SCALAR:
402 4
                        $value = $this->resolveScalar($fakeField, $fakeAst, $resolvedValueItem);
403
404 3
                        break;
405
406
407 11
                    case TypeMap::KIND_OBJECT:
408 9
                        $value = $this->resolveObject($fakeField, $fakeAst, $resolvedValueItem);
409
410 9
                        break;
411
412 3
                    case TypeMap::KIND_UNION:
413 3
                    case TypeMap::KIND_INTERFACE:
414 3
                        $value = $this->resolveComposite($fakeField, $fakeAst, $resolvedValueItem);
415
416 3
                        break;
417
418
                    default:
419
                        $value = null;
420 13
                }
421 14
            } catch (\Exception $e) {
422 1
                $this->executionContext->addError($e);
423
424 1
                $value = null;
425
            }
426
427 14
            $result[] = $value;
428 15
        }
429
430 15
        return $result;
431
    }
432
433 7
    protected function resolveComposite(FieldInterface $field, AstFieldInterface $ast, $parentValue)
434
    {
435
        /** @var AstQuery $ast */
436 7
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
437
438 7
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
439
440
        /** @var AbstractUnionType $type */
441 7
        $type         = $field->getType()->getNullableType();
442 7
        $resolvedType = $type->resolveType($resolvedValue);
443
444 7
        if (!$resolvedType) {
445
            throw new ResolveException('Resolving function must return type');
446
        }
447
448 7
        if ($type instanceof AbstractInterfaceType) {
449 6
            $this->resolveValidator->assertTypeImplementsInterface($resolvedType, $type);
450 6
        } else {
451 1
            $this->resolveValidator->assertTypeInUnionTypes($resolvedType, $type);
452
        }
453
454 7
        $fakeField = new Field([
455 7
            'name' => $field->getName(),
456 7
            'type' => $resolvedType,
457 7
        ]);
458
459 7
        return $this->resolveObject($fakeField, $ast, $resolvedValue, true);
460
    }
461
462 56
    protected function parseAndCreateRequest($payload, $variables = [])
463
    {
464 56
        if (empty($payload)) {
465 1
            throw new \InvalidArgumentException('Must provide an operation.');
466
        }
467
468 56
        $parser  = new Parser();
469 56
        $request = new Request($parser->parse($payload), $variables);
470
471 56
        (new RequestValidator())->validate($request);
472
473 55
        $this->executionContext->setRequest($request);
474 55
    }
475
476 50
    protected function doResolve(FieldInterface $field, AstFieldInterface $ast, $parentValue = null)
477
    {
478
        /** @var AstQuery|AstField $ast */
479 50
        $arguments = $this->parseArgumentsValues($field, $ast);
480 50
        $astFields = $ast instanceof AstQuery ? $ast->getFields() : [];
481
482 50
        return $field->resolve($parentValue, $arguments, $this->createResolveInfo($field, $astFields));
483
    }
484
485 50
    protected function parseArgumentsValues(FieldInterface $field, AstFieldInterface $ast)
486
    {
487 50
        $values   = [];
488 50
        $defaults = [];
489
490 50
        foreach ($field->getArguments() as $argument) {
491
            /** @var $argument InputField */
492 35
            if ($argument->getConfig()->has('defaultValue')) {
493 6
                $defaults[$argument->getName()] = $argument->getConfig()->getDefaultValue();
494 6
            }
495 50
        }
496
497 50
        foreach ($ast->getArguments() as $astArgument) {
498 27
            $argument     = $field->getArgument($astArgument->getName());
499 27
            $argumentType = $argument->getType()->getNullableType();
500
501 27
            $values[$argument->getName()] = $argumentType->parseValue($astArgument->getValue());
502
503 27
            if (isset($defaults[$argument->getName()])) {
504 3
                unset($defaults[$argument->getName()]);
505 3
            }
506 50
        }
507
508 50
        return array_merge($values, $defaults);
509
    }
510
511 55
    private function getAlias(AstFieldInterface $ast)
512
    {
513 55
        return $ast->getAlias() ?: $ast->getName();
514
    }
515
516 50
    protected function createResolveInfo(FieldInterface $field, array $astFields)
517
    {
518 50
        return new ResolveInfo($field, $astFields, $this->executionContext);
519
    }
520
521
522
    /**
523
     * You can access ExecutionContext to check errors and inject dependencies
524
     *
525
     * @return ExecutionContext
526
     */
527 11
    public function getExecutionContext()
528
    {
529 11
        return $this->executionContext;
530
    }
531
532 56
    public function getResponseData()
533
    {
534 56
        $result = [];
535
536 56
        if (!empty($this->data)) {
537 55
            $result['data'] = $this->data;
538 55
        }
539
540 56
        if ($this->executionContext->hasErrors()) {
541 18
            $result['errors'] = $this->executionContext->getErrorsArray();
542 18
        }
543
544 56
        return $result;
545
    }
546
547
    /**
548
     * @return int
549
     */
550
    public function getMaxComplexity()
551
    {
552
        return $this->maxComplexity;
553
    }
554
555
    /**
556
     * @param int $maxComplexity
557
     */
558 1
    public function setMaxComplexity($maxComplexity)
559
    {
560 1
        $this->maxComplexity = $maxComplexity;
561 1
    }
562
563
}
564