Passed
Push — master ( 43996d...1595cb )
by Kirill
04:07
created

DefinitionContext::typeOf()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 14
rs 9.9666
c 0
b 0
f 0
cc 4
nc 4
nop 1
1
<?php
2
3
/**
4
 * This file is part of Railt package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Railt\SDL\Backend\Context;
13
14
use GraphQL\Contracts\TypeSystem\ArgumentInterface;
15
use GraphQL\Contracts\TypeSystem\DefinitionInterface;
16
use GraphQL\Contracts\TypeSystem\EnumValueInterface;
17
use GraphQL\Contracts\TypeSystem\FieldInterface;
18
use GraphQL\Contracts\TypeSystem\InputFieldInterface;
19
use Phplrt\Source\Exception\NotAccessibleException;
20
use Phplrt\Visitor\Traverser;
21
use Railt\SDL\Backend\Context;
22
use Railt\SDL\Backend\Runtime\DirectiveExecution;
23
use Railt\SDL\Backend\Runtime\DirectiveExecutionInterface;
24
use Railt\SDL\Backend\VariablesVisitor;
25
use Railt\SDL\Exception\RuntimeErrorException;
26
use Railt\SDL\Frontend\Ast\Definition\ArgumentDefinitionNode;
27
use Railt\SDL\Frontend\Ast\Definition\EnumValueDefinitionNode;
28
use Railt\SDL\Frontend\Ast\Definition\FieldDefinitionNode;
29
use Railt\SDL\Frontend\Ast\Definition\InputFieldDefinitionNode;
30
use Railt\SDL\Frontend\Ast\DefinitionNode;
31
use Railt\SDL\Frontend\Ast\Executable\DirectiveNode;
32
use Railt\SDL\Frontend\Ast\Node;
33
use Railt\SDL\Frontend\Ast\Type\ListTypeNode;
34
use Railt\SDL\Frontend\Ast\Type\NamedTypeNode;
35
use Railt\SDL\Frontend\Ast\Type\NonNullTypeNode;
36
use Railt\SDL\Frontend\Ast\Type\TypeNode;
37
use Railt\SDL\Frontend\Ast\Value\VariableValueNode;
38
use Railt\TypeSystem\Argument;
39
use Railt\TypeSystem\EnumValue;
40
use Railt\TypeSystem\Field;
41
use Railt\TypeSystem\InputField;
42
use Railt\TypeSystem\Reference\TypeReference;
43
use Railt\TypeSystem\Reference\TypeReferenceInterface;
44
use Railt\TypeSystem\Schema;
45
use Railt\TypeSystem\Type\ListType;
46
use Railt\TypeSystem\Type\NonNullType;
47
use Railt\TypeSystem\Type\WrappingType;
48
use Railt\TypeSystem\Value\StringValue;
49
use Railt\TypeSystem\Value\ValueInterface;
50
51
/**
52
 * Class Record
53
 */
54
abstract class DefinitionContext implements DefinitionContextInterface
55
{
56
    /**
57
     * @var Schema
58
     */
59
    protected Schema $schema;
60
61
    /**
62
     * @var DefinitionNode
63
     */
64
    protected DefinitionNode $ast;
65
66
    /**
67
     * @var Context
68
     */
69
    private Context $context;
70
71
    /**
72
     * Record constructor.
73
     *
74
     * @param Context $context
75
     * @param Schema $schema
76
     * @param DefinitionNode $ast
77
     */
78
    public function __construct(Context $context, Schema $schema, DefinitionNode $ast)
79
    {
80
        $this->context = $context;
81
        $this->schema = $schema;
82
        $this->ast = $ast;
83
    }
84
85
    /**
86
     * @param iterable $ast
87
     * @param array $variables
88
     * @return iterable|Node
89
     */
90
    protected function precompile(iterable $ast, array $variables): iterable
91
    {
92
        $traverser = new Traverser([
93
            new VariablesVisitor($variables),
94
        ]);
95
96
        return $traverser->traverse($ast);
97
    }
98
99
    /**
100
     * @param EnumValueDefinitionNode $node
101
     * @return EnumValueInterface
102
     * @throws \Throwable
103
     */
104
    protected function buildEnumValueDefinition(EnumValueDefinitionNode $node): EnumValueInterface
105
    {
106
        $value = new EnumValue($node->name->value, [
107
            'description' => $this->descriptionOf($node),
108
        ]);
109
110
        foreach ($node->directives as $directive) {
111
            $this->executeDirective($value, $directive);
112
        }
113
114
        return $value;
115
    }
116
117
    /**
118
     * @param Node $node
119
     * @return string|null
120
     */
121
    protected function descriptionOf(Node $node): ?string
122
    {
123
        return $node->description instanceof StringValue
124
            ? $node->description->toPHPValue()
125
            : null;
126
    }
127
128
    /**
129
     * @param DefinitionInterface $ctx
130
     * @param DirectiveNode $node
131
     * @return void
132
     */
133
    protected function executeDirective(DefinitionInterface $ctx, DirectiveNode $node): void
134
    {
135
        $this->context->addExecution(
136
            $this->buildDirectiveExecution($ctx, $node)
137
        );
138
    }
139
140
    /**
141
     * @param DefinitionInterface $ctx
142
     * @param DirectiveNode $node
143
     * @return DirectiveExecutionInterface
144
     */
145
    protected function buildDirectiveExecution(
146
        DefinitionInterface $ctx,
147
        DirectiveNode $node
148
    ): DirectiveExecutionInterface {
149
        $arguments = [];
150
151
        foreach ($node->arguments as $argument) {
152
            $arguments[$argument->name->value] = $this->value($argument->value);
153
        }
154
155
        return new DirectiveExecution($node->name->name->value, $ctx, $arguments);
156
    }
157
158
    /**
159
     * @param ValueInterface $value
160
     * @return ValueInterface
161
     */
162
    protected function value(ValueInterface $value): ValueInterface
163
    {
164
        return $value;
165
    }
166
167
    /**
168
     * @param InputFieldDefinitionNode $node
169
     * @return InputFieldInterface
170
     * @throws \InvalidArgumentException
171
     * @throws \Throwable
172
     */
173
    protected function buildInputFieldDefinition(InputFieldDefinitionNode $node): InputFieldInterface
174
    {
175
        $field = new InputField($node->name->value, $this->typeOf($node->type), [
176
            'description' => $this->descriptionOf($node),
177
        ]);
178
179
        if ($node->defaultValue) {
180
            $field->setDefaultValue($this->value($node->defaultValue));
181
        }
182
183
        foreach ($node->directives as $directive) {
184
            $this->executeDirective($field, $directive);
185
        }
186
187
        return $field;
188
    }
189
190
    /**
191
     * @param TypeNode $type
192
     * @return TypeReferenceInterface|WrappingType
193
     * @throws \InvalidArgumentException
194
     * @throws \Throwable
195
     */
196
    protected function typeOf(TypeNode $type)
197
    {
198
        switch (true) {
199
            case $type instanceof NonNullTypeNode:
200
                return new NonNullType($this->typeOf($type->type));
201
202
            case $type instanceof ListTypeNode:
203
                return new ListType($this->typeOf($type->type));
204
205
            case $type instanceof NamedTypeNode:
206
                return $this->ref($type);
207
208
            default:
209
                throw new \InvalidArgumentException('Invalid type ref');
210
        }
211
    }
212
213
    /**
214
     * @param NamedTypeNode $node
215
     * @return TypeReferenceInterface
216
     */
217
    protected function ref(NamedTypeNode $node): TypeReferenceInterface
218
    {
219
        return new TypeReference($this->schema, $node->name->value);
220
    }
221
222
    /**
223
     * @param FieldDefinitionNode $node
224
     * @return FieldInterface
225
     * @throws \InvalidArgumentException
226
     * @throws \Throwable
227
     */
228
    protected function buildFieldDefinition(FieldDefinitionNode $node): FieldInterface
229
    {
230
        $field = new Field($node->name->value, $this->typeOf($node->type), [
231
            'description' => $this->descriptionOf($node),
232
        ]);
233
234
        foreach ($node->arguments as $argument) {
235
            $field->addArgument($this->buildArgumentDefinition($argument));
236
        }
237
238
        foreach ($node->directives as $directive) {
239
            $this->executeDirective($field, $directive);
240
        }
241
242
        return $field;
243
    }
244
245
    /**
246
     * @param ArgumentDefinitionNode $node
247
     * @return ArgumentInterface
248
     * @throws \InvalidArgumentException
249
     * @throws \Throwable
250
     */
251
    protected function buildArgumentDefinition(ArgumentDefinitionNode $node): ArgumentInterface
252
    {
253
        $argument = new Argument($node->name->value, $this->typeOf($node->type), [
254
            'description' => $this->descriptionOf($node),
255
        ]);
256
257
        if ($node->defaultValue) {
258
            $argument->setDefaultValue($this->value($node->defaultValue));
259
        }
260
261
        foreach ($node->directives as $directive) {
262
            $this->executeDirective($argument, $directive);
263
        }
264
265
        return $argument;
266
    }
267
}
268