Passed
Pull Request — master (#15)
by
unknown
02:41
created

buildObjectFieldByReflectionMethodMiddleware()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 3
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Andi\GraphQL\Spiral\Bootloader;
6
7
use Andi\GraphQL\ArgumentResolver\ArgumentResolver;
8
use Andi\GraphQL\ArgumentResolver\ArgumentResolverInterface;
9
use Andi\GraphQL\InputObjectFieldResolver\InputObjectFieldResolver;
10
use Andi\GraphQL\InputObjectFieldResolver\InputObjectFieldResolverInterface;
11
use Andi\GraphQL\ObjectFieldResolver\Middleware\AbstractObjectFieldByReflectionMethodMiddleware;
12
use Andi\GraphQL\ObjectFieldResolver\Middleware\AbstractOuterObjectFieldByReflectionMethodMiddleware;
13
use Andi\GraphQL\ObjectFieldResolver\Middleware\AdditionalFieldByReflectionMethodMiddleware;
14
use Andi\GraphQL\ObjectFieldResolver\Middleware\InterfaceFieldByReflectionMethodMiddleware;
15
use Andi\GraphQL\ObjectFieldResolver\Middleware\MutationFieldByReflectionMethodMiddleware;
16
use Andi\GraphQL\ObjectFieldResolver\Middleware\ObjectFieldByReflectionMethodMiddleware;
17
use Andi\GraphQL\ObjectFieldResolver\Middleware\QueryFieldByReflectionMethodMiddleware;
18
use Andi\GraphQL\ObjectFieldResolver\ObjectFieldResolver;
19
use Andi\GraphQL\ObjectFieldResolver\ObjectFieldResolverInterface;
20
use Andi\GraphQL\Spiral\Command\ConfigCommand;
21
use Andi\GraphQL\Spiral\Common\SchemaWarmupper;
22
use Andi\GraphQL\Spiral\Common\ValueResolver;
23
use Andi\GraphQL\Spiral\Config\GraphQLConfig;
24
use Andi\GraphQL\Spiral\Listener\AdditionalFieldListener;
25
use Andi\GraphQL\Spiral\Listener\AttributedMutationFieldListener;
26
use Andi\GraphQL\Spiral\Listener\AttributedQueryFieldListener;
27
use Andi\GraphQL\Spiral\Listener\AttributedTypeLoaderListener;
28
use Andi\GraphQL\Spiral\Listener\MutationFieldListener;
29
use Andi\GraphQL\Spiral\Listener\QueryFieldListener;
30
use Andi\GraphQL\Spiral\Listener\TypeLoaderListener;
31
use Andi\GraphQL\Spiral\Middleware\GraphQLMiddleware;
32
use Andi\GraphQL\Type\MutationType;
33
use Andi\GraphQL\Type\QueryType;
34
use Andi\GraphQL\TypeRegistry;
35
use Andi\GraphQL\TypeRegistryInterface;
36
use Andi\GraphQL\TypeResolver\TypeResolver;
37
use Andi\GraphQL\TypeResolver\TypeResolverInterface;
38
use GraphQL\Server\ServerConfig;
39
use GraphQL\Server\StandardServer;
40
use GraphQL\Type\Definition as Webonyx;
41
use GraphQL\Type\Schema;
42
use GraphQL\Type\SchemaConfig;
43
use Psr\Container\ContainerInterface;
44
use Spiral\Attributes\ReaderInterface;
45
use Spiral\Boot\AbstractKernel;
46
use Spiral\Boot\Bootloader\Bootloader;
47
use Spiral\Boot\EnvironmentInterface;
48
use Spiral\Bootloader\Http\HttpBootloader;
49
use Spiral\Config\ConfiguratorInterface;
50
use Spiral\Console\Bootloader\ConsoleBootloader;
51
use Spiral\Core\Internal\Proxy;
52
use Spiral\Core\InvokerInterface;
53
use Spiral\Core\ScopeInterface;
54
use Spiral\Tokenizer\TokenizerListenerRegistryInterface;
55
56
final class GraphQLBootloader extends Bootloader
57
{
58
    protected const SINGLETONS = [
59
        StandardServer::class => [self::class, 'buildStandardServer'],
60
        ServerConfig::class => [self::class, 'buildServerConfig'],
61
        Schema::class => [self::class, 'buildSchema'],
62
        SchemaConfig::class => [self::class, 'buildSchemaConfig'],
63
64
        TypeRegistryInterface::class => TypeRegistry::class,
65
        TypeRegistry::class => [self::class, 'buildTypeRegistry'],
66
67
        TypeResolverInterface::class => TypeResolver::class,
68
        ObjectFieldResolverInterface::class => ObjectFieldResolver::class,
69
        InputObjectFieldResolverInterface::class => InputObjectFieldResolver::class,
70
        ArgumentResolverInterface::class => ArgumentResolver::class,
71
72
        TypeResolver::class => [self::class, 'buildTypeResolver'],
73
        ObjectFieldResolver::class => [self::class, 'buildObjectFieldResolver'],
74
        InputObjectFieldResolver::class => [self::class, 'buildInputObjectFieldResolver'],
75
        ArgumentResolver::class => [self::class, 'buildArgumentResolver'],
76
    ];
77
78
    protected const BINDINGS = [
79
        QueryFieldByReflectionMethodMiddleware::class => [self::class, 'buildQueryFieldByReflectionMethodMiddleware'],
80
        MutationFieldByReflectionMethodMiddleware::class => [self::class, 'buildMutationFieldByReflectionMethodMiddleware'],
81
        AdditionalFieldByReflectionMethodMiddleware::class => [self::class, 'buildAdditionalFieldByReflectionMethodMiddleware'],
82
83
        ObjectFieldByReflectionMethodMiddleware::class => [self::class, 'buildObjectFieldByReflectionMethodMiddleware'],
84
        InterfaceFieldByReflectionMethodMiddleware::class => [self::class, 'buildInterfaceFieldByReflectionMethodMiddleware'],
85
    ];
86
87 7
    public function __construct(
88
        private readonly ConfiguratorInterface $configurator,
89
    ) {
90 7
    }
91
92 7
    public function init(
93
        AbstractKernel $kernel,
94
        EnvironmentInterface $env,
95
        HttpBootloader $http,
96
        ConsoleBootloader $console,
97
    ): void {
98 7
        $this->configurator->setDefaults(GraphQLConfig::CONFIG, [
99 7
            'url' => $env->get('GRAPHQL_URL', '/api/graphql'),
100 7
        ]);
101
102 7
        if (!empty($this->configurator->getConfig('graphql')['url'])) {
103 7
            $http->addMiddleware(GraphQLMiddleware::class);
104
        }
105
106 7
        $console->addCommand(ConfigCommand::class);
107 7
        $kernel->bootstrapped(static function (Schema $schema): void {
108 7
            SchemaWarmupper::warmup($schema);
109 7
        });
110
    }
111
112 7
    public function boot(
113
        GraphQLConfig $config,
114
        TypeRegistryInterface $typeRegistry,
115
        TypeResolverInterface $typeResolver,
116
        TokenizerListenerRegistryInterface $listenerRegistry,
117
        AttributedTypeLoaderListener $attributedTypeLoaderListener,
118
        TypeLoaderListener $typeLoaderListener,
119
        QueryFieldListener $queryFieldListener,
120
        MutationFieldListener $mutationFieldListener,
121
        AttributedQueryFieldListener $attributedQueryFieldListener,
122
        AttributedMutationFieldListener $attributedMutationFieldListener,
123
        AdditionalFieldListener $additionalFieldListener,
124
    ): void {
125 7
        $this->registerQueryType($config->getQueryType(), $typeRegistry, $typeResolver);
126 7
        $this->registerMutationType($config->getMutationType(), $typeRegistry, $typeResolver);
127 7
        $this->registerAdditionalTypes($config->getAdditionalTypes(), $typeRegistry, $typeResolver);
128
129 7
        $listenerRegistry->addListener($attributedTypeLoaderListener);
130 7
        $listenerRegistry->addListener($typeLoaderListener);
131 7
        $listenerRegistry->addListener($queryFieldListener);
132 7
        $listenerRegistry->addListener($mutationFieldListener);
133 7
        $listenerRegistry->addListener($attributedQueryFieldListener);
134 7
        $listenerRegistry->addListener($attributedMutationFieldListener);
135 7
        $listenerRegistry->addListener($additionalFieldListener);
136
    }
137
138 7
    private function registerQueryType(
139
        string $class,
140
        TypeRegistryInterface $typeRegistry,
141
        TypeResolverInterface $typeResolver,
142
    ): void {
143 7
        if ($typeRegistry->has($class)) {
144
            return;
145
        }
146
147 7
        if (GraphQLConfig::DEFAULT_QUERY_TYPE === $class) {
148 7
            $class = QueryType::class;
149
        }
150
151 7
        $queryType = $typeResolver->resolve($class);
152 7
        \assert($queryType instanceof Webonyx\NamedType);
153 7
        $typeRegistry->register($queryType, $class);
154
    }
155
156 7
    private function registerMutationType(
157
        ?string $class,
158
        TypeRegistryInterface $typeRegistry,
159
        TypeResolverInterface $typeResolver,
160
    ): void {
161 7
        if (null === $class || $typeRegistry->has($class)) {
162 6
            return;
163
        }
164
165 1
        if (GraphQLConfig::DEFAULT_MUTATION_TYPE === $class) {
166 1
            $class = MutationType::class;
167
        }
168
169 1
        $mutationType = $typeResolver->resolve($class);
170 1
        \assert($mutationType instanceof Webonyx\NamedType);
171 1
        $typeRegistry->register($mutationType, $class);
172
    }
173
174 7
    private function registerAdditionalTypes(
175
        array $types,
176
        TypeRegistryInterface $typeRegistry,
177
        TypeResolverInterface $typeResolver,
178
    ): void {
179 7
        foreach ($types as $name => $aliases) {
180 1
            $aliases = (array)$aliases;
181 1
            if (\is_int($name)) {
182 1
                $name = \array_shift($aliases);
183
            }
184
185 1
            $type = $typeResolver->resolve($name);
186 1
            \assert($type instanceof Webonyx\NamedType);
187 1
            $typeRegistry->register($type, $name, ...$aliases);
188
        }
189
    }
190
191 1
    private function buildStandardServer(ServerConfig $config): StandardServer
192
    {
193 1
        return new StandardServer($config);
194
    }
195
196 3
    private function buildServerConfig(
197
        Schema $schema,
198
        GraphQLConfig $config,
199
        ContainerInterface $container,
200
    ): ServerConfig {
201 3
        $serverConfig = (new ServerConfig())->setSchema($schema);
202
203 3
        $scope = Proxy::create(
204 3
            new \ReflectionClass(ScopeInterface::class),
205 3
            null,
206 3
            new \Spiral\Core\Attribute\Proxy(),
207 3
        );
208 3
        $invoker = Proxy::create(
209 3
            new \ReflectionClass(InvokerInterface::class),
210 3
            null,
211 3
            new \Spiral\Core\Attribute\Proxy(),
212 3
        );
213
214 3
        if ($rootValueName = $config->getRootValue()) {
215 2
            $rootValue = $container->get($rootValueName);
216 2
            $rootValueFn = \is_callable($rootValue)
217 1
                ? $rootValue
218 1
                : static fn() => $rootValue;
219
220 2
            $serverConfig->setRootValue(new ValueResolver($scope, $invoker, $rootValueFn));
221
        }
222
223 3
        if ($contextName = $config->getContext()) {
224 2
            $context = $container->get($contextName);
225 2
            $contextFn = \is_callable($context)
226 1
                ? $context
227 1
                : static fn() => $context;
228
229 2
            $serverConfig->setContext(new ValueResolver($scope, $invoker, $contextFn));
230
        }
231
232 3
        $serverConfig->setDebugFlag($config->getDebugFlag());
233
234 3
        return $serverConfig;
235
    }
236
237 7
    private function buildSchema(SchemaConfig $config): Schema
238
    {
239 7
        return new Schema($config);
240
    }
241
242 7
    private function buildSchemaConfig(
243
        GraphQLConfig $config,
244
        TypeRegistryInterface $typeRegistry,
245
    ): SchemaConfig {
246 7
        $schemaConfig = new SchemaConfig();
247 7
        $schemaConfig->setTypeLoader($typeRegistry->get(...));
248 7
        $schemaConfig->setTypes($typeRegistry->getTypes(...));
249
250 7
        $schemaConfig->setQuery($typeRegistry->get($config->getQueryType()));
251
252 7
        $mutationType = $config->getMutationType();
253 7
        if (null !== $mutationType && $typeRegistry->has($mutationType)) {
254 1
            $schemaConfig->setMutation($typeRegistry->get($mutationType));
255
        }
256
257 7
        return $schemaConfig;
258
    }
259
260 7
    private function buildTypeRegistry(): TypeRegistry
261
    {
262 7
        return new TypeRegistry();
263
    }
264
265 7
    private function buildTypeResolver(
266
        ContainerInterface $container,
267
        GraphQLConfig $config,
268
    ): TypeResolver {
269 7
        $typeResolver = new TypeResolver();
270
271 7
        foreach ($config->getTypeResolverMiddlewares() as $name => $priority) {
272 7
            $typeResolver->pipe($container->get($name), $priority);
273
        }
274
275 7
        return $typeResolver;
276
    }
277
278 7
    private function buildObjectFieldResolver(
279
        ContainerInterface $container,
280
        GraphQLConfig $config,
281
    ): ObjectFieldResolver {
282 7
        $objectFieldResolver = new ObjectFieldResolver();
283
284 7
        foreach ($config->getObjectFieldResolverMiddlewares() as $name => $priority) {
285 7
            $objectFieldResolver->pipe($container->get($name), $priority);
286
        }
287
288 7
        return $objectFieldResolver;
289
    }
290
291 7
    private function buildInputObjectFieldResolver(
292
        ContainerInterface $container,
293
        GraphQLConfig $config,
294
    ): InputObjectFieldResolver {
295 7
        $inputObjectFieldResolver = new InputObjectFieldResolver();
296
297 7
        foreach ($config->getInputObjectFieldResolverMiddlewares() as $name => $priority) {
298 7
            $inputObjectFieldResolver->pipe($container->get($name), $priority);
299
        }
300
301 7
        return $inputObjectFieldResolver;
302
    }
303
304 7
    private function buildArgumentResolver(
305
        ContainerInterface $container,
306
        GraphQLConfig $config,
307
    ): ArgumentResolver {
308 7
        $argumentResolver = new ArgumentResolver();
309
310 7
        foreach ($config->getArgumentResolverMiddlewares() as $name => $priority) {
311 7
            $argumentResolver->pipe($container->get($name), $priority);
312
        }
313
314 7
        return $argumentResolver;
315
    }
316
317 7
    private function buildQueryFieldByReflectionMethodMiddleware(
318
        ReaderInterface $reader,
319
        TypeRegistryInterface $typeRegistry,
320
        ArgumentResolverInterface $argumentResolver,
321
    ): QueryFieldByReflectionMethodMiddleware {
322 7
        return $this->buildAbstractOuterObjectFieldByReflectionMethodMiddleware(
323 7
            QueryFieldByReflectionMethodMiddleware::class,
324 7
            $reader,
325 7
            $typeRegistry,
326 7
            $argumentResolver,
327 7
        );
328
    }
329
330 7
    private function buildMutationFieldByReflectionMethodMiddleware(
331
        ReaderInterface $reader,
332
        TypeRegistryInterface $typeRegistry,
333
        ArgumentResolverInterface $argumentResolver,
334
    ): MutationFieldByReflectionMethodMiddleware {
335 7
        return $this->buildAbstractOuterObjectFieldByReflectionMethodMiddleware(
336 7
            MutationFieldByReflectionMethodMiddleware::class,
337 7
            $reader,
338 7
            $typeRegistry,
339 7
            $argumentResolver,
340 7
        );
341
    }
342
343 7
    private function buildAdditionalFieldByReflectionMethodMiddleware(
344
        ReaderInterface $reader,
345
        TypeRegistryInterface $typeRegistry,
346
        ArgumentResolverInterface $argumentResolver,
347
    ): AdditionalFieldByReflectionMethodMiddleware {
348 7
        return $this->buildAbstractOuterObjectFieldByReflectionMethodMiddleware(
349 7
            AdditionalFieldByReflectionMethodMiddleware::class,
350 7
            $reader,
351 7
            $typeRegistry,
352 7
            $argumentResolver,
353 7
        );
354
    }
355
356
    /**
357
     * @param class-string<AbstractOuterObjectFieldByReflectionMethodMiddleware> $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<AbstractOut...ectionMethodMiddleware> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<AbstractOuterObjectFieldByReflectionMethodMiddleware>.
Loading history...
358
     */
359 7
    private function buildAbstractOuterObjectFieldByReflectionMethodMiddleware(
360
        string $class,
361
        ReaderInterface $reader,
362
        TypeRegistryInterface $typeRegistry,
363
        ArgumentResolverInterface $argumentResolver,
364
    ): AbstractOuterObjectFieldByReflectionMethodMiddleware {
365 7
        $scope = Proxy::create(
366 7
            new \ReflectionClass(ScopeInterface::class),
367 7
            null,
368 7
            new \Spiral\Core\Attribute\Proxy(),
369 7
        );
370 7
        $invoker = Proxy::create(
371 7
            new \ReflectionClass(InvokerInterface::class),
372 7
            null,
373 7
            new \Spiral\Core\Attribute\Proxy(),
374 7
        );
375
376 7
        return new $class(
377 7
            $reader,
378 7
            $typeRegistry,
379 7
            $argumentResolver,
380 7
            $scope,
381 7
            $invoker,
382 7
        );
383
    }
384
385 7
    private function buildObjectFieldByReflectionMethodMiddleware(
386
        ReaderInterface $reader,
387
        TypeRegistryInterface $typeRegistry,
388
        ArgumentResolverInterface $argumentResolver,
389
    ): ObjectFieldByReflectionMethodMiddleware {
390 7
        return $this->buildAbstractObjectFieldByReflectionMethodMiddleware(
391 7
            ObjectFieldByReflectionMethodMiddleware::class,
392 7
            $reader,
393 7
            $typeRegistry,
394 7
            $argumentResolver,
395 7
        );
396
    }
397
398
399 7
    private function buildInterfaceFieldByReflectionMethodMiddleware(
400
        ReaderInterface $reader,
401
        TypeRegistryInterface $typeRegistry,
402
        ArgumentResolverInterface $argumentResolver,
403
    ): InterfaceFieldByReflectionMethodMiddleware {
404 7
        return $this->buildAbstractObjectFieldByReflectionMethodMiddleware(
405 7
            InterfaceFieldByReflectionMethodMiddleware::class,
406 7
            $reader,
407 7
            $typeRegistry,
408 7
            $argumentResolver,
409 7
        );
410
    }
411
412
    /**
413
     * @param class-string<AbstractObjectFieldByReflectionMethodMiddleware> $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<AbstractObj...ectionMethodMiddleware> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<AbstractObjectFieldByReflectionMethodMiddleware>.
Loading history...
414
     */
415 7
    private function buildAbstractObjectFieldByReflectionMethodMiddleware(
416
        string $class,
417
        ReaderInterface $reader,
418
        TypeRegistryInterface $typeRegistry,
419
        ArgumentResolverInterface $argumentResolver,
420
    ): AbstractObjectFieldByReflectionMethodMiddleware {
421 7
        $scope = Proxy::create(
422 7
            new \ReflectionClass(ScopeInterface::class),
423 7
            null,
424 7
            new \Spiral\Core\Attribute\Proxy(),
425 7
        );
426 7
        $invoker = Proxy::create(
427 7
            new \ReflectionClass(InvokerInterface::class),
428 7
            null,
429 7
            new \Spiral\Core\Attribute\Proxy(),
430 7
        );
431
432 7
        return new $class(
433 7
            $reader,
434 7
            $typeRegistry,
435 7
            $argumentResolver,
436 7
            $scope,
437 7
            $invoker,
438 7
        );
439
    }
440
}
441