Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.74% |
45 / 47 |
|
77.78% |
7 / 9 |
CRAP | |
0.00% |
0 / 1 |
CreateObjectAction | |
95.74% |
45 / 47 |
|
77.78% |
7 / 9 |
21 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isAuthorized | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
10.08 | |||
__invoke | |
94.44% |
17 / 18 |
|
0.00% |
0 / 1 |
3.00 | |||
getInputType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getOutputType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPossibleActionResponseStatuses | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
getDescription | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getTags | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRouteAttributes | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | namespace Apie\Common\Actions; |
3 | |
4 | use Apie\Common\Events\ApieResourceCreated; |
5 | use Apie\Common\IntegrationTestLogger; |
6 | use Apie\Core\Actions\ActionInterface; |
7 | use Apie\Core\Actions\ActionResponse; |
8 | use Apie\Core\Actions\ActionResponseStatus; |
9 | use Apie\Core\Actions\ActionResponseStatusList; |
10 | use Apie\Core\Actions\ApieFacadeInterface; |
11 | use Apie\Core\Attributes\Description; |
12 | use Apie\Core\BoundedContext\BoundedContextId; |
13 | use Apie\Core\Context\ApieContext; |
14 | use Apie\Core\ContextConstants; |
15 | use Apie\Core\Entities\EntityInterface; |
16 | use Apie\Core\Enums\RequestMethod; |
17 | use Apie\Core\Exceptions\IndexNotFoundException; |
18 | use Apie\Core\Lists\StringList; |
19 | use Apie\Core\Utils\EntityUtils; |
20 | use Apie\Core\ValueObjects\Utils; |
21 | use Exception; |
22 | use LogicException; |
23 | use ReflectionClass; |
24 | |
25 | /** |
26 | * Action to create a new object. |
27 | */ |
28 | final class CreateObjectAction implements ActionInterface |
29 | { |
30 | public function __construct(private readonly ApieFacadeInterface $apieFacade) |
31 | { |
32 | } |
33 | |
34 | public static function isAuthorized(ApieContext $context, bool $runtimeChecks, bool $throwError = false): bool |
35 | { |
36 | $refl = new ReflectionClass($context->getContext(ContextConstants::RESOURCE_NAME, $throwError)); |
37 | if (!$context->appliesToContext($refl, $runtimeChecks, $throwError ? new LogicException('Class access is not allowed') : null)) { |
38 | return false; |
39 | } |
40 | if (EntityUtils::isPolymorphicEntity($refl) && $runtimeChecks && $context->hasContext(ContextConstants::RAW_CONTENTS)) { |
41 | $rawContents = Utils::toArray($context->getContext(ContextConstants::RAW_CONTENTS, $throwError)); |
42 | try { |
43 | $refl = EntityUtils::findClass($rawContents, $refl); |
44 | } catch (IndexNotFoundException) { |
45 | } |
46 | } |
47 | $constructor = $refl->getConstructor(); |
48 | if ($constructor && !$context->appliesToContext($constructor, $runtimeChecks, $throwError ? new LogicException('Class instantiation is not allowed') : null)) { |
49 | return false; |
50 | } |
51 | return true; |
52 | } |
53 | |
54 | /** |
55 | * @param array<string|int, mixed> $rawContents |
56 | */ |
57 | public function __invoke(ApieContext $context, array $rawContents): ActionResponse |
58 | { |
59 | $context->withContext(ContextConstants::RAW_CONTENTS, $rawContents) |
60 | ->withContext(ContextConstants::APIE_ACTION, __CLASS__) |
61 | ->checkAuthorization(); |
62 | try { |
63 | $resource = $this->apieFacade->denormalizeNewObject( |
64 | $rawContents, |
65 | $context->getContext(ContextConstants::RESOURCE_NAME), |
66 | $context |
67 | ); |
68 | } catch (Exception $error) { |
69 | IntegrationTestLogger::logException($error); |
70 | return ActionResponse::createClientError($this->apieFacade, $context, $error); |
71 | } |
72 | $context = $context->withContext(ContextConstants::RESOURCE, $resource); |
73 | if ($context->getContext(RequestMethod::class, false) === RequestMethod::PUT) { |
74 | /** @phpstan-ignore argument.templateType */ |
75 | $resource = $this->apieFacade->upsert($resource, new BoundedContextId($context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))); |
76 | } else { |
77 | $resource = $this->apieFacade->persistNew($resource, new BoundedContextId($context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))); |
78 | } |
79 | $context = $context->withContext(ContextConstants::RESOURCE, $resource); |
80 | $context->dispatchEvent(new ApieResourceCreated($resource)); |
81 | return ActionResponse::createCreationSuccess($this->apieFacade, $context, $resource, $resource); |
82 | } |
83 | |
84 | /** |
85 | * @return ReflectionClass<EntityInterface> |
86 | */ |
87 | public static function getInputType(ReflectionClass $class): ReflectionClass |
88 | { |
89 | return $class; |
90 | } |
91 | |
92 | /** |
93 | * @return ReflectionClass<EntityInterface> |
94 | */ |
95 | public static function getOutputType(ReflectionClass $class): ReflectionClass |
96 | { |
97 | return $class; |
98 | } |
99 | |
100 | public static function getPossibleActionResponseStatuses(): ActionResponseStatusList |
101 | { |
102 | return new ActionResponseStatusList([ |
103 | ActionResponseStatus::CREATED, |
104 | ActionResponseStatus::CLIENT_ERROR, |
105 | ActionResponseStatus::PERISTENCE_ERROR |
106 | ]); |
107 | } |
108 | |
109 | /** |
110 | * @param ReflectionClass<object> $class |
111 | */ |
112 | public static function getDescription(ReflectionClass $class): string |
113 | { |
114 | $description = 'Creates an instance of ' . $class->getShortName(); |
115 | foreach ($class->getAttributes(Description::class) as $attribute) { |
116 | $description .= '. ' . $attribute->newInstance()->description; |
117 | } |
118 | |
119 | return $description; |
120 | } |
121 | |
122 | /** |
123 | * @param ReflectionClass<object> $class |
124 | */ |
125 | public static function getTags(ReflectionClass $class): StringList |
126 | { |
127 | return new StringList([$class->getShortName(), 'resource']); |
128 | } |
129 | |
130 | /** |
131 | * @param ReflectionClass<object> $class |
132 | */ |
133 | public static function getRouteAttributes(ReflectionClass $class): array |
134 | { |
135 | return [ |
136 | ContextConstants::CREATE_OBJECT => true, |
137 | ContextConstants::RESOURCE_NAME => $class->name, |
138 | ContextConstants::DISPLAY_FORM => true, |
139 | ]; |
140 | } |
141 | } |