Passed
Push — master ( e9abae...70f275 )
by Daniel
02:24
created

Endpoint::factory()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 2.0011

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 18
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 29
ccs 14
cts 15
cp 0.9333
crap 2.0011
rs 9.6666
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\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Message\StreamFactoryInterface;
13
use Psr\Http\Server\RequestHandlerInterface;
14
use Psr\Log\LoggerInterface;
15
use Ramsey\Uuid\UuidFactory;
16
use Ramsey\Uuid\UuidFactoryInterface;
17
use Ramsey\Uuid\UuidInterface;
18
use Teapot\StatusCode;
19
use Throwable;
20
use Usox\JsonSchemaApi\Contract\MethodProviderInterface;
21
use Usox\JsonSchemaApi\Exception\ApiException;
22
use Usox\JsonSchemaApi\Exception\InternalException;
23
use Usox\JsonSchemaApi\Dispatch\MethodDispatcher;
24
use Usox\JsonSchemaApi\Dispatch\MethodDispatcherInterface;
25
use Usox\JsonSchemaApi\Dispatch\MethodValidator;
26
use Usox\JsonSchemaApi\Dispatch\RequestValidator;
27
use Usox\JsonSchemaApi\Dispatch\RequestValidatorInterface;
28
use Usox\JsonSchemaApi\Dispatch\SchemaLoader;
29
use Usox\JsonSchemaApi\Response\ResponseBuilder;
30
use Usox\JsonSchemaApi\Response\ResponseBuilderInterface;
31
32
final class Endpoint implements
33
    EndpointInterface
34
{
35
    private RequestValidatorInterface $inputValidator;
36
37
    private MethodDispatcherInterface $methodRetriever;
38
39
    private ResponseBuilderInterface $responseBuilder;
40
41
    private UuidFactoryInterface $uuidFactory;
42
43
    private StreamFactoryInterface $streamFactory;
44
45
    private ?LoggerInterface $logger;
46
47 7
    public function __construct(
48
        RequestValidatorInterface $inputValidator,
49
        MethodDispatcherInterface $methodRetriever,
50
        ResponseBuilderInterface $responseBuilder,
51
        UuidFactoryInterface $uuidFactory,
52
        StreamFactoryInterface $streamFactory,
53
        ?LoggerInterface $logger = null
54
    ) {
55 7
        $this->inputValidator = $inputValidator;
56 7
        $this->methodRetriever = $methodRetriever;
57 7
        $this->responseBuilder = $responseBuilder;
58 7
        $this->uuidFactory = $uuidFactory;
59 7
        $this->streamFactory = $streamFactory;
60 7
        $this->logger = $logger;
61 7
    }
62
63
    /**
64
     * Try to execute the api handler and build the response
65
     */
66 6
    public function serve(
67
        ServerRequestInterface $request,
68
        ResponseInterface $response
69
    ): ResponseInterface {
70 6
        $statusCode = StatusCode::OK;
71
72
        try {
73
            // Process and build the response
74 6
            $responseData = $this->responseBuilder->buildResponse(
75 6
                $this->methodRetriever->dispatch(
76 6
                    $request,
77 6
                    $this->inputValidator->validate($request)
78
                )
79
            );
80 4
        } catch (ApiException $e) {
81 2
            $uuid = $this->uuidFactory->uuid4();
82
83 2
            $this->log($e, $uuid);
84
85
            // Build an error response
86 2
            $responseData = $this->responseBuilder->buildErrorResponse($e, $uuid);
87
88 2
            $statusCode = StatusCode::BAD_REQUEST;
89 2
        } catch (InternalException $e) {
90 1
            $uuid = $this->uuidFactory->uuid4();
91
92 1
            $this->log($e, $uuid, $e->getContext());
93
94 1
            $responseData = '';
95 1
            $statusCode = StatusCode::INTERNAL_SERVER_ERROR;
96 1
        } catch (Throwable $e) {
97 1
            $uuid = $this->uuidFactory->uuid4();
98
99 1
            $this->log($e, $uuid);
100
101 1
            $responseData = '';
102 1
            $statusCode = StatusCode::INTERNAL_SERVER_ERROR;
103
        }
104
105
        return $response
106 6
            ->withHeader('Content-Type', 'application/json')
107 6
            ->withStatus($statusCode)
108 6
            ->withBody(
109 6
                $this->streamFactory->createStream(
110 6
                    (string) json_encode($responseData)
111
                )
112
            );
113
    }
114
115
    /**
116
     * @param Throwable $e
117
     * @param UuidInterface $uuid
118
     * @param array<mixed, mixed> $context
119
     */
120 4
    private function log(
121
        Throwable $e,
122
        UuidInterface $uuid,
123
        array $context = []
124
    ): void {
125 4
        if ($this->logger !== null) {
126 4
            $this->logger->error(
127 4
                sprintf('%s (%d)', $e->getMessage(), $e->getCode()),
128
                [
129 4
                    'id' => $uuid->toString(),
130 4
                    'file' => $e->getFile(),
131 4
                    'line' => $e->getLine(),
132 4
                    'context' => $context
133
                ]
134
            );
135
        }
136 4
    }
137
138
    /**
139
     * Builds the endpoint.
140
     * The StreamFactory can be omitted, the endpoint will search
141
     * for any existing PSR17 implementations
142
     */
143 1
    public static function factory(
144
        MethodProviderInterface $methodProvider,
145
        ?StreamFactoryInterface $streamFactory = null,
146
        ?LoggerInterface $logger = null
147
    ): EndpointInterface {
148 1
        $schemaValidator = new Validator();
149 1
        $schemaLoader = new SchemaLoader();
150
151 1
        if ($streamFactory === null) {
152
            $streamFactory = Psr17FactoryDiscovery::findStreamFactory();
153
        }
154
155 1
        return new self(
156 1
            new RequestValidator(
157 1
                $schemaLoader,
158
                $schemaValidator
159
            ),
160 1
            new MethodDispatcher(
161 1
                $schemaLoader,
162 1
                new MethodValidator(
163 1
                    $schemaValidator,
164 1
                    new ErrorFormatter()
165
                ),
166
                $methodProvider
167
            ),
168 1
            new ResponseBuilder(),
169 1
            new UuidFactory(),
170
            $streamFactory,
171
            $logger
172
        );
173
    }
174
175 1
    public function process(
176
        ServerRequestInterface $request,
177
        RequestHandlerInterface $handler
178
    ): ResponseInterface {
179 1
        return $this->serve(
180 1
            $request,
181 1
            $handler->handle($request)
182
        );
183
    }
184
}
185