Passed
Push — main ( 223ead...e5cf91 )
by Mr.
47s queued 10s
created

Router::dispatch()   B

Complexity

Conditions 8
Paths 14

Size

Total Lines 41
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 10.6235

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 30
nc 14
nop 0
dl 0
loc 41
ccs 19
cts 29
cp 0.6552
crap 10.6235
rs 8.1954
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LibraryCatalog\Service\Http;
6
7
use DI\Container;
8
use FastRoute\Dispatcher;
9
use LibraryCatalog\Controller\V1\Author;
10
use LibraryCatalog\Controller\V1\Book;
11
use LibraryCatalog\Controller\V1\Error;
12
use LibraryCatalog\Controller\V1\Index;
13
use Psr\Http\Message\RequestInterface;
14
use Psr\Http\Message\ResponseInterface;
15
16
class Router
17
{
18
    /** @var Container */
19
    protected Container $container;
20
    /** @var Dispatcher */
21
    protected Dispatcher $dispatcher;
22
    /** @var string */
23
    protected string $uri;
24
    /** @var string */
25
    protected string $httpMethod;
26
    /** @var array */
27
    protected array $serverParams;
28
29
    /**
30
     * Router constructor.
31
     * @param Container $container
32
     * @param string $uri
33
     * @param string $httpMethod
34
     * @param array $serverParams
35
     */
36 17
    public function __construct(Container $container, string $uri, string $httpMethod, array $serverParams)
37
    {
38 17
        $this->container = $container;
39 17
        $this->uri = $uri;
40 17
        $this->httpMethod = $httpMethod;
41 17
        $this->serverParams = $serverParams;
42 17
    }
43
44
    /**
45
     * @return ResponseInterface
46
     * @throws \DI\DependencyException
47
     * @throws \DI\NotFoundException
48
     * @throws \LibraryCatalog\Exception\HttpUnauthorizedException
49
     */
50 17
    public function dispatch(): ResponseInterface
51
    {
52
        // Prepare request with extracted Auth.
53 17
        $psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
54 17
        $request = $psr17Factory->createRequest($this->httpMethod, $this->uri);
55 17
        if (isset($this->serverParams['HTTP_AUTHORIZATION'])) {
56
            // We could copy all headers but we take only what we want for speed optimization.
57 14
            $request = $request->withHeader('Authorization', $this->serverParams['HTTP_AUTHORIZATION']);
58
        }
59
60 17
        $routeInfo = $this->getDispatcher()->dispatch($this->httpMethod, $this->uri);
61 17
        switch ($routeInfo[0]) {
62 17
            case Dispatcher::NOT_FOUND:
63
                $response = (new Error($this->container, $request))->notFoundError($this->uri);
64
                break;
65 17
            case Dispatcher::METHOD_NOT_ALLOWED:
66
                $response = (new Error($this->container, $request))
67
                    ->error($this->uri, 405, 'Method not allowed');
68
                break;
69 17
            case Dispatcher::FOUND:
70
                try {
71 17
                    $response = $this->handle($routeInfo[1], $routeInfo[2], $request);
72 2
                } catch (\LibraryCatalog\Exception\HttpBadRequestException $e) {
73 1
                    $response = (new Error($this->container, $request))
74 1
                        ->badRequestError($this->uri, $e->getMessage());
75 1
                } catch (\LibraryCatalog\Exception\HttpUnauthorizedException $e) {
76 1
                    $response = (new Error($this->container, $request))
77 1
                        ->unauthorizedError($this->uri, $e->getMessage());
78
                } catch (\Exception | \Throwable $e) {
79
                    //@todo Log
80
                    $response = (new Error($this->container, $request))->systemError($this->uri);
81
                }
82 17
                break;
83
            default:
84
                // Undefined behavior.
85
                $response = (new Error($this->container, $request))
86
                    ->systemError($this->uri, 'Undefined behavior');
87
                break;
88
        }
89
90 17
        return $response;
91
    }
92
93
    /**
94
     * @param string $handler
95
     * @param array $params
96
     * @param RequestInterface $request
97
     * @return ResponseInterface
98
     * @throws \DI\DependencyException
99
     * @throws \DI\NotFoundException
100
     * @throws \LibraryCatalog\Controller\TransformerException
101
     * @throws \LibraryCatalog\Exception\HttpUnauthorizedException
102
     * @throws \Rakit\Validation\RuleQuashException
103
     */
104 17
    protected function handle(string $handler, array $params, RequestInterface $request): ResponseInterface
105
    {
106 17
        switch ($handler) {
107 17
            case 'index':
108 1
                $response = (new Index($this->container, $request))->indexAction();
109 1
                break;
110 16
            case 'healthcheck':
111 1
                $response = (new Index($this->container, $request))->healthcheckAction();
112 1
                break;
113
114 15
            case 'get_author_handler':
115 5
                $response = (new Author($this->container, $request))
116 4
                    ->getOneHandler($this->uri, $params['id']);
117 4
                break;
118 10
            case 'post_author_handler':
119 4
                $response = (new Author($this->container, $request))->postOneHandler(
120 4
                    $this->uri,
121 4
                    $this->container->get('HttpTransformer')
122 4
                        ->deserialize($this->container->get('RawInput')->get()),
123
                );
124 3
                break;
125
126 6
            case 'get_book_handler':
127 3
                $response = (new Book($this->container, $request))
128 3
                    ->getOneHandler($this->uri, $params['id']);
129 3
                break;
130 3
            case 'post_book_handler':
131 3
                $response = (new Book($this->container, $request))->postOneHandler(
132 3
                    $this->uri,
133 3
                    $this->container->get('HttpTransformer')
134 3
                        ->deserialize($this->container->get('RawInput')->get()),
135
                );
136 3
                break;
137
            default:
138
                // Undefined behavior.
139
                $response = (new Error($this->container, $request))
140
                    ->systemError($this->uri, 'Undefined route');
141
                break;
142
        }
143
144 15
        return $response;
145
    }
146
147
    /**
148
     * @return Dispatcher
149
     */
150 17
    protected function getDispatcher(): Dispatcher
151
    {
152 17
        if (!isset($this->dispatcher)) {
153 17
            $this->dispatcher = \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $r) {
154 17
                $r->addRoute('GET', '/', 'index');
155
156 17
                $r->addRoute('GET', '/healthcheck', 'healthcheck');
157 17
                $r->addRoute('GET', '/api/v1/healthcheck', 'healthcheck');
158
159 17
                $r->addRoute('GET', '/author/{id:\d+}', 'get_author_handler');
160 17
                $r->addRoute('GET', '/api/v1/author/{id:\d+}', 'get_author_handler');
161 17
                $r->addRoute('POST', '/author', 'post_author_handler');
162 17
                $r->addRoute('POST', '/api/v1/author', 'post_author_handler');
163
164 17
                $r->addRoute('GET', '/book/{id:\d+}', 'get_book_handler');
165 17
                $r->addRoute('GET', '/api/v1/book/{id:\d+}', 'get_book_handler');
166 17
                $r->addRoute('POST', '/book', 'post_book_handler');
167 17
                $r->addRoute('POST', '/api/v1/book', 'post_book_handler');
168 17
            });
169
        }
170
171 17
        return $this->dispatcher;
172
    }
173
}
174