Passed
Push — master ( ff0e1a...d70f62 )
by Mr
02:29
created

ActionHandler::getValidator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 8
ccs 0
cts 7
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the daikon-cqrs/boot project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Daikon\Boot\Middleware;
10
11
use Daikon\Boot\Middleware\Action\ActionInterface;
12
use Daikon\Boot\Middleware\Action\ResponderInterface;
13
use Daikon\Interop\Assertion;
14
use Daikon\Interop\AssertionFailedException;
15
use Daikon\Interop\RuntimeException;
16
use Daikon\Validize\Validation\ValidatorDefinition;
17
use Daikon\Validize\ValueObject\Severity;
18
use Exception;
19
use Fig\Http\Message\StatusCodeInterface;
20
use Psr\Container\ContainerInterface;
21
use Psr\Http\Message\ResponseInterface;
22
use Psr\Http\Message\ServerRequestInterface;
23
use Psr\Http\Server\MiddlewareInterface;
24
use Psr\Http\Server\RequestHandlerInterface;
25
use Psr\Log\LoggerInterface;
26
27
class ActionHandler implements MiddlewareInterface, StatusCodeInterface
28
{
29
    use ResolvesDependency;
30
31
    public const ATTR_ERRORS = '_errors';
32
    public const ATTR_PAYLOAD = '_payload';
33
    public const ATTR_STATUS_CODE = '_status_code';
34
    public const ATTR_RESPONDER = '_responder';
35
36
    protected ContainerInterface $container;
37
38
    protected LoggerInterface $logger;
39
40
    public function __construct(ContainerInterface $container, LoggerInterface $logger)
41
    {
42
        $this->container = $container;
43
        $this->logger = $logger;
44
    }
45
46
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
47
    {
48
        $requestHandler = $request->getAttribute(RoutingHandler::ATTR_REQUEST_HANDLER);
49
        return $requestHandler instanceof ActionInterface
50
            ? $this->executeAction($requestHandler, $request)
51
            : $handler->handle($request);
52
    }
53
54
    protected function executeAction(ActionInterface $action, ServerRequestInterface $request): ResponseInterface
55
    {
56
        try {
57
            if ($validator = $action->getValidator($request)) {
58
                $validatorDefinition = new ValidatorDefinition(self::ATTR_PAYLOAD, Severity::critical());
59
                $request = $request->withAttribute(
60
                    self::ATTR_PAYLOAD,
61
                    $validator($validatorDefinition->withArgument($request))
62
                );
63
                Assertion::noContent($request->getAttribute(self::ATTR_ERRORS));
64
            }
65
            $request = $action($request);
66
        } catch (Exception $error) {
67
            switch (true) {
68
                case $error instanceof AssertionFailedException:
69
                    $statusCode = self::STATUS_UNPROCESSABLE_ENTITY;
70
                    break;
71
                default:
72
                    $this->logger->error($error->getMessage(), ['trace' => $error->getTrace()]);
73
                    $statusCode = self::STATUS_INTERNAL_SERVER_ERROR;
74
            }
75
            $request = $action->handleError(
76
                $request->withAttribute(
77
                    self::ATTR_STATUS_CODE,
78
                    $request->getAttribute(self::ATTR_STATUS_CODE, $statusCode)
79
                )->withAttribute(
80
                    self::ATTR_ERRORS,
81
                    $request->getAttribute(self::ATTR_ERRORS, $error)
82
                )
83
            );
84
        }
85
86
        if (!$responder = $this->resolveResponder($request)) {
87
            throw $error ?? new RuntimeException(
88
                sprintf("Unable to determine responder for '%s'.", get_class($action))
89
            );
90
        }
91
92
        return $responder->handle($request);
93
    }
94
95
    protected function resolveResponder(ServerRequestInterface $request): ?ResponderInterface
96
    {
97
        $responder = $request->getAttribute(self::ATTR_RESPONDER);
98
        if ($responder) {
99
            /** @var ResponderInterface $responder */
100
            $responder = $this->resolve($this->container, $responder, ResponderInterface::class);
101
        }
102
        return $responder;
103
    }
104
}
105