Completed
Push — master ( f1bb68...f91685 )
by Alexandr
03:16
created

ResolveValidator::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 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
ccs 2
cts 2
cp 1
cc 1
eloc 2
nc 1
nop 0
crap 1
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 MongoDB\BSON\Serializable;
11
use Youshido\GraphQL\Execution\Context\ExecutionContextInterface;
12
use Youshido\GraphQL\Execution\Request;
13
use Youshido\GraphQL\Field\AbstractField;
14
use Youshido\GraphQL\Field\InputField;
15
use Youshido\GraphQL\Parser\Ast\Argument;
16
use Youshido\GraphQL\Parser\Ast\ArgumentValue\Literal;
17
use Youshido\GraphQL\Parser\Ast\ArgumentValue\Variable;
18
use Youshido\GraphQL\Parser\Ast\Field as AstField;
19
use Youshido\GraphQL\Parser\Ast\Fragment;
20
use Youshido\GraphQL\Parser\Ast\FragmentReference;
21
use Youshido\GraphQL\Parser\Ast\Mutation;
22
use Youshido\GraphQL\Parser\Ast\Query;
23
use Youshido\GraphQL\Type\AbstractType;
24
use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType;
25
use Youshido\GraphQL\Type\ListType\AbstractListType;
26
use Youshido\GraphQL\Type\Object\AbstractObjectType;
27
use Youshido\GraphQL\Type\TypeMap;
28
use Youshido\GraphQL\Type\TypeService;
29
use Youshido\GraphQL\Type\Union\AbstractUnionType;
30
use Youshido\GraphQL\Validator\Exception\ResolveException;
31
32
class ResolveValidator implements ResolveValidatorInterface
33
{
34
35
    /** @var  ExecutionContextInterface */
36
    protected $executionContext;
37
38 28
    public function __construct(ExecutionContextInterface $executionContext)
39
    {
40 28
        $this->executionContext = $executionContext;
41 28
    }
42
43
    /**
44
     * @param AbstractObjectType      $objectType
45
     * @param Mutation|Query|AstField $field
46
     * @return null
47
     */
48 23
    public function objectHasField($objectType, $field)
49
    {
50 23
        if (!($objectType instanceof AbstractObjectType) || !$objectType->hasField($field->getName())) {
51 3
            $this->executionContext->addError(new ResolveException(sprintf('Field "%s" not found in type "%s"', $field->getName(), $objectType->getNamedType()->getName())));
52
53 3
            return false;
54
        }
55
56 23
        return true;
57
    }
58
59
    /**
60
     * @inheritdoc
61
     */
62 23
    public function validateArguments(AbstractField $field, $query, Request $request)
63
    {
64 23
        if (!count($field->getArguments())) return true;
65
66
        $requiredArguments = array_filter($field->getArguments(), function (InputField $argument) {
67 14
            return $argument->getType()->getKind() == TypeMap::KIND_NON_NULL;
68 14
        });
69
70 14
        $withDefaultArguments = array_filter($field->getArguments(), function (InputField $argument) {
71 14
            return $argument->getConfig()->get('default') !== null;
72 14
        });
73
74 14
        foreach ($query->getArguments() as $argument) {
75 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...
76 2
                $this->executionContext->addError(new ResolveException(sprintf('Unknown argument "%s" on field "%s"', $argument->getName(), $field->getName())));
77
78 2
                return false;
79
            }
80
81
            /** @var AbstractType $argumentType */
82 10
            $originalArgumentType = $field->getArgument($argument->getName())->getType();
83 10
            $argumentType         = $field->getArgument($argument->getName())->getType()->getNullableType()->getNamedType();
84 10
            if ($argument->getValue() instanceof Variable) {
85
                /** @var Variable $variable */
86 4
                $variable = $argument->getValue();
87
88
                //todo: here validate argument
89
90 4
                if ($variable->getTypeName() !== $argumentType->getName()) {
91 2
                    $this->executionContext->addError(new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getName())));
92
93 2
                    return false;
94
                }
95
96
                /** @var Variable $requestVariable */
97 3
                $requestVariable = $request->getVariable($variable->getName());
98 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...
99 1
                    $this->executionContext->addError(new ResolveException(sprintf('Variable "%s" does not exist for query "%s"', $argument->getName(), $field->getName())));
100
101 1
                    return false;
102
                }
103 3
                $variable->setValue($requestVariable);
104
105 3
            }
106
107 9
            $values = $argument->getValue()->getValue();
108 9
            if (!$originalArgumentType instanceof AbstractListType) {
109 9
                $values = [$values];
110 9
            }
111 9
            foreach ($values as $value) {
112 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...
113 1
                    $this->executionContext->addError(new ResolveException(sprintf('Not valid type for argument "%s" in query "%s"', $argument->getName(), $field->getName())));
114
115 1
                    return false;
116
                }
117 9
            }
118
119 9
            if (array_key_exists($argument->getName(), $requiredArguments)) {
120 3
                unset($requiredArguments[$argument->getName()]);
121 3
            }
122 9
            if (array_key_exists($argument->getName(), $withDefaultArguments)) {
123 1
                unset($withDefaultArguments[$argument->getName()]);
124 1
            }
125 13
        }
126
127 13
        if (count($requiredArguments)) {
128 1
            $this->executionContext->addError(new ResolveException(sprintf('Require "%s" arguments to query "%s"', implode(', ', array_keys($requiredArguments)), $query->getName())));
129
130 1
            return false;
131
        }
132
133 12
        if (count($withDefaultArguments)) {
134 1
            foreach ($withDefaultArguments as $name => $argument) {
135 1
                $query->addArgument(new Argument($name, new Literal($argument->getConfig()->get('default'))));
136 1
            }
137 1
        }
138
139 12
        return true;
140
    }
141
142 7
    public function assertTypeImplementsInterface(AbstractType $type, AbstractInterfaceType $interface)
143
    {
144 7
        if (!$interface->isValidValue($type)) {
145 1
            throw new ResolveException('Type ' . $type->getName() . ' does not implement ' . $interface->getName());
146
        }
147 6
    }
148
149 3
    public function assertTypeInUnionTypes(AbstractType $type, AbstractUnionType $unionType)
150
    {
151 3
        $unionTypes = $unionType->getTypes();
152 3
        $valid      = false;
153 3
        if (empty($unionTypes)) return false;
154
155 3
        foreach ($unionTypes as $unionType) {
156 3
            if ($unionType->getName() == $type->getName()) {
157 2
                $valid = true;
158
159 2
                break;
160
            }
161 3
        }
162
163 3
        if (!$valid) {
164 2
            throw new ResolveException('Type ' . $type->getName() . ' not exist in types of ' . $unionType->getName());
165
        }
166 2
    }
167
168
    /**
169
     * @param Fragment          $fragment
170
     * @param FragmentReference $fragmentReference
171
     * @param AbstractType      $queryType
172
     *
173
     * @throws \Exception
174
     */
175 4
    public function assertValidFragmentForField(Fragment $fragment, FragmentReference $fragmentReference, AbstractType $queryType)
176
    {
177 4
        if ($fragment->getModel() !== $queryType->getName()) {
178 1
            throw new ResolveException(sprintf('Fragment reference "%s" not found on model "%s"', $fragmentReference->getName(), $queryType->getName()));
179
        }
180 3
    }
181
182 8
    public function hasArrayAccess($data)
183
    {
184 8
        return is_array($data) ||
185
               ($data instanceof \ArrayAccess &&
186
                $data instanceof \Traversable &&
187
                $data instanceof Serializable &&
0 ignored issues
show
Bug introduced by
The class MongoDB\BSON\Serializable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
188 8
                $data instanceof \Countable);
189
    }
190
191 20
    public function isValidValueForField(AbstractField $field, $value)
192
    {
193 20
        $fieldType = $field->getType();
194 20
        if ($fieldType->getKind() == TypeMap::KIND_NON_NULL && is_null($value)) {
195 2
            $this->executionContext->addError(new ResolveException(sprintf('Cannot return null for non-nullable field %s', $field->getName())));
196
197 2
            return null;
198
        } else {
199 20
            $fieldType = $this->resolveTypeIfAbstract($fieldType->getNullableType(), $value);
200
        }
201
202 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...
203 2
            $this->executionContext->addError(new ResolveException(sprintf('Not valid value for %s field %s', $fieldType->getNullableType()->getKind(), $field->getName())));
204
205 2
            return null;
206
        }
207
208 20
        return true;
209
    }
210
211 20
    public function resolveTypeIfAbstract(AbstractType $type, $resolvedValue)
212
    {
213 20
        if (TypeService::isAbstractType($type)) {
214
            /** @var AbstractInterfaceType $type */
215 6
            $resolvedType = $type->resolveType($resolvedValue);
216
217 6
            if (!$resolvedType) {
218
                $this->executionContext->addError(new \Exception('Cannot resolve type'));
219
220
                return $type;
221
            }
222 6
            if ($type instanceof AbstractInterfaceType) {
223 5
                $this->assertTypeImplementsInterface($resolvedType, $type);
224 5
            } else {
225
                /** @var AbstractUnionType $type */
226 1
                $this->assertTypeInUnionTypes($resolvedType, $type);
227
            }
228
229 6
            return $resolvedType;
230
        }
231
232 20
        return $type;
233
    }
234
235
    /**
236
     * @return ExecutionContextInterface
237
     */
238 2
    public function getExecutionContext()
239
    {
240 2
        return $this->executionContext;
241
    }
242
243
    /**
244
     * @param ExecutionContextInterface $executionContext
245
     */
246
    public function setExecutionContext($executionContext)
247
    {
248
        $this->executionContext = $executionContext;
249
    }
250
251
}
252