Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
69.81% covered (warning)
69.81%
74 / 106
44.44% covered (danger)
44.44%
4 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
AddAuditLog
69.81% covered (warning)
69.81%
74 / 106
44.44% covered (danger)
44.44%
4 / 9
47.06
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
 getSubscribedEvents
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 createUser
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 onApieResourceCreated
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
 onApieResourceRead
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
4.00
 onApieResourceReadList
5.56% covered (danger)
5.56%
1 / 18
0.00% covered (danger)
0.00%
0 / 1
26.06
 onApieResourceModified
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
4.00
 onApieResourceMethodCalled
7.69% covered (danger)
7.69%
1 / 13
0.00% covered (danger)
0.00%
0 / 1
10.08
 onApieResourceRemoved
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2namespace Apie\Common\Events;
3
4use Apie\Common\Command\ApieUpdateRecalculatingCommand;
5use Apie\Common\Other\Audit\AuditCreate;
6use Apie\Common\Other\Audit\AuditMethodCalled;
7use Apie\Common\Other\Audit\AuditMigration;
8use Apie\Common\Other\Audit\AuditModified;
9use Apie\Common\Other\Audit\AuditRead;
10use Apie\Common\Other\Audit\AuditRemoved;
11use Apie\Common\Other\AuditLog;
12use Apie\Core\Attributes\Auditable;
13use Apie\Core\BoundedContext\BoundedContextId;
14use Apie\Core\Context\ApieContext;
15use Apie\Core\ContextConstants;
16use Apie\Core\Datalayers\ApieDatalayer;
17use Apie\Core\Enums\ConsoleCommand;
18use Apie\Core\Enums\RequestMethod;
19use Apie\Core\ValueObjects\IdFriendlyEntityReference;
20use Apie\Core\ValueObjects\Utils;
21use Apie\Serializer\ValueObjects\SerializedPhpObject;
22use ReflectionClass;
23use Symfony\Component\Console\Command\Command;
24use Symfony\Component\EventDispatcher\EventSubscriberInterface;
25
26class AddAuditLog implements EventSubscriberInterface
27{
28    public function __construct(
29        private readonly ApieDatalayer $datalayer
30    ) {
31    }
32
33    public static function getSubscribedEvents(): array
34    {
35        return [
36            ApieResourceCreated::class      => 'onApieResourceCreated',
37            ApieResourceRead::class         => 'onApieResourceRead',
38            ApieResourceReadList::class     => 'onApieResourceReadList',
39            ApieResourceModified::class     => 'OnApieResourceModified',
40            ApieResourceRemoved::class      => 'onApieResourceRemoved',
41            ApieResourceMethodCalled::class => 'onApieResourceMethodCalled'
42        ];
43    }
44
45    private function createUser(ApieContext $context): ?SerializedPhpObject
46    {
47        $user = $context->getContext(ContextConstants::AUTHENTICATED_USER, false);
48        if ($user === null) {
49            if ($context->getContext(ConsoleCommand::class, false)) {
50                return SerializedPhpObject::createFromPhpObject(ConsoleCommand::CONSOLE_COMMAND);
51            }
52            return null;
53        }
54        return SerializedPhpObject::createFromPhpObject($user);
55    }
56
57    public function onApieResourceCreated(ApieResourceCreated $event): void
58    {
59        foreach ((new ReflectionClass($event->resource))->getAttributes(Auditable::class) as $auditable) {
60            $reference = IdFriendlyEntityReference::createFromContext($event->context);
61            if ($reference instanceof IdFriendlyEntityReference) {
62                $auditLog = new AuditLog(
63                    $reference,
64                    SerializedPhpObject::createFromPhpObject($event->resource),
65                    new AuditCreate($event->context->getContext(RequestMethod::class, false) === RequestMethod::PUT),
66                    $this->createUser($event->context)
67                );
68                $this->datalayer->persistNew(
69                    $auditLog,
70                    new BoundedContextId($event->context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))
71                );
72            }
73        }
74    }
75
76    public function onApieResourceRead(ApieResourceRead $event): void
77    {
78        foreach ((new ReflectionClass($event->resource))->getAttributes(Auditable::class) as $auditable) {
79            $attribute = $auditable->newInstance();
80            if (!$attribute->readEvents) {
81                continue;
82            }
83            $reference = IdFriendlyEntityReference::createFromContext($event->context);
84
85            if ($reference instanceof IdFriendlyEntityReference) {
86                $auditLog = new AuditLog(
87                    $reference,
88                    SerializedPhpObject::createFromPhpObject($event->resource),
89                    new AuditRead(),
90                    $this->createUser($event->context)
91                );
92                $this->datalayer->persistNew(
93                    $auditLog,
94                    new BoundedContextId($event->context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))
95                );
96            }
97        }
98    }
99
100    public function onApieResourceReadList(ApieResourceReadList $event): void
101    {
102        foreach ((new ReflectionClass($event->resource))->getAttributes(Auditable::class) as $auditable) {
103            $attribute = $auditable->newInstance();
104            if (!$attribute->readAllEvents) {
105                continue;
106            }
107            foreach ($event->resource as $entity) {
108                $context = $event->context->withContext(ContextConstants::RESOURCE_ID, Utils::toString($entity->getId()));
109                $reference = IdFriendlyEntityReference::createFromContext($event->context);
110
111                if ($reference instanceof IdFriendlyEntityReference) {
112                    $auditLog = new AuditLog(
113                        $reference,
114                        SerializedPhpObject::createFromPhpObject($entity),
115                        new AuditRead(fromList: true),
116                        $this->createUser($context)
117                    );
118                    $this->datalayer->persistNew(
119                        $auditLog,
120                        new BoundedContextId($context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))
121                    );
122                }
123            }
124        }
125    }
126
127    public function onApieResourceModified(ApieResourceModified $event): void
128    {
129        foreach ((new ReflectionClass($event->resource))->getAttributes(Auditable::class) as $auditable) {
130            $reference = IdFriendlyEntityReference::createFromContext($event->context);
131            $content = $event->context->getContext(ContextConstants::RAW_CONTENTS, false);
132            $auditEvent = new AuditModified($content);
133            $command = $event->context->getContext(Command::class, false);
134            if ($command instanceof ApieUpdateRecalculatingCommand) {
135                $auditEvent = new AuditMigration();
136            }
137
138            if ($reference instanceof IdFriendlyEntityReference) {
139                $auditLog = new AuditLog(
140                    $reference,
141                    SerializedPhpObject::createFromPhpObject($event->resource),
142                    $auditEvent,
143                    $this->createUser($event->context)
144                );
145                $this->datalayer->persistNew(
146                    $auditLog,
147                    new BoundedContextId($event->context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))
148                );
149            }
150        }
151    }
152
153    public function onApieResourceMethodCalled(ApieResourceMethodCalled $event): void
154    {
155        foreach ((new ReflectionClass($event->resource))->getAttributes(Auditable::class) as $auditable) {
156            $reference = IdFriendlyEntityReference::createFromContext($event->context);
157            
158            if ($reference instanceof IdFriendlyEntityReference) {
159                $auditLog = new AuditLog(
160                    $reference,
161                    SerializedPhpObject::createFromPhpObject($event->resource),
162                    new AuditMethodCalled($event->methodName, $event->context->getContext(ContextConstants::RAW_CONTENTS, false)),
163                    $this->createUser($event->context)
164                );
165                $this->datalayer->persistNew(
166                    $auditLog,
167                    new BoundedContextId($event->context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))
168                );
169            }
170        }
171    }
172
173    public function onApieResourceRemoved(ApieResourceRemoved $event): void
174    {
175        foreach ((new ReflectionClass($event->resource))->getAttributes(Auditable::class) as $auditable) {
176            $reference = IdFriendlyEntityReference::createFromContext($event->context);
177            
178            if ($reference instanceof IdFriendlyEntityReference) {
179                $auditLog = new AuditLog(
180                    $reference,
181                    SerializedPhpObject::createFromPhpObject($event->resource),
182                    new AuditRemoved(),
183                    $this->createUser($event->context)
184                );
185                $this->datalayer->persistNew(
186                    $auditLog,
187                    new BoundedContextId($event->context->getContext(ContextConstants::BOUNDED_CONTEXT_ID))
188                );
189            }
190        }
191    }
192}