Passed
Push — master ( 70f275...3a5d69 )
by Daniel
04:17
created

Endpoint::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
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 9
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
    {
44 7
    }
45
46
    /**
47
     * Try to execute the api handler and build the response
48
     */
49 6
    public function serve(
50
        ServerRequestInterface $request,
51
        ResponseInterface $response
52
    ): ResponseInterface {
53 6
        $statusCode = StatusCode::OK;
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->log($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
            $uuid = $this->uuidFactory->uuid4();
74
75 1
            $this->log($e, $uuid, $e->getContext());
76
77 1
            $responseData = '';
78 1
            $statusCode = StatusCode::INTERNAL_SERVER_ERROR;
79 1
        } catch (Throwable $e) {
80 1
            $uuid = $this->uuidFactory->uuid4();
81
82 1
            $this->log($e, $uuid);
83
84 1
            $responseData = '';
85 1
            $statusCode = StatusCode::INTERNAL_SERVER_ERROR;
86
        }
87
88
        return $response
89 6
            ->withHeader('Content-Type', 'application/json')
90 6
            ->withStatus($statusCode)
91 6
            ->withBody(
92 6
                $this->streamFactory->createStream(
93 6
                    (string) json_encode($responseData)
94
                )
95
            );
96
    }
97
98
    /**
99
     * @param array<mixed, mixed> $context
100
     */
101 4
    private function log(
102
        Throwable $e,
103
        UuidInterface $uuid,
104
        array $context = []
105
    ): void {
106 4
        if ($this->logger !== null) {
107 4
            $this->logger->error(
108 4
                sprintf('%s (%d)', $e->getMessage(), $e->getCode()),
109
                [
110 4
                    'id' => $uuid->toString(),
111 4
                    'file' => $e->getFile(),
112 4
                    'line' => $e->getLine(),
113 4
                    'context' => $context
114
                ]
115
            );
116
        }
117 4
    }
118
119
    /**
120
     * Builds the endpoint.
121
     * The StreamFactory can be omitted, the endpoint will search
122
     * for any existing PSR17 implementations
123
     */
124 1
    public static function factory(
125
        MethodProviderInterface $methodProvider,
126
        ?StreamFactoryInterface $streamFactory = null,
127
        ?LoggerInterface $logger = null
128
    ): EndpointInterface {
129 1
        $schemaValidator = new Validator();
130 1
        $schemaLoader = new SchemaLoader();
131
132 1
        if ($streamFactory === null) {
133
            $streamFactory = Psr17FactoryDiscovery::findStreamFactory();
134
        }
135
136 1
        return new self(
137 1
            new RequestValidator(
138 1
                $schemaLoader,
139
                $schemaValidator
140
            ),
141 1
            new MethodDispatcher(
142 1
                $schemaLoader,
143 1
                new MethodValidator(
144 1
                    $schemaValidator,
145 1
                    new ErrorFormatter()
146
                ),
147
                $methodProvider
148
            ),
149 1
            new ResponseBuilder(),
150 1
            new UuidFactory(),
151
            $streamFactory,
152
            $logger
153
        );
154
    }
155
156 1
    public function process(
157
        ServerRequestInterface $request,
158
        RequestHandlerInterface $handler
159
    ): ResponseInterface {
160 1
        return $this->serve(
161 1
            $request,
162 1
            $handler->handle($request)
163
        );
164
    }
165
}
166