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