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

GraphQLBootloader::boot()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 10
nc 1
nop 11
dl 0
loc 24
ccs 11
cts 11
cp 1
crap 1
rs 9.9332
c 1
b 0
f 1

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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