Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.38% covered (warning)
78.38%
29 / 37
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
GraphqlController
78.38% covered (warning)
78.38%
29 / 37
50.00% covered (danger)
50.00%
1 / 2
4.16
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __invoke
77.78% covered (warning)
77.78%
28 / 36
0.00% covered (danger)
0.00%
0 / 1
3.10
1<?php
2
3declare(strict_types=1);
4
5namespace Apie\Graphql\Controllers;
6
7use Apie\Common\Events\ResponseDispatcher;
8use Apie\Common\IntegrationTestLogger;
9use Apie\Core\ContextBuilders\ContextBuilderFactory;
10use Apie\Core\ContextConstants;
11use Apie\Graphql\Factories\GraphqlSchemaFactory;
12use GraphQL\Error\DebugFlag;
13use GraphQL\Error\FormattedError;
14use GraphQL\GraphQL;
15use GraphQL\Upload\UploadMiddleware;
16use Nyholm\Psr7\Factory\Psr17Factory;
17use Psr\Http\Message\ResponseInterface;
18use Psr\Http\Message\ServerRequestInterface;
19use Psr\Http\Server\RequestHandlerInterface;
20
21final class GraphqlController
22{
23    public function __construct(
24        private readonly GraphqlSchemaFactory $schemaFactory,
25        private readonly ContextBuilderFactory $contextBuilderFactory,
26        private readonly ResponseDispatcher $responseDispatcher
27    ) {
28    }
29
30    public function __invoke(ServerRequestInterface $request): ResponseInterface
31    {
32        
33        $uploadMiddleware = new UploadMiddleware();
34
35        return $uploadMiddleware->process(
36            $request,
37            new class($this->schemaFactory, $this->contextBuilderFactory, $this->responseDispatcher) implements RequestHandlerInterface {
38                public function __construct(
39                    private readonly GraphqlSchemaFactory $schemaFactory,
40                    private readonly ContextBuilderFactory $contextBuilderFactory,
41                    private readonly ResponseDispatcher $responseDispatcher
42                ) {
43                }
44
45                public function handle(ServerRequestInterface $request): ResponseInterface
46                {
47                    $payload = $this->parseRequest($request);
48                    $context = $this->contextBuilderFactory->createFromRequest($request, [ContextConstants::GRAPHQL => true]);
49                    try {
50                        $result = GraphQL::executeQuery(
51                            schema: $this->schemaFactory->createSchema($context),
52                            source: $payload['query'] ?? '',
53                            rootValue: $context,
54                            variableValues: $payload['variables'] ?? null,
55                            operationName: $payload['operationName'] ?? null
56                        );
57
58                        $output = $result->toArray(DebugFlag::INCLUDE_DEBUG_MESSAGE|DebugFlag::INCLUDE_TRACE);
59                    } catch (\Throwable $e) {
60                        IntegrationTestLogger::logException($e);
61                        $output = [
62                            'errors' => [
63                                FormattedError::createFromException($e),
64                            ],
65                        ];
66                    }
67
68                    $psr17Factory = new Psr17Factory();
69                    $responseBody = $psr17Factory->createStream(json_encode($output, JSON_THROW_ON_ERROR));
70
71                    $response = $psr17Factory->createResponse(200)
72                        ->withBody($responseBody)
73                        ->withHeader('Content-Type', 'application/json');
74                    $response = $this->responseDispatcher->triggerResponseCreated($response, $context);
75
76                    return $response;
77                }
78
79                private function parseRequest(ServerRequestInterface $request): array
80                {
81                    $contentType = $request->getHeaderLine('Content-Type');
82
83                    if (str_contains($contentType, 'application/json')) {
84                        $raw = (string) $request->getBody();
85                        return json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
86                    }
87
88                    return $request->getParsedBody() ?? [];
89                }
90            }
91        );
92
93    }
94}