Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.02% covered (success)
93.02%
40 / 43
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
GetItemAction
93.02% covered (success)
93.02%
40 / 43
77.78% covered (warning)
77.78%
7 / 9
16.09
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
83.33% covered (warning)
83.33%
10 / 12
0.00% covered (danger)
0.00%
0 / 1
7.23
 __invoke
94.12% covered (success)
94.12%
16 / 17
0.00% covered (danger)
0.00%
0 / 1
2.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%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2namespace Apie\Common\Actions;
3
4use Apie\Common\Other\LockUtil;
5use Apie\Core\Actions\ActionInterface;
6use Apie\Core\Actions\ActionResponse;
7use Apie\Core\Actions\ActionResponseStatus;
8use Apie\Core\Actions\ActionResponseStatusList;
9use Apie\Core\Actions\ApieFacadeInterface;
10use Apie\Core\BoundedContext\BoundedContextId;
11use Apie\Core\Context\ApieContext;
12use Apie\Core\ContextConstants;
13use Apie\Core\Entities\EntityInterface;
14use Apie\Core\Exceptions\InvalidTypeException;
15use Apie\Core\IdentifierUtils;
16use Apie\Core\Lists\PermissionList;
17use Apie\Core\Lists\StringList;
18use Apie\Core\Permissions\PermissionInterface;
19use Apie\Core\Permissions\RequiresPermissionsInterface;
20use Apie\Core\Utils\EntityUtils;
21use LogicException;
22use ReflectionClass;
23
24/**
25 * Action to get a single item resource.
26 */
27final class GetItemAction 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        $resource = $context->getContext(ContextConstants::RESOURCE, false);
37        if ($resource instanceof RequiresPermissionsInterface) {
38            $requiredPermissions = $resource->getRequiredPermissions();
39            $user = $context->getContext(ContextConstants::AUTHENTICATED_USER, false);
40            if ($user instanceof PermissionInterface) {
41                $hasPermisions = $user->getPermissionIdentifiers();
42                return $hasPermisions->hasOverlap($requiredPermissions);
43            }
44            return $requiredPermissions->hasOverlap(new PermissionList(['']));
45        }
46        if (EntityUtils::isPolymorphicEntity($refl) && $runtimeChecks && $resource) {
47            $refl = new ReflectionClass($resource);
48        }
49        return $context->appliesToContext($refl, $runtimeChecks, $throwError ? new LogicException('Operation is not allowed') : null);
50    }
51
52    /**
53     * @param array<string|int, mixed> $rawContents
54     */
55    public function __invoke(ApieContext $context, array $rawContents): ActionResponse
56    {
57        $context->withContext(ContextConstants::APIE_ACTION, __CLASS__)->checkAuthorization();
58        $resourceClass = new ReflectionClass($context->getContext(ContextConstants::RESOURCE_NAME));
59        $id = $context->getContext(ContextConstants::RESOURCE_ID);
60        if (!$resourceClass->implementsInterface(EntityInterface::class)) {
61            throw new InvalidTypeException($resourceClass->name, 'EntityInterface');
62        }
63        $lock = LockUtil::createLock(
64            $context,
65            [ContextConstants::BOUNDED_CONTEXT_ID, ContextConstants::RESOURCE_NAME, ContextConstants::RESOURCE_ID]
66        );
67        try {
68            $result = $this->apieFacade->find(
69                IdentifierUtils::idStringToIdentifier($id, $context),
70                new BoundedContextId($context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))
71            );
72            $context = $context->withContext(ContextConstants::RESOURCE, $result);
73            $context->withContext(ContextConstants::APIE_ACTION, __CLASS__)->checkAuthorization();
74        } finally {
75            $lock->release();
76        }
77        return ActionResponse::createRunSuccess($this->apieFacade, $context, $result, $result);
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::AUTHORIZATION_ERROR,
101            ActionResponseStatus::NOT_FOUND
102        ]);
103    }
104
105    public static function getDescription(ReflectionClass $class): string
106    {
107        return 'Gets a resource of ' . $class->getShortName() . ' with a specific id';
108    }
109    
110    public static function getTags(ReflectionClass $class): StringList
111    {
112        return new StringList([$class->getShortName(), 'resource']);
113    }
114
115    public static function getRouteAttributes(ReflectionClass $class): array
116    {
117        return [
118            ContextConstants::GET_OBJECT => true,
119            ContextConstants::RESOURCE_NAME => $class->name,
120        ];
121    }
122}