Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
81.82% |
36 / 44 |
|
77.78% |
7 / 9 |
CRAP | |
0.00% |
0 / 1 |
ModifyObjectAction | |
81.82% |
36 / 44 |
|
77.78% |
7 / 9 |
18.74 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isAuthorized | |
55.56% |
5 / 9 |
|
0.00% |
0 / 1 |
11.30 | |||
__invoke | |
80.00% |
16 / 20 |
|
0.00% |
0 / 1 |
3.07 | |||
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% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
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\IntegrationTestLogger; |
5 | use Apie\Core\Actions\ActionInterface; |
6 | use Apie\Core\Actions\ActionResponse; |
7 | use Apie\Core\Actions\ActionResponseStatus; |
8 | use Apie\Core\Actions\ActionResponseStatusList; |
9 | use Apie\Core\Actions\ApieFacadeInterface; |
10 | use Apie\Core\BoundedContext\BoundedContextId; |
11 | use Apie\Core\Context\ApieContext; |
12 | use Apie\Core\ContextConstants; |
13 | use Apie\Core\Entities\EntityInterface; |
14 | use Apie\Core\Exceptions\EntityNotFoundException; |
15 | use Apie\Core\Exceptions\InvalidTypeException; |
16 | use Apie\Core\IdentifierUtils; |
17 | use Apie\Core\Lists\ItemHashmap; |
18 | use Apie\Core\Lists\StringList; |
19 | use Apie\Core\Metadata\MetadataFactory; |
20 | use Apie\Core\Utils\EntityUtils; |
21 | use Apie\Core\ValueObjects\Exceptions\InvalidStringForValueObjectException; |
22 | use LogicException; |
23 | use ReflectionClass; |
24 | |
25 | /** |
26 | * Action to modify an existing object. |
27 | */ |
28 | final class ModifyObjectAction 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 (EntityUtils::isPolymorphicEntity($refl) && $runtimeChecks && $context->hasContext(ContextConstants::RESOURCE)) { |
38 | $refl = new ReflectionClass($context->getContext(ContextConstants::RESOURCE, $throwError)); |
39 | } |
40 | $metadata = MetadataFactory::getModificationMetadata($refl, $context); |
41 | if ($metadata->getHashmap()->count() === 0) { |
42 | if ($throwError) { |
43 | throw new LogicException('Metadata for ' . $refl->getShortName() . ' has no fields to edit.'); |
44 | } |
45 | return false; |
46 | } |
47 | return $context->appliesToContext($refl, $runtimeChecks, $throwError ? new LogicException('Operation on ' . $refl->getShortName() . ' is not allowed') : null); |
48 | } |
49 | |
50 | /** |
51 | * @param array<string|int, mixed> $rawContents |
52 | */ |
53 | public function __invoke(ApieContext $context, array $rawContents): ActionResponse |
54 | { |
55 | $context->withContext(ContextConstants::APIE_ACTION, __CLASS__)->checkAuthorization(); |
56 | $resourceClass = new ReflectionClass($context->getContext(ContextConstants::RESOURCE_NAME)); |
57 | $id = $context->getContext(ContextConstants::RESOURCE_ID); |
58 | if (!$resourceClass->implementsInterface(EntityInterface::class)) { |
59 | throw new InvalidTypeException($resourceClass->name, 'EntityInterface'); |
60 | } |
61 | try { |
62 | $resource = $this->apieFacade->find( |
63 | IdentifierUtils::idStringToIdentifier($id, $context), |
64 | new BoundedContextId($context->getContext(ContextConstants::BOUNDED_CONTEXT_ID)) |
65 | ); |
66 | } catch (InvalidStringForValueObjectException|EntityNotFoundException $error) { |
67 | IntegrationTestLogger::logException($error); |
68 | return ActionResponse::createClientError($this->apieFacade, $context, $error); |
69 | } |
70 | $context = $context->withContext(ContextConstants::RESOURCE, $resource); |
71 | $resource = $this->apieFacade->denormalizeOnExistingObject( |
72 | new ItemHashmap($rawContents), |
73 | $resource, |
74 | $context |
75 | ); |
76 | $resource = $this->apieFacade->persistExisting($resource, new BoundedContextId($context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))); |
77 | return ActionResponse::createRunSuccess($this->apieFacade, $context, $resource, $resource); |
78 | } |
79 | |
80 | /** |
81 | * @return ReflectionClass<EntityInterface> |
82 | */ |
83 | public static function getInputType(ReflectionClass $class): ReflectionClass |
84 | { |
85 | return $class; |
86 | } |
87 | |
88 | /** |
89 | * @return ReflectionClass<EntityInterface> |
90 | */ |
91 | public static function getOutputType(ReflectionClass $class): ReflectionClass |
92 | { |
93 | return $class; |
94 | } |
95 | |
96 | public static function getPossibleActionResponseStatuses(): ActionResponseStatusList |
97 | { |
98 | return new ActionResponseStatusList([ |
99 | ActionResponseStatus::SUCCESS, |
100 | ActionResponseStatus::CLIENT_ERROR, |
101 | ActionResponseStatus::PERISTENCE_ERROR |
102 | ]); |
103 | } |
104 | |
105 | /** |
106 | * @param ReflectionClass<object> $class |
107 | */ |
108 | public static function getDescription(ReflectionClass $class): string |
109 | { |
110 | return 'Modifies an instance of ' . $class->getShortName(); |
111 | } |
112 | |
113 | /** |
114 | * @param ReflectionClass<object> $class |
115 | */ |
116 | public static function getTags(ReflectionClass $class): StringList |
117 | { |
118 | return new StringList([$class->getShortName(), 'resource']); |
119 | } |
120 | |
121 | /** |
122 | * @param ReflectionClass<object> $class |
123 | */ |
124 | public static function getRouteAttributes(ReflectionClass $class): array |
125 | { |
126 | return [ |
127 | ContextConstants::EDIT_OBJECT => true, |
128 | ContextConstants::RESOURCE_NAME => $class->name, |
129 | ContextConstants::DISPLAY_FORM => true, |
130 | ]; |
131 | } |
132 | } |