Completed
Push — master ( f8e797...0b7ef3 )
by Alexandr
03:58
created

ResolveValidator::setExecutionContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
ccs 0
cts 3
cp 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * Date: 01.12.15
4
 *
5
 * @author Portey Vasil <[email protected]>
6
 */
7
8
namespace Youshido\GraphQL\Validator\ResolveValidator;
9
10
use Youshido\GraphQL\Execution\Context\ExecutionContextInterface;
11
use Youshido\GraphQL\Execution\Request;
12
use Youshido\GraphQL\Field\AbstractField;
13
use Youshido\GraphQL\Field\InputField;
14
use Youshido\GraphQL\Parser\Ast\Argument;
15
use Youshido\GraphQL\Parser\Ast\ArgumentValue\Literal;
16
use Youshido\GraphQL\Parser\Ast\ArgumentValue\Variable;
17
use Youshido\GraphQL\Parser\Ast\Field as AstField;
18
use Youshido\GraphQL\Parser\Ast\Fragment;
19
use Youshido\GraphQL\Parser\Ast\FragmentReference;
20
use Youshido\GraphQL\Parser\Ast\Mutation;
21
use Youshido\GraphQL\Parser\Ast\Query;
22
use Youshido\GraphQL\Type\AbstractType;
23
use Youshido\GraphQL\Type\CompositeTypeInterface;
24
use Youshido\GraphQL\Type\InputObject\AbstractInputObjectType;
25
use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType;
26
use Youshido\GraphQL\Type\ListType\AbstractListType;
27
use Youshido\GraphQL\Type\Object\AbstractObjectType;
28
use Youshido\GraphQL\Type\TypeMap;
29
use Youshido\GraphQL\Type\TypeService;
30
use Youshido\GraphQL\Type\Union\AbstractUnionType;
31
use Youshido\GraphQL\Validator\Exception\ResolveException;
32
33
class ResolveValidator implements ResolveValidatorInterface
34
{
35
36
    /** @var  ExecutionContextInterface */
37
    protected $executionContext;
38
39 29
    public function __construct(ExecutionContextInterface $executionContext)
40
    {
41 29
        $this->executionContext = $executionContext;
42 29
    }
43
44
    /**
45
     * @param AbstractObjectType      $objectType
46
     * @param Mutation|Query|AstField $field
47
     * @return null
48
     */
49 23
    public function objectHasField($objectType, $field)
50
    {
51 23
        if (!(($objectType instanceof AbstractObjectType || $objectType instanceof AbstractInputObjectType)) || !$objectType->hasField($field->getName())) {
52 3
            $this->executionContext->addError(new ResolveException(sprintf('Field "%s" not found in type "%s"', $field->getName(), $objectType->getNamedType()->getName())));
53
54 3
            return false;
55
        }
56
57 23
        return true;
58
    }
59
60
    /**
61
     * @inheritdoc
62
     */
63 23
    public function validateArguments(AbstractField $field, $query, Request $request)
64
    {
65 23
        if (!count($field->getArguments())) return true;
66
67
        $requiredArguments = array_filter($field->getArguments(), function (InputField $argument) {
68 14
            return $argument->getType()->getKind() == TypeMap::KIND_NON_NULL;
69 14
        });
70
71 14
        $withDefaultArguments = array_filter($field->getArguments(), function (InputField $argument) {
72 14
            return $argument->getConfig()->get('default') !== null;
73 14
        });
74
75 14
        foreach ($query->getArguments() as $argument) {
76 10 View Code Duplication
            if (!$field->hasArgument($argument->getName())) {
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...
77 2
                $this->executionContext->addError(new ResolveException(sprintf('Unknown argument "%s" on field "%s"', $argument->getName(), $field->getName())));
78
79 2
                return false;
80
            }
81
82
            /** @var AbstractType $argumentType */
83 10
            $originalArgumentType = $field->getArgument($argument->getName())->getType();
84 10
            $argumentType         = $field->getArgument($argument->getName())->getType()->getNullableType()->getNamedType();
85 10
            if ($argument->getValue() instanceof Variable) {
86
                /** @var Variable $variable */
87 4
                $variable = $argument->getValue();
88
89
                //todo: here validate argument
90
91 4
                if ($variable->getTypeName() !== $argumentType->getName()) {
92 2
                    $this->executionContext->addError(new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getName())));
93
94 2
                    return false;
95
                }
96
97
                /** @var Variable $requestVariable */
98 3
                $requestVariable = $request->getVariable($variable->getName());
99 3 View Code Duplication
                if (!$requestVariable) {
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...
100 1
                    $this->executionContext->addError(new ResolveException(sprintf('Variable "%s" does not exist for query "%s"', $argument->getName(), $field->getName())));
101
102 1
                    return false;
103
                }
104 3
                $variable->setValue($requestVariable);
105
106 3
            }
107
108 9
            $values = $argument->getValue()->getValue();
109 9
            if (!$originalArgumentType instanceof AbstractListType) {
110 9
                $values = [$values];
111 9
            }
112 9
            foreach ($values as $value) {
113 9 View Code Duplication
                if (!$argumentType->isValidValue($argumentType->parseValue($value))) {
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...
114 1
                    $this->executionContext->addError(new ResolveException(sprintf('Not valid type for argument "%s" in query "%s"', $argument->getName(), $field->getName())));
115
116 1
                    return false;
117
                }
118 9
            }
119
120 9
            if (array_key_exists($argument->getName(), $requiredArguments)) {
121 3
                unset($requiredArguments[$argument->getName()]);
122 3
            }
123 9
            if (array_key_exists($argument->getName(), $withDefaultArguments)) {
124 1
                unset($withDefaultArguments[$argument->getName()]);
125 1
            }
126 13
        }
127
128 13
        if (count($requiredArguments)) {
129 1
            $this->executionContext->addError(new ResolveException(sprintf('Require "%s" arguments to query "%s"', implode(', ', array_keys($requiredArguments)), $query->getName())));
130
131 1
            return false;
132
        }
133
134 12
        if (count($withDefaultArguments)) {
135 1
            foreach ($withDefaultArguments as $name => $argument) {
136 1
                $query->addArgument(new Argument($name, new Literal($argument->getConfig()->get('default'))));
137 1
            }
138 1
        }
139
140 12
        return true;
141
    }
142
143 7
    public function assertTypeImplementsInterface(AbstractType $type, AbstractInterfaceType $interface)
144
    {
145 7
        if (!$interface->isValidValue($type)) {
146 1
            throw new ResolveException('Type ' . $type->getName() . ' does not implement ' . $interface->getName());
147
        }
148 6
    }
149
150 3
    public function assertTypeInUnionTypes(AbstractType $type, AbstractUnionType $unionType)
151
    {
152 3
        $unionTypes = $unionType->getTypes();
153 3
        $valid      = false;
154 3
        if (empty($unionTypes)) return false;
155
156 3
        foreach ($unionTypes as $unionType) {
157 3
            if ($unionType->getName() == $type->getName()) {
158 2
                $valid = true;
159
160 2
                break;
161
            }
162 3
        }
163
164 3
        if (!$valid) {
165 2
            throw new ResolveException('Type ' . $type->getName() . ' not exist in types of ' . $unionType->getName());
166
        }
167 2
    }
168
169
    /**
170
     * @param Fragment          $fragment
171
     * @param FragmentReference $fragmentReference
172
     * @param AbstractType      $queryType
173
     *
174
     * @throws \Exception
175
     */
176 5
    public function assertValidFragmentForField(Fragment $fragment, FragmentReference $fragmentReference, AbstractType $queryType)
177
    {
178 5
        $innerType = $queryType;
179 5
        while ($innerType->isCompositeType()) {
180 1
          $innerType = $innerType->getTypeOf();
181 1
        }
182
183 5
        if ($fragment->getModel() !== $innerType->getName()) {
184 1
            throw new ResolveException(sprintf('Fragment reference "%s" not found on model "%s"', $fragmentReference->getName(), $queryType->getName()));
185
        }
186 4
    }
187
188 9
    public function hasArrayAccess($data)
189
    {
190 9
        return is_array($data) || $data instanceof \Traversable;
191
    }
192
193 20
    public function isValidValueForField(AbstractField $field, $value)
194
    {
195 20
        $fieldType = $field->getType();
196 20
        if ($fieldType->getKind() == TypeMap::KIND_NON_NULL && is_null($value)) {
197 2
            $this->executionContext->addError(new ResolveException(sprintf('Cannot return null for non-nullable field %s', $field->getName())));
198
199 2
            return null;
200
        } else {
201 20
            $fieldType = $this->resolveTypeIfAbstract($fieldType->getNullableType(), $value);
202
        }
203
204 20 View Code Duplication
        if (!is_null($value) && !$fieldType->isValidValue($value)) {
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...
205 2
            $this->executionContext->addError(new ResolveException(sprintf('Not valid value for %s field %s', $fieldType->getNullableType()->getKind(), $field->getName())));
206
207 2
            return null;
208
        }
209
210 20
        return true;
211
    }
212
213 20
    public function resolveTypeIfAbstract(AbstractType $type, $resolvedValue)
214
    {
215 20
        if (TypeService::isAbstractType($type)) {
216
            /** @var AbstractInterfaceType $type */
217 6
            $resolvedType = $type->resolveType($resolvedValue);
218
219 6
            if (!$resolvedType) {
220
                $this->executionContext->addError(new \Exception('Cannot resolve type'));
221
222
                return $type;
223
            }
224 6
            if ($type instanceof AbstractInterfaceType) {
225 5
                $this->assertTypeImplementsInterface($resolvedType, $type);
226 5
            } else {
227
                /** @var AbstractUnionType $type */
228 1
                $this->assertTypeInUnionTypes($resolvedType, $type);
229
            }
230
231 6
            return $resolvedType;
232
        }
233
234 20
        return $type;
235
    }
236
237
    /**
238
     * @return ExecutionContextInterface
239
     */
240 2
    public function getExecutionContext()
241
    {
242 2
        return $this->executionContext;
243
    }
244
245
    /**
246
     * @param ExecutionContextInterface $executionContext
247
     */
248
    public function setExecutionContext($executionContext)
249
    {
250
        $this->executionContext = $executionContext;
251
    }
252
253
}
254