Issues (6)

src/Endpoint.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Usox\JsonSchemaApi;
6
7
use Http\Discovery\Psr17FactoryDiscovery;
8
use Opis\JsonSchema\Errors\ErrorFormatter;
9
use Opis\JsonSchema\Validator;
10
use Psr\Http\Message\ResponseFactoryInterface;
11
use Psr\Http\Message\ResponseInterface;
12
use Psr\Http\Message\ServerRequestInterface;
13
use Psr\Http\Message\StreamFactoryInterface;
14
use Psr\Log\LoggerInterface;
15
use Ramsey\Uuid\UuidFactory;
16
use Ramsey\Uuid\UuidFactoryInterface;
17
use Ramsey\Uuid\UuidInterface;
18
use Teapot\StatusCode\Http;
19
use Throwable;
20
use Usox\JsonSchemaApi\Contract\MethodProviderInterface;
21
use Usox\JsonSchemaApi\Dispatch\MethodDispatcher;
22
use Usox\JsonSchemaApi\Dispatch\MethodDispatcherInterface;
23
use Usox\JsonSchemaApi\Dispatch\MethodValidator;
24
use Usox\JsonSchemaApi\Dispatch\RequestValidator;
25
use Usox\JsonSchemaApi\Dispatch\RequestValidatorInterface;
26
use Usox\JsonSchemaApi\Dispatch\SchemaLoader;
27
use Usox\JsonSchemaApi\Exception\ApiException;
28
use Usox\JsonSchemaApi\Exception\InternalException;
29
use Usox\JsonSchemaApi\Response\ResponseBuilder;
30
use Usox\JsonSchemaApi\Response\ResponseBuilderInterface;
31
32
/**
33
 * @see Endpoint::factory()
34
 */
35
final readonly class Endpoint implements
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_READONLY, expecting T_CLASS on line 35 at column 6
Loading history...
36
    EndpointInterface
37
{
38 8
    public function __construct(
39
        private RequestValidatorInterface $inputValidator,
40
        private MethodDispatcherInterface $methodRetriever,
41
        private ResponseBuilderInterface $responseBuilder,
42
        private UuidFactoryInterface $uuidFactory,
43
        private StreamFactoryInterface $streamFactory,
44
        private ResponseFactoryInterface $responseFactory,
45
        private ?LoggerInterface $logger = null,
46
    ) {
47 8
    }
48
49
    /**
50
     * Execute the api handler and build the response
51
     */
52 6
    public function serve(
53
        ServerRequestInterface $request,
54
    ): ResponseInterface {
55 6
        $statusCode = Http::OK;
56 6
        $responseData = null;
57
58
        try {
59
            // Process and build the response
60 6
            $responseData = $this->responseBuilder->buildResponse(
61 6
                $this->methodRetriever->dispatch(
62 6
                    $request,
63 6
                    $this->inputValidator->validate($request),
64 6
                ),
65 6
            );
66 4
        } catch (ApiException $e) {
67 2
            $uuid = $this->uuidFactory->uuid4();
68
69 2
            $this->logError($e, $uuid);
70
71
            // Build an error response
72 2
            $responseData = $this->responseBuilder->buildErrorResponse($e, $uuid);
73
74 2
            $statusCode = Http::BAD_REQUEST;
75 2
        } catch (InternalException $e) {
76 1
            $this->logError(
77 1
                $e,
78 1
                $this->uuidFactory->uuid4(),
79 1
                $e->getContext(),
80 1
            );
81
82 1
            $statusCode = Http::INTERNAL_SERVER_ERROR;
83 1
        } catch (Throwable $e) {
84 1
            $this->logError(
85 1
                $e,
86 1
                $this->uuidFactory->uuid4(),
87 1
            );
88
89 1
            $statusCode = Http::INTERNAL_SERVER_ERROR;
90
        }
91
92 6
        $response = $this->responseFactory->createResponse($statusCode);
93
94 6
        if ($responseData !== null) {
95 4
            $response = $response->withBody(
96 4
                $this->streamFactory->createStream(
97 4
                    (string) json_encode($responseData),
98 4
                ),
99 4
            );
100
        }
101
102 6
        return $response
103 6
            ->withHeader('Content-Type', 'application/json')
104 6
        ;
105
    }
106
107
    /**
108
     * @param array<mixed, mixed> $context
109
     */
110 4
    private function logError(
111
        Throwable $e,
112
        UuidInterface $uuid,
113
        array $context = [],
114
    ): void {
115 4
        $this->logger?->error(
116 4
            sprintf('%s (%d)', $e->getMessage(), $e->getCode()),
117 4
            [
118 4
                'id' => $uuid->toString(),
119 4
                'file' => $e->getFile(),
120 4
                'line' => $e->getLine(),
121 4
                'context' => $context,
122 4
            ],
123 4
        );
124
    }
125
126
    /**
127
     * Builds the endpoint.
128
     *
129
     * The factories may be omitted, the endpoint will try to autodetect existing PSR17 implementations
130
     */
131 2
    public static function factory(
132
        MethodProviderInterface $methodProvider,
133
        ?StreamFactoryInterface $streamFactory = null,
134
        ?ResponseFactoryInterface $responseFactory = null,
135
        ?LoggerInterface $logger = null,
136
    ): EndpointInterface {
137 2
        $schemaValidator = new Validator();
138 2
        $schemaLoader = new SchemaLoader();
139
140 2
        if ($streamFactory === null) {
141 1
            $streamFactory = Psr17FactoryDiscovery::findStreamFactory();
142
        }
143 2
144 1
        if ($responseFactory === null) {
145
            $responseFactory = Psr17FactoryDiscovery::findResponseFactory();
146
        }
147 2
148 2
        return new self(
149 2
            new RequestValidator(
150 2
                $schemaLoader,
151 2
                $schemaValidator,
152 2
            ),
153 2
            new MethodDispatcher(
154 2
                $schemaLoader,
155 2
                new MethodValidator(
156 2
                    $schemaValidator,
157 2
                    new ErrorFormatter(),
158 2
                ),
159 2
                $methodProvider,
160 2
                $logger,
161 2
            ),
162 2
            new ResponseBuilder(),
163 2
            new UuidFactory(),
164 2
            $streamFactory,
165 2
            $responseFactory,
166 2
            $logger,
167
        );
168
    }
169 1
170
    public function handle(
171
        ServerRequestInterface $request,
172 1
    ): ResponseInterface {
173 1
        return $this->serve(
174 1
            $request,
175
        );
176
    }
177
}
178