Passed
Push — master ( f964a5...914cd8 )
by Mr
02:08
created

ActionHandler::execute()   A

Complexity

Conditions 5
Paths 28

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 22
nc 28
nop 2
dl 0
loc 32
ccs 0
cts 30
cp 0
crap 30
rs 9.2568
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\Interop\Assertion;
13
use Daikon\Interop\AssertionFailedException;
14
use Daikon\Interop\RuntimeException;
15
use Daikon\Validize\Validation\ValidatorDefinition;
16
use Daikon\Validize\ValueObject\Severity;
17
use Exception;
18
use Fig\Http\Message\StatusCodeInterface;
19
use Psr\Container\ContainerInterface;
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use Psr\Http\Server\MiddlewareInterface;
23
use Psr\Http\Server\RequestHandlerInterface;
24
use Psr\Log\LoggerInterface;
25
26
class ActionHandler implements MiddlewareInterface, StatusCodeInterface
27
{
28
    use ResolvesDependency;
29
30
    public const ERRORS = '_errors';
31
    public const PAYLOAD = '_payload';
32
    public const STATUS_CODE = '_status_code';
33
    public const RESPONDER = '_responder';
34
35
    protected ContainerInterface $container;
36
37
    protected LoggerInterface $logger;
38
39
    public function __construct(ContainerInterface $container, LoggerInterface $logger)
40
    {
41
        $this->container = $container;
42
        $this->logger = $logger;
43
    }
44
45
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
46
    {
47
        $requestHandler = $request->getAttribute(RoutingHandler::REQUEST_HANDLER);
48
        return $requestHandler instanceof ActionInterface
49
            ? $this->execute($requestHandler, $request)
50
            : $handler->handle($request);
51
    }
52
53
    protected function execute(ActionInterface $action, ServerRequestInterface $request): ResponseInterface
54
    {
55
        try {
56
            if ($validator = $action->getValidator($request)) {
57
                $validatorDefinition = (new ValidatorDefinition('$', Severity::critical()))->withArgument($request);
58
                $request = $request->withAttribute(self::PAYLOAD, $validator($validatorDefinition));
59
                Assertion::noContent($request->getAttribute(self::ERRORS));
60
            }
61
            $request = $action($request);
62
        } catch (Exception $error) {
63
            switch (true) {
64
                case $error instanceof AssertionFailedException:
65
                    $statusCode = self::STATUS_UNPROCESSABLE_ENTITY;
66
                    break;
67
                default:
68
                    $this->logger->error($error->getMessage(), ['trace' => $error->getTrace()]);
69
                    $statusCode = self::STATUS_INTERNAL_SERVER_ERROR;
70
            }
71
            $request = $action->handleError(
72
                $request
73
                    ->withAttribute(self::STATUS_CODE, $request->getAttribute(self::STATUS_CODE, $statusCode))
74
                    ->withAttribute(self::ERRORS, $request->getAttribute(self::ERRORS, $error))
75
            );
76
        }
77
78
        if (!$responder = $this->resolveResponder($request)) {
79
            throw $error ?? new RuntimeException(
80
                sprintf("Unable to determine responder for '%s'.", get_class($action))
81
            );
82
        }
83
84
        return $responder->handle($request);
85
    }
86
87
    protected function resolveResponder(ServerRequestInterface $request): RequestHandlerInterface
88
    {
89
        $responder = $request->getAttribute(self::RESPONDER);
90
        if (!$responder instanceof RequestHandlerInterface) {
91
            /** @var RequestHandlerInterface $responder */
92
            $responder = $this->resolve($this->container, $responder, RequestHandlerInterface::class);
93
        }
94
        return $responder;
95
    }
96
}
97