Completed
Pull Request — master (#161)
by Sam
02:49
created

Processor::resolveQuery()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

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