Passed
Push — master ( 92cace...514db2 )
by Daniel
03:05 queued 10s
created

Endpoint::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
c 1
b 0
f 0
nc 1
nop 6
dl 0
loc 8
ccs 1
cts 1
cp 1
crap 1
rs 10
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 7
    public function __construct(
36
        private RequestValidatorInterface $inputValidator,
37
        private MethodDispatcherInterface $methodRetriever,
38
        private ResponseBuilderInterface $responseBuilder,
39
        private UuidFactoryInterface $uuidFactory,
40
        private StreamFactoryInterface $streamFactory,
41
        private ?LoggerInterface $logger = null
42
    ) {
43 7
    }
44
45
    /**
46
     * Try to execute the api handler and build the response
47
     */
48 6
    public function serve(
49
        ServerRequestInterface $request,
50
        ResponseInterface $response
51
    ): ResponseInterface {
52 6
        $statusCode = StatusCode::OK;
53 6
        $responseData = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $responseData is dead and can be removed.
Loading history...
54
55
        try {
56
            // Process and build the response
57 6
            $responseData = $this->responseBuilder->buildResponse(
58 6
                $this->methodRetriever->dispatch(
59 6
                    $request,
60 6
                    $this->inputValidator->validate($request)
61
                )
62
            );
63 4
        } catch (ApiException $e) {
64 2
            $uuid = $this->uuidFactory->uuid4();
65
66 2
            $this->logError($e, $uuid);
67
68
            // Build an error response
69 2
            $responseData = $this->responseBuilder->buildErrorResponse($e, $uuid);
70
71 2
            $statusCode = StatusCode::BAD_REQUEST;
72 2
        } catch (InternalException $e) {
73 1
            $this->logError(
74 1
                $e,
75 1
                $this->uuidFactory->uuid4(),
76 1
                $e->getContext()
77
            );
78
79 1
            $statusCode = StatusCode::INTERNAL_SERVER_ERROR;
80 1
        } catch (Throwable $e) {
81 1
            $this->logError(
82 1
                $e,
83 1
                $this->uuidFactory->uuid4()
84
            );
85
86 1
            $statusCode = StatusCode::INTERNAL_SERVER_ERROR;
87
        }
88
89 6
        if ($responseData !== null) {
0 ignored issues
show
introduced by
The condition $responseData !== null is always true.
Loading history...
90 4
            $response = $response->withBody(
91 4
                $this->streamFactory->createStream(
92 4
                    (string) json_encode($responseData)
93
                )
94
            );
95
        }
96
97
        return $response
98 6
            ->withHeader('Content-Type', 'application/json')
99 6
            ->withStatus($statusCode);
100
    }
101
102
    /**
103
     * @param array<mixed, mixed> $context
104
     */
105 4
    private function logError(
106
        Throwable $e,
107
        UuidInterface $uuid,
108
        array $context = []
109
    ): void {
110 4
        $this->logger?->error(
111 4
            sprintf('%s (%d)', $e->getMessage(), $e->getCode()),
112
            [
113 4
                'id' => $uuid->toString(),
114 4
                'file' => $e->getFile(),
115 4
                'line' => $e->getLine(),
116 4
                'context' => $context
117
            ]
118
        );
119 4
    }
120
121
    /**
122
     * Builds the endpoint.
123
     * The StreamFactory can be omitted, the endpoint will search
124
     * for any existing PSR17 implementations
125
     */
126 1
    public static function factory(
127
        MethodProviderInterface $methodProvider,
128
        ?StreamFactoryInterface $streamFactory = null,
129
        ?LoggerInterface $logger = null
130
    ): EndpointInterface {
131 1
        $schemaValidator = new Validator();
132 1
        $schemaLoader = new SchemaLoader();
133
134 1
        if ($streamFactory === null) {
135
            $streamFactory = Psr17FactoryDiscovery::findStreamFactory();
136
        }
137
138 1
        return new self(
139 1
            new RequestValidator(
140 1
                $schemaLoader,
141
                $schemaValidator
142
            ),
143 1
            new MethodDispatcher(
144 1
                $schemaLoader,
145 1
                new MethodValidator(
146 1
                    $schemaValidator,
147 1
                    new ErrorFormatter()
148
                ),
149
                $methodProvider,
150
                $logger,
151
            ),
152 1
            new ResponseBuilder(),
153 1
            new UuidFactory(),
154
            $streamFactory,
155
            $logger
156
        );
157
    }
158
159 1
    public function process(
160
        ServerRequestInterface $request,
161
        RequestHandlerInterface $handler
162
    ): ResponseInterface {
163 1
        return $this->serve(
164 1
            $request,
165 1
            $handler->handle($request)
166
        );
167
    }
168
}
169