Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
2.13% covered (danger)
2.13%
1 / 47
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
LoginService
2.13% covered (danger)
2.13%
1 / 47
25.00% covered (danger)
25.00%
1 / 4
256.00
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
 authorize
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
110
 constructData
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 provideLogins
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2namespace Apie\Common;
3
4use Apie\Common\ActionDefinitions\RunGlobalMethodDefinition;
5use Apie\Common\Exceptions\CanNotLoginException;
6use Apie\Core\BoundedContext\BoundedContext;
7use Apie\Core\BoundedContext\BoundedContextHashmap;
8use Apie\Core\BoundedContext\BoundedContextId;
9use Apie\Core\Context\ApieContext;
10use Apie\Core\Entities\EntityInterface;
11use Apie\Core\Lists\ItemHashmap;
12use Apie\Serializer\Serializer;
13use Generator;
14use ReflectionMethod;
15use SensitiveParameter;
16
17final class LoginService
18{
19    public function __construct(
20        private readonly BoundedContextHashmap $boundedContextHashmap,
21        private readonly ActionDefinitionProvider $actionDefinitionProvider,
22        private readonly Serializer $serializer
23    ) {
24    }
25    public function authorize(
26        string $username,
27        #[SensitiveParameter]
28        string $password,
29        ApieContext $apieContext = new ApieContext()
30    ): EntityInterface {
31        $prio = $apieContext->getContext(BoundedContextId::class, false);
32        /** @var array<\Exception> */
33        $errors = [];
34        if ($prio instanceof BoundedContextId && isset($this->boundedContextHashmap[$prio->toNative()])) {
35            $boundedContext = $this->boundedContextHashmap[$prio->toNative()];
36            foreach ($this->provideLogins($boundedContext, $apieContext) as $actionDefinition) {
37                $method = $actionDefinition->getMethod();
38                try {
39                    $constructedData = $this->constructData($method, $username, $password);
40                    $response = $this->serializer->denormalizeOnMethodCall(
41                        $constructedData,
42                        null,
43                        $method,
44                        $apieContext
45                    );
46                    if ($response instanceof EntityInterface) {
47                        return $response;
48                    }
49                } catch (\Exception) {
50                }
51            }
52        }
53        foreach ($this->boundedContextHashmap as $boundedContext) {
54            foreach ($this->provideLogins($boundedContext, $apieContext) as $actionDefinition) {
55                $method = $actionDefinition->getMethod();
56                try {
57                    $constructedData = $this->constructData($method, $username, $password);
58                    $response = $this->serializer->denormalizeOnMethodCall(
59                        $constructedData,
60                        null,
61                        $method,
62                        $apieContext
63                    );
64                    if ($response instanceof EntityInterface) {
65                        return $response;
66                    }
67                } catch (\Exception $error) {
68                    $errors[] = $error;
69                }
70            }
71        }
72        throw new CanNotLoginException($errors);
73    }
74
75    private function constructData(ReflectionMethod $method, string|int|bool $username, #[SensitiveParameter] string|int|bool $password): ItemHashmap
76    {
77        // TODO: find which argument is the correct one.
78        // @phpstan-ignore nullCoalesce.expr, nullsafe.neverNull
79        $usernameField = $method->getParameters()[0]?->getName() ?? 'username';
80        // @phpstan-ignore nullCoalesce.expr, nullsafe.neverNull
81        $passwordField = $method->getParameters()[1]?->getName() ?? 'password';
82        return new ItemHashmap([
83            $usernameField => (string) $username,
84            $passwordField => (string) $password
85        ]);
86    }
87
88    /**
89     * @return Generator<int, RunGlobalMethodDefinition>
90     */
91    private function provideLogins(BoundedContext $boundedContext, ApieContext $apieContext): \Generator
92    {
93        $actions = $this->actionDefinitionProvider->provideActionDefinitions(
94            $boundedContext,
95            $apieContext,
96            runtimeChecks: true
97        );
98        foreach ($actions as $actionDefinition) {
99            if ($actionDefinition instanceof RunGlobalMethodDefinition
100                && $actionDefinition->getMethod()->getName() === 'verifyAuthentication') {
101                yield $actionDefinition;
102            }
103        }
104    }
105}