Passed
Pull Request — master (#129)
by Christoffer
02:26
created

ValuesResolver   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 176
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 20
dl 0
loc 176
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
B coerceValueForVariableNode() 0 29 5
A coerceValueFromAST() 0 6 1
A getDirectiveValues() 0 15 3
A __construct() 0 3 1
C coerceArgumentValues() 0 64 10
1
<?php
2
3
namespace Digia\GraphQL\Execution;
4
5
use Digia\GraphQL\Error\CoercingException;
6
use Digia\GraphQL\Error\ExecutionException;
7
use Digia\GraphQL\Error\InvalidTypeException;
8
use Digia\GraphQL\Error\InvariantException;
9
use Digia\GraphQL\Language\Node\ArgumentNode;
10
use Digia\GraphQL\Language\Node\DirectivesTrait;
11
use Digia\GraphQL\Language\Node\FieldNode;
12
use Digia\GraphQL\Language\Node\NameAwareInterface;
13
use Digia\GraphQL\Language\Node\NodeInterface;
14
use Digia\GraphQL\Language\Node\ValueNodeInterface;
15
use Digia\GraphQL\Language\Node\VariableNode;
16
use Digia\GraphQL\Type\Definition\DirectiveInterface;
17
use Digia\GraphQL\Type\Definition\Field;
18
use Digia\GraphQL\Type\Definition\NonNullType;
19
use Digia\GraphQL\Type\Definition\TypeInterface;
20
use Digia\GraphQL\Util\ValueNodeCoercer;
21
use function Digia\GraphQL\Util\find;
22
use function Digia\GraphQL\Util\keyMap;
23
24
/**
25
 * Class ValuesResolver
26
 * @package Digia\GraphQL\Execution
27
 */
28
class ValuesResolver
29
{
30
    /**
31
     * @var ValueNodeCoercer
32
     */
33
    protected $valueNodeCoercer;
34
35
    /**
36
     * ValuesResolver constructor.
37
     * @param ValueNodeCoercer $valueNodeCoercer
38
     */
39
    public function __construct(ValueNodeCoercer $valueNodeCoercer)
40
    {
41
        $this->valueNodeCoercer = $valueNodeCoercer;
42
    }
43
44
    /**
45
     * @see http://facebook.github.io/graphql/October2016/#CoerceArgumentValues()
46
     *
47
     * @param Field|DirectiveInterface $objectType
48
     * @param FieldNode                $field
49
     * @param array                    $variableValues
50
     * @return array
51
     * @throws ExecutionException
52
     * @throws InvalidTypeException
53
     * @throws InvariantException
54
     */
55
    public function coerceArgumentValues($objectType, $field, array $variableValues = []): array
56
    {
57
        $coercedValues       = [];
58
        $argumentDefinitions = $objectType->getArguments();
59
        $argumentNodes       = $field->getArguments();
60
61
        if (empty($argumentDefinitions) || empty($argumentNodes)) {
62
            return $coercedValues;
63
        }
64
65
        $argumentNodeMap = keyMap($argumentNodes, function (ArgumentNode $value) {
66
            return $value->getNameValue();
67
        });
68
69
        foreach ($argumentDefinitions as $argumentDefinition) {
70
            $argumentName = $argumentDefinition->getName();
71
            $argumentType = $argumentDefinition->getType();
72
            /** @var ArgumentNode $argumentNode */
73
            $argumentNode = $argumentNodeMap[$argumentName];
74
            $defaultValue = $argumentDefinition->getDefaultValue();
75
76
            if (null === $argumentNode) {
77
                if (null === $defaultValue) {
78
                    $coercedValues[$argumentName] = $defaultValue;
79
                } elseif (!$argumentType instanceof NonNullType) {
80
                    throw new ExecutionException(
81
                        sprintf('Argument "%s" of required type "%s" was not provided.', $argumentName, $argumentType),
82
                        [$field]
83
                    );
84
                }
85
            } elseif ($argumentNode instanceof VariableNode) {
86
                $coercedValues[$argumentName] = $this->coerceValueForVariableNode(
87
                    $argumentNode,
88
                    $argumentType,
89
                    $argumentName,
90
                    $variableValues,
91
                    $defaultValue
92
                );
93
            } else {
94
                $coercedValue = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $coercedValue is dead and can be removed.
Loading history...
95
96
                try {
97
                    $coercedValue = $this->coerceValueFromAST($argumentNode->getValue(), $argumentType,
98
                        $variableValues);
99
                } catch (CoercingException $ex) {
100
                    // Value nodes that cannot be resolved should be treated as invalid values
101
                    // therefore we catch the exception and leave the `$coercedValue` as `null`.
102
                }
103
104
                if (null === $coercedValue) {
105
                    // Note: ValuesOfCorrectType validation should catch this before
106
                    // execution. This is a runtime check to ensure execution does not
107
                    // continue with an invalid argument value.
108
                    throw new ExecutionException(
109
                        sprintf('Argument "%s" has invalid value %s.', $argumentName, $argumentNode),
110
                        [$argumentNode->getValue()]
111
                    );
112
                }
113
114
                $coercedValues[$argumentName] = $coercedValue;
115
            }
116
        }
117
118
        return $coercedValues;
119
    }
120
121
    /**
122
     * @param DirectiveInterface            $directive
123
     * @param NodeInterface|DirectivesTrait $node
124
     * @param array                         $variableValues
125
     * @return array|null
126
     * @throws ExecutionException
127
     * @throws InvalidTypeException
128
     * @throws InvariantException
129
     */
130
    public function getDirectiveValues(
131
        DirectiveInterface $directive,
132
        NodeInterface $node,
133
        array $variableValues = []
134
    ): ?array {
135
        $directiveNode = $node->hasDirectives()
0 ignored issues
show
Bug introduced by
The method hasDirectives() does not exist on Digia\GraphQL\Language\Node\NodeInterface. It seems like you code against a sub-type of Digia\GraphQL\Language\Node\NodeInterface such as Digia\GraphQL\Language\Node\FragmentSpreadNode or Digia\GraphQL\Language\Node\InlineFragmentNode or Digia\GraphQL\Language\Node\FieldNode or Digia\GraphQL\Language\N...EnumValueDefinitionNode or Digia\GraphQL\Language\N...nputValueDefinitionNode or Digia\GraphQL\Language\Node\FieldDefinitionNode or Digia\GraphQL\Language\Node\FragmentDefinitionNode or Digia\GraphQL\Language\N...OperationDefinitionNode or Digia\GraphQL\Language\Node\SchemaDefinitionNode or Digia\GraphQL\Language\N...bjectTypeDefinitionNode or Digia\GraphQL\Language\N...calarTypeDefinitionNode or Digia\GraphQL\Language\N...bjectTypeDefinitionNode or Digia\GraphQL\Language\N...rfaceTypeDefinitionNode or Digia\GraphQL\Language\N...UnionTypeDefinitionNode or Digia\GraphQL\Language\Node\EnumTypeDefinitionNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...ScalarTypeExtensionNode or Digia\GraphQL\Language\Node\EnumTypeExtensionNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...erfaceTypeExtensionNode or Digia\GraphQL\Language\Node\UnionTypeExtensionNode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

135
        $directiveNode = $node->/** @scrutinizer ignore-call */ hasDirectives()
Loading history...
136
            ? find($node->getDirectives(), function (NameAwareInterface $value) use ($directive) {
0 ignored issues
show
Bug introduced by
The method getDirectives() does not exist on Digia\GraphQL\Language\Node\NodeInterface. It seems like you code against a sub-type of Digia\GraphQL\Language\Node\NodeInterface such as Digia\GraphQL\Language\Node\FragmentSpreadNode or Digia\GraphQL\Language\Node\InlineFragmentNode or Digia\GraphQL\Language\Node\FieldNode or Digia\GraphQL\Language\N...EnumValueDefinitionNode or Digia\GraphQL\Language\N...nputValueDefinitionNode or Digia\GraphQL\Language\Node\FieldDefinitionNode or Digia\GraphQL\Language\Node\FragmentDefinitionNode or Digia\GraphQL\Language\N...OperationDefinitionNode or Digia\GraphQL\Language\Node\SchemaDefinitionNode or Digia\GraphQL\Language\N...bjectTypeDefinitionNode or Digia\GraphQL\Language\N...calarTypeDefinitionNode or Digia\GraphQL\Language\N...bjectTypeDefinitionNode or Digia\GraphQL\Language\N...rfaceTypeDefinitionNode or Digia\GraphQL\Language\N...UnionTypeDefinitionNode or Digia\GraphQL\Language\Node\EnumTypeDefinitionNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...ScalarTypeExtensionNode or Digia\GraphQL\Language\Node\EnumTypeExtensionNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...erfaceTypeExtensionNode or Digia\GraphQL\Language\Node\UnionTypeExtensionNode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

136
            ? find($node->/** @scrutinizer ignore-call */ getDirectives(), function (NameAwareInterface $value) use ($directive) {
Loading history...
137
                return $value->getNameValue() === $directive->getName();
138
            }) : null;
139
140
        if (null !== $directiveNode) {
141
            return $this->coerceArgumentValues($directive, $directiveNode, $variableValues);
142
        }
143
144
        return null;
145
    }
146
147
    /**
148
     * @param ValueNodeInterface|null $valueNode
149
     * @param TypeInterface           $type
150
     * @param string                  $argumentName
151
     * @param array                   $variableValues
152
     * @return mixed|null
153
     * @throws ExecutionException
154
     * @throws InvalidTypeException
155
     * @throws InvariantException
156
     * @throws CoercingException
157
     */
158
    public function coerceValueFromAST(
159
        ?ValueNodeInterface $valueNode,
160
        TypeInterface $type,
161
        $variableValues = []
162
    ) {
163
        return $this->valueNodeCoercer->coerce($valueNode, $type, $variableValues);
164
    }
165
166
    /**
167
     * @param VariableNode  $variableNode
168
     * @param TypeInterface $argumentType
169
     * @param string        $argumentName
170
     * @param array         $variableValues
171
     * @param               $defaultValue
172
     * @return mixed
173
     * @throws ExecutionException
174
     */
175
    protected function coerceValueForVariableNode(
176
        VariableNode $variableNode,
177
        TypeInterface $argumentType,
178
        string $argumentName,
179
        array $variableValues,
180
        $defaultValue
181
    ) {
182
        $variableName = $variableNode->getNameValue();
183
184
        if (!empty($variableValues) && isset($variableValues[$variableName])) {
185
            // Note: this does not check that this variable value is correct.
186
            // This assumes that this query has been validated and the variable
187
            // usage here is of the correct type.
188
            return $variableValues[$variableName];
189
        }
190
191
        if (null !== $defaultValue) {
192
            return $defaultValue;
193
        }
194
195
        if ($argumentType instanceof NonNullType) {
196
            throw new ExecutionException(
197
                sprintf(
198
                    'Argument "%s" of required type "%s" was provided the variable "%s" which was not provided a runtime value.',
199
                    $argumentName,
200
                    $argumentType,
201
                    $variableName
202
                ),
203
                [$variableNode->getValue()]
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on Digia\GraphQL\Language\Node\VariableNode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

203
                [$variableNode->/** @scrutinizer ignore-call */ getValue()]

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
204
            );
205
        }
206
    }
207
}
208