Passed
Branch 001-basic (dcea18)
by Mr.
07:50
created

Router::handle()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 41
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 7.0403

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 32
c 1
b 0
f 0
nc 7
nop 3
dl 0
loc 41
ccs 29
cts 32
cp 0.9063
crap 7.0403
rs 8.4746
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 $e) {
79
                    //@todo Log
80
                    //throw $e;
81
                    $response = (new Error($this->container, $request))->systemError($this->uri);
82
                } catch (\Throwable $e) {
83
                    //@todo Log
84
                    //throw $e;
85
                    $response = (new Error($this->container, $request))->systemError($this->uri);
86
                }
87 17
                break;
88
            default:
89
                // Undefined behavior.
90
                $response = (new Error($this->container, $request))
91
                    ->systemError($this->uri, 'Undefined behavior');
92
                break;
93
        }
94
95 17
        return $response;
96
    }
97
98
    /**
99
     * @param string $handler
100
     * @param array $params
101
     * @param RequestInterface $request
102
     * @return ResponseInterface
103
     * @throws \DI\DependencyException
104
     * @throws \DI\NotFoundException
105
     * @throws \LibraryCatalog\Controller\TransformerException
106
     * @throws \LibraryCatalog\Exception\HttpUnauthorizedException
107
     * @throws \Rakit\Validation\RuleQuashException
108
     */
109 17
    protected function handle(string $handler, array $params, RequestInterface $request): ResponseInterface
110
    {
111 17
        switch ($handler) {
112 17
            case 'index':
113 1
                $response = (new Index($this->container, $request))->indexAction();
114 1
                break;
115 16
            case 'healthcheck':
116 1
                $response = (new Index($this->container, $request))->healthcheckAction();
117 1
                break;
118
119 15
            case 'get_author_handler':
120 5
                $response = (new Author($this->container, $request))
121 4
                    ->getOneHandler($this->uri, $params['id']);
122 4
                break;
123 10
            case 'post_author_handler':
124 4
                $response = (new Author($this->container, $request))->postOneHandler(
125 4
                    $this->uri,
126 4
                    $this->container->get('HttpTransformer')
127 4
                        ->deserialize($this->container->get('RawInput')->get()),
128
                );
129 3
                break;
130
131 6
            case 'get_book_handler':
132 3
                $response = (new Book($this->container, $request))
133 3
                    ->getOneHandler($this->uri, $params['id']);
134 3
                break;
135 3
            case 'post_book_handler':
136 3
                $response = (new Book($this->container, $request))->postOneHandler(
137 3
                    $this->uri,
138 3
                    $this->container->get('HttpTransformer')
139 3
                        ->deserialize($this->container->get('RawInput')->get()),
140
                );
141 3
                break;
142
            default:
143
                // Undefined behavior.
144
                $response = (new Error($this->container, $request))
145
                    ->systemError($this->uri, 'Undefined route');
146
                break;
147
        }
148
149 15
        return $response;
150
    }
151
152
    /**
153
     * @return Dispatcher
154
     */
155 17
    protected function getDispatcher(): Dispatcher
156
    {
157 17
        if (!isset($this->dispatcher)) {
158 17
            $this->dispatcher = \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $r) {
159 17
                $r->addRoute('GET', '/', 'index');
160
161 17
                $r->addRoute('GET', '/healthcheck', 'healthcheck');
162 17
                $r->addRoute('GET', '/api/v1/healthcheck', 'healthcheck');
163
164 17
                $r->addRoute('GET', '/author/{id:\d+}', 'get_author_handler');
165 17
                $r->addRoute('GET', '/api/v1/author/{id:\d+}', 'get_author_handler');
166 17
                $r->addRoute('POST', '/author', 'post_author_handler');
167 17
                $r->addRoute('POST', '/api/v1/author', 'post_author_handler');
168
169 17
                $r->addRoute('GET', '/book/{id:\d+}', 'get_book_handler');
170 17
                $r->addRoute('GET', '/api/v1/book/{id:\d+}', 'get_book_handler');
171 17
                $r->addRoute('POST', '/book', 'post_book_handler');
172 17
                $r->addRoute('POST', '/api/v1/book', 'post_book_handler');
173 17
            });
174
        }
175
176 17
        return $this->dispatcher;
177
    }
178
}
179