Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
73.33% covered (warning)
73.33%
22 / 30
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
GraphqlController
73.33% covered (warning)
73.33%
22 / 30
33.33% covered (danger)
33.33%
1 / 3
5.47
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
70.83% covered (warning)
70.83%
17 / 24
0.00% covered (danger)
0.00%
0 / 1
2.10
 parseRequest
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
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 Nyholm\Psr7\Factory\Psr17Factory;
16use Psr\Http\Message\ResponseInterface;
17use Psr\Http\Message\ServerRequestInterface;
18
19final class GraphqlController
20{
21    public function __construct(
22        private readonly GraphqlSchemaFactory $schemaFactory,
23        private readonly ContextBuilderFactory $contextBuilderFactory,
24        private readonly ResponseDispatcher $responseDispatcher
25    ) {
26    }
27
28    public function __invoke(ServerRequestInterface $request): ResponseInterface
29    {
30        $context = $this->contextBuilderFactory->createFromRequest($request, [ContextConstants::GRAPHQL => true]);
31
32        try {
33            $payload = $this->parseRequest($request);
34
35            $result = GraphQL::executeQuery(
36                schema: $this->schemaFactory->createSchema($context),
37                source: $payload['query'] ?? '',
38                rootValue: $context,
39                variableValues: $payload['variables'] ?? null,
40                operationName: $payload['operationName'] ?? null
41            );
42
43            $output = $result->toArray(DebugFlag::INCLUDE_DEBUG_MESSAGE|DebugFlag::INCLUDE_TRACE);
44        } catch (\Throwable $e) {
45            IntegrationTestLogger::logException($e);
46            $output = [
47                'errors' => [
48                    FormattedError::createFromException($e),
49                ],
50            ];
51        }
52
53        $psr17Factory = new Psr17Factory();
54        $responseBody = $psr17Factory->createStream(json_encode($output, JSON_THROW_ON_ERROR));
55
56        $response = $psr17Factory->createResponse(200)
57            ->withBody($responseBody)
58            ->withHeader('Content-Type', 'application/json');
59        $response = $this->responseDispatcher->triggerResponseCreated($response, $context);
60
61        return $response;
62    }
63
64    private function parseRequest(ServerRequestInterface $request): array
65    {
66        $contentType = $request->getHeaderLine('Content-Type');
67
68        if (str_contains($contentType, 'application/json')) {
69            $raw = (string) $request->getBody();
70            return json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
71        }
72
73        return $request->getParsedBody() ?? [];
74    }
75}