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