Passed
Push — master ( aad48e...92cace )
by Daniel
02:22
created

Endpoint::logError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 3
dl 0
loc 12
ccs 7
cts 7
cp 1
crap 1
rs 10
c 0
b 0
f 0
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
            $uuid = $this->uuidFactory->uuid4();
74
75 1
            $this->logError($e, $uuid, $e->getContext());
76
77 1
            $statusCode = StatusCode::INTERNAL_SERVER_ERROR;
78 1
        } catch (Throwable $e) {
79 1
            $uuid = $this->uuidFactory->uuid4();
80
81 1
            $this->logError($e, $uuid);
82
83 1
            $statusCode = StatusCode::INTERNAL_SERVER_ERROR;
84
        }
85
86 6
        if ($responseData !== null) {
0 ignored issues
show
introduced by
The condition $responseData !== null is always true.
Loading history...
87 4
            $response = $response->withBody(
88 4
                $this->streamFactory->createStream(
89 4
                    (string) json_encode($responseData)
90
                )
91
            );
92
        }
93
94
        return $response
95 6
            ->withHeader('Content-Type', 'application/json')
96 6
            ->withStatus($statusCode);
97
    }
98
99
    /**
100
     * @param array<mixed, mixed> $context
101
     */
102 4
    private function logError(
103
        Throwable $e,
104
        UuidInterface $uuid,
105
        array $context = []
106
    ): void {
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 4
    }
117
118
    /**
119
     * Builds the endpoint.
120
     * The StreamFactory can be omitted, the endpoint will search
121
     * for any existing PSR17 implementations
122
     */
123 1
    public static function factory(
124
        MethodProviderInterface $methodProvider,
125
        ?StreamFactoryInterface $streamFactory = null,
126
        ?LoggerInterface $logger = null
127
    ): EndpointInterface {
128 1
        $schemaValidator = new Validator();
129 1
        $schemaLoader = new SchemaLoader();
130
131 1
        if ($streamFactory === null) {
132
            $streamFactory = Psr17FactoryDiscovery::findStreamFactory();
133
        }
134
135 1
        return new self(
136 1
            new RequestValidator(
137 1
                $schemaLoader,
138
                $schemaValidator
139
            ),
140 1
            new MethodDispatcher(
141 1
                $schemaLoader,
142 1
                new MethodValidator(
143 1
                    $schemaValidator,
144 1
                    new ErrorFormatter()
145
                ),
146
                $methodProvider,
147
                $logger,
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