Completed
Pull Request — master (#31)
by Sebastian
06:26 queued 02:32
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\InputObject\AbstractInputObjectType;
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 instanceof AbstractInputObjectType)) || !$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
            }
106
107 9
            $values = $argument->getValue()->getValue();
108 9
            if (!$originalArgumentType instanceof AbstractListType) {
109 9
                $values = [$values];
110
            }
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 9
                    return false;
116
                }
117
            }
118
119 9
            if (array_key_exists($argument->getName(), $requiredArguments)) {
120 3
                unset($requiredArguments[$argument->getName()]);
121
            }
122 9
            if (array_key_exists($argument->getName(), $withDefaultArguments)) {
123 9
                unset($withDefaultArguments[$argument->getName()]);
124
            }
125
        }
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
            }
137
        }
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 3
                break;
160
            }
161
        }
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 9
    public function hasArrayAccess($data)
183
    {
184 9
        return is_array($data) || $data instanceof \Traversable;
185
    }
186
187 20
    public function isValidValueForField(AbstractField $field, $value)
188
    {
189 20
        $fieldType = $field->getType();
190 20
        if ($fieldType->getKind() == TypeMap::KIND_NON_NULL && is_null($value)) {
191 2
            $this->executionContext->addError(new ResolveException(sprintf('Cannot return null for non-nullable field %s', $field->getName())));
192
193 2
            return null;
194
        } else {
195 20
            $fieldType = $this->resolveTypeIfAbstract($fieldType->getNullableType(), $value);
196
        }
197
198 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...
199 2
            $this->executionContext->addError(new ResolveException(sprintf('Not valid value for %s field %s', $fieldType->getNullableType()->getKind(), $field->getName())));
200
201 2
            return null;
202
        }
203
204 20
        return true;
205
    }
206
207 20
    public function resolveTypeIfAbstract(AbstractType $type, $resolvedValue)
208
    {
209 20
        if (TypeService::isAbstractType($type)) {
210
            /** @var AbstractInterfaceType $type */
211 6
            $resolvedType = $type->resolveType($resolvedValue);
212
213 6
            if (!$resolvedType) {
214
                $this->executionContext->addError(new \Exception('Cannot resolve type'));
215
216
                return $type;
217
            }
218 6
            if ($type instanceof AbstractInterfaceType) {
219 5
                $this->assertTypeImplementsInterface($resolvedType, $type);
220
            } else {
221
                /** @var AbstractUnionType $type */
222 1
                $this->assertTypeInUnionTypes($resolvedType, $type);
223
            }
224
225 6
            return $resolvedType;
226
        }
227
228 20
        return $type;
229
    }
230
231
    /**
232
     * @return ExecutionContextInterface
233
     */
234 2
    public function getExecutionContext()
235
    {
236 2
        return $this->executionContext;
237
    }
238
239
    /**
240
     * @param ExecutionContextInterface $executionContext
241
     */
242
    public function setExecutionContext($executionContext)
243
    {
244
        $this->executionContext = $executionContext;
245
    }
246
247
}
248