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