Passed
Pull Request — master (#164)
by Christoffer
02:18
created

BuilderContext::buildDefinitions()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 39
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 39
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 21
nc 7
nop 0
1
<?php
2
3
namespace Digia\GraphQL\Schema\Building;
4
5
use Digia\GraphQL\Error\LanguageException;
6
use Digia\GraphQL\Language\Node\DirectiveDefinitionNode;
7
use Digia\GraphQL\Language\Node\DocumentNode;
8
use Digia\GraphQL\Language\Node\NamedTypeNode;
9
use Digia\GraphQL\Language\Node\OperationTypeDefinitionNode;
10
use Digia\GraphQL\Language\Node\SchemaDefinitionNode;
11
use Digia\GraphQL\Language\Node\TypeDefinitionNodeInterface;
12
use Digia\GraphQL\Language\Node\TypeNodeInterface;
13
use Digia\GraphQL\Schema\DefinitionBuilderCreatorInterface;
14
use Digia\GraphQL\Schema\DefinitionBuilderInterface;
15
use Digia\GraphQL\Schema\ResolverRegistryInterface;
16
use Digia\GraphQL\Type\Definition\Directive;
17
use Digia\GraphQL\Type\Definition\TypeInterface;
18
use function Digia\GraphQL\Util\arraySome;
19
20
class BuilderContext implements BuilderContextInterface
21
{
22
    /**
23
     * @var DocumentNode
24
     */
25
    protected $document;
26
27
    /**
28
     * @var ResolverRegistryInterface
29
     */
30
    protected $resolverRegistry;
31
32
    /**
33
     * @var DefinitionBuilderCreatorInterface
34
     */
35
    protected $definitionBuilderCreator;
36
37
    /**
38
     * @var DefinitionBuilderInterface
39
     */
40
    protected $definitionBuilder;
41
42
    /**
43
     * @var SchemaDefinitionNode|null
44
     */
45
    protected $schemaDefinition;
46
47
    /**
48
     * @var TypeInterface[]
49
     */
50
    protected $typeDefinitionMap;
51
52
    /**
53
     * @var DirectiveDefinitionNode[]
54
     */
55
    protected $directiveDefinitions;
56
57
    /**
58
     * @var OperationTypeDefinitionNode[]
59
     */
60
    protected $operationTypeDefinitions;
61
62
    /**
63
     * BuilderContext constructor.
64
     * @param DocumentNode                      $document
65
     * @param ResolverRegistryInterface         $resolverRegistry
66
     * @param DefinitionBuilderCreatorInterface $definitionBuilderCreator
67
     * @throws LanguageException
68
     */
69
    public function __construct(
70
        DocumentNode $document,
71
        ResolverRegistryInterface $resolverRegistry,
72
        DefinitionBuilderCreatorInterface $definitionBuilderCreator
73
    ) {
74
        $this->document                 = $document;
75
        $this->resolverRegistry         = $resolverRegistry;
76
        $this->definitionBuilderCreator = $definitionBuilderCreator;
77
    }
78
79
    /**
80
     * @throws LanguageException
81
     */
82
    public function boot(): void
83
    {
84
        $this->buildDefinitions();
85
86
        $this->definitionBuilder = $this->createDefinitionBuilder();
87
88
        if (null !== $this->schemaDefinition) {
89
            $this->buildOperationTypeDefinitions();
90
        }
91
    }
92
93
    /**
94
     * @return TypeInterface|null
95
     */
96
    public function buildQueryType(): ?TypeInterface
97
    {
98
        $definition = $this->operationTypeDefinitions['query'] ?? $this->typeDefinitionMap['Query'] ?? null;
99
        return null !== $definition ? $this->definitionBuilder->buildType($definition) : null;
0 ignored issues
show
Bug introduced by
It seems like $definition can also be of type Digia\GraphQL\Type\Definition\TypeInterface; however, parameter $node of Digia\GraphQL\Schema\Def...rInterface::buildType() does only seem to accept Digia\GraphQL\Language\Node\NodeInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

99
        return null !== $definition ? $this->definitionBuilder->buildType(/** @scrutinizer ignore-type */ $definition) : null;
Loading history...
100
    }
101
102
    /**
103
     * @return TypeInterface|null
104
     */
105
    public function buildMutationType(): ?TypeInterface
106
    {
107
        $definition = $this->operationTypeDefinitions['mutation'] ?? $this->typeDefinitionMap['Mutation'] ?? null;
108
        return null !== $definition ? $this->definitionBuilder->buildType($definition) : null;
0 ignored issues
show
Bug introduced by
It seems like $definition can also be of type Digia\GraphQL\Type\Definition\TypeInterface; however, parameter $node of Digia\GraphQL\Schema\Def...rInterface::buildType() does only seem to accept Digia\GraphQL\Language\Node\NodeInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

108
        return null !== $definition ? $this->definitionBuilder->buildType(/** @scrutinizer ignore-type */ $definition) : null;
Loading history...
109
    }
110
111
    /**
112
     * @return TypeInterface|null
113
     */
114
    public function buildSubscriptionType(): ?TypeInterface
115
    {
116
        $definition = $this->operationTypeDefinitions['subscription'] ?? $this->typeDefinitionMap['Subscription'] ?? null;
117
        return null !== $definition ? $this->definitionBuilder->buildType($definition) : null;
0 ignored issues
show
Bug introduced by
It seems like $definition can also be of type Digia\GraphQL\Type\Definition\TypeInterface; however, parameter $node of Digia\GraphQL\Schema\Def...rInterface::buildType() does only seem to accept Digia\GraphQL\Language\Node\NodeInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
        return null !== $definition ? $this->definitionBuilder->buildType(/** @scrutinizer ignore-type */ $definition) : null;
Loading history...
118
    }
119
120
    /**
121
     * @return TypeInterface[]
122
     */
123
    public function buildTypes(): array
124
    {
125
        return \array_map(function (TypeDefinitionNodeInterface $definition) {
126
            return $this->definitionBuilder->buildType($definition);
127
        }, \array_values($this->typeDefinitionMap));
128
    }
129
130
    /**
131
     * @return Directive[]
132
     */
133
    public function buildDirectives(): array
134
    {
135
        $directives = \array_map(function (DirectiveDefinitionNode $definition) {
136
            return $this->definitionBuilder->buildDirective($definition);
137
        }, $this->directiveDefinitions);
138
139
        $specifiedDirectivesMap = [
140
            'skip'       => GraphQLSkipDirective(),
141
            'include'    => GraphQLIncludeDirective(),
142
            'deprecated' => GraphQLDeprecatedDirective(),
143
        ];
144
145
        foreach ($specifiedDirectivesMap as $name => $directive) {
146
            if (!arraySome($directives, function (Directive $directive) use ($name) {
147
                return $directive->getName() === $name;
148
            })) {
149
                $directives[] = $directive;
150
            }
151
        }
152
153
        return $directives;
154
    }
155
156
    /**
157
     * @return SchemaDefinitionNode|null
158
     */
159
    public function getSchemaDefinition(): ?SchemaDefinitionNode
160
    {
161
        return $this->schemaDefinition;
162
    }
163
164
    /**
165
     * @inheritdoc
166
     * @throws LanguageException
167
     */
168
    protected function buildDefinitions(): void
169
    {
170
        $schemaDefinition     = null;
171
        $typeDefinitionMap    = [];
172
        $directiveDefinitions = [];
173
174
        foreach ($this->document->getDefinitions() as $definition) {
175
            if ($definition instanceof SchemaDefinitionNode) {
176
                if (null !== $schemaDefinition) {
177
                    throw new LanguageException('Must provide only one schema definition.');
178
                }
179
180
                $schemaDefinition = $definition;
181
182
                continue;
183
            }
184
185
            if ($definition instanceof TypeDefinitionNodeInterface) {
186
                $typeName = $definition->getNameValue();
187
188
                if (isset($typeDefinitionMap[$typeName])) {
189
                    throw new LanguageException(sprintf('Type "%s" was defined more than once.', $typeName));
190
                }
191
192
                $typeDefinitionMap[$typeName] = $definition;
193
194
                continue;
195
            }
196
197
            if ($definition instanceof DirectiveDefinitionNode) {
198
                $directiveDefinitions[] = $definition;
199
200
                continue;
201
            }
202
        }
203
204
        $this->schemaDefinition     = $schemaDefinition;
205
        $this->typeDefinitionMap    = $typeDefinitionMap;
206
        $this->directiveDefinitions = $directiveDefinitions;
207
    }
208
209
    /**
210
     * @return DefinitionBuilderInterface
211
     */
212
    protected function createDefinitionBuilder(): DefinitionBuilderInterface
213
    {
214
        return $this->definitionBuilderCreator->create($this->typeDefinitionMap, null, $this->resolverRegistry);
215
    }
216
217
    /**
218
     * @throws LanguageException
219
     */
220
    protected function buildOperationTypeDefinitions(): void
221
    {
222
        $operationTypeDefinitions = [];
223
224
        foreach ($this->schemaDefinition->getOperationTypes() as $operationTypeDefinition) {
0 ignored issues
show
Bug introduced by
The method getOperationTypes() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

224
        foreach ($this->schemaDefinition->/** @scrutinizer ignore-call */ getOperationTypes() as $operationTypeDefinition) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
225
            /** @var TypeNodeInterface|NamedTypeNode $operationType */
226
            $operationType = $operationTypeDefinition->getType();
227
            $typeName      = $operationType->getNameValue();
0 ignored issues
show
Bug introduced by
The method getNameValue() does not exist on Digia\GraphQL\Language\Node\TypeNodeInterface. It seems like you code against a sub-type of Digia\GraphQL\Language\Node\TypeNodeInterface such as Digia\GraphQL\Language\Node\NamedTypeNode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

227
            /** @scrutinizer ignore-call */ 
228
            $typeName      = $operationType->getNameValue();
Loading history...
228
            $operation     = $operationTypeDefinition->getOperation();
229
230
            if (isset($operationTypeDefinitions[$typeName])) {
231
                throw new LanguageException(
232
                    \sprintf('Must provide only one %s type in schema.', $operation)
233
                );
234
            }
235
236
            if (!isset($this->typeDefinitionMap[$typeName])) {
237
                throw new LanguageException(
238
                    \sprintf('Specified %s type %s not found in document.', $operation, $typeName)
239
                );
240
            }
241
242
            $operationTypeDefinitions[$operation] = $operationType;
243
        }
244
245
        $this->operationTypeDefinitions = $operationTypeDefinitions;
246
    }
247
}
248