ActionHandler::execute()   A
last analyzed

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 19
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\Boot\Middleware\Action\DaikonRequest;
13
use Daikon\Interop\Assertion;
14
use Daikon\Interop\AssertionFailedException;
15
use Daikon\Interop\DaikonException;
16
use Daikon\Interop\RuntimeException;
17
use Daikon\Validize\Validation\ValidatorDefinition;
18
use Daikon\Validize\ValueObject\Severity;
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
    protected ContainerInterface $container;
32
33
    protected LoggerInterface $logger;
34
35
    public function __construct(ContainerInterface $container, LoggerInterface $logger)
36
    {
37
        $this->container = $container;
38
        $this->logger = $logger;
39
    }
40
41
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
42
    {
43
        $requestHandler = $request->getAttribute(RoutingHandler::REQUEST_HANDLER);
44
        return $requestHandler instanceof ActionInterface
45
            ? $this->execute($requestHandler, DaikonRequest::wrap($request))
46
            : $handler->handle($request);
47
    }
48
49
    protected function execute(ActionInterface $action, DaikonRequest $request): ResponseInterface
50
    {
51
        try {
52
            if ($validator = $action->getValidator($request)) {
53
                $validatorDefinition = (new ValidatorDefinition('$', Severity::critical()))->withArgument($request);
54
                $request = $request->withPayload($validator($validatorDefinition));
55
                Assertion::noContent($request->getErrors());
56
            }
57
            $request = $action($request);
58
        } catch (DaikonException $error) {
59
            switch (true) {
60
                case $error instanceof AssertionFailedException:
61
                    $statusCode = self::STATUS_UNPROCESSABLE_ENTITY;
62
                    break;
63
                default:
64
                    $this->logger->error($error->getMessage(), ['exception' => $error->getTrace()]);
65
                    $statusCode = self::STATUS_INTERNAL_SERVER_ERROR;
66
            }
67
            $request = $action->handleError(
68
                $request
69
                    ->withStatusCode($request->getStatusCode($statusCode))
70
                    ->withErrors($request->getErrors($error))
71
            );
72
        }
73
74
        if (!$responder = $this->resolveResponder($request)) {
75
            throw $error ?? new RuntimeException(
76
                sprintf("Unable to determine responder for '%s'.", get_class($action))
77
            );
78
        }
79
80
        return $responder->handle($request);
81
    }
82
83
    protected function resolveResponder(DaikonRequest $request): RequestHandlerInterface
84
    {
85
        $responder = $request->getResponder();
86
        if (!$responder instanceof RequestHandlerInterface) {
87
            /** @var RequestHandlerInterface $responder */
88
            $responder = $this->resolve($this->container, $responder, RequestHandlerInterface::class);
89
        }
90
        return $responder;
91
    }
92
}
93