Completed
Push — master ( d52ee7...e7842c )
by Alexandr
03:54
created

ResolveValidator::objectHasField()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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