Move AuditLog classes around and clean up AuditLog handler.

This commit is contained in:
Buster "Silver Eagle" Neece 2021-05-30 20:15:34 -05:00
parent ac6b726865
commit 00c4afae50
No known key found for this signature in database
GPG Key ID: 6D9E12FF03411F4E
24 changed files with 101 additions and 103 deletions

View File

@ -1,14 +0,0 @@
<?php
namespace App\Annotations\AuditLog;
use Attribute;
/**
* Indicate a property or method that should be used to determine the "identifier"
* for an auditable record.
*/
#[Attribute(Attribute::TARGET_METHOD)]
class AuditIdentifier
{
}

View File

@ -2,9 +2,9 @@
namespace App\Doctrine\Event;
use App\Annotations\AuditLog\Auditable;
use App\Annotations\AuditLog\AuditIgnore;
use App\Entity;
use App\Entity\Attributes\Auditable;
use App\Entity\Attributes\AuditIgnore;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
@ -12,6 +12,7 @@ use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Proxy\Proxy;
use Doctrine\ORM\UnitOfWork;
use ProxyManager\Proxy\GhostObjectInterface;
use ReflectionClass;
use ReflectionObject;
@ -37,11 +38,29 @@ class AuditLog implements EventSubscriber
public function onFlush(OnFlushEventArgs $args): void
{
$newAuditLogs = [];
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$singleAuditLogs = $this->handleSingleUpdates($em, $uow);
$collectionAuditLogs = $this->handleCollectionUpdates($uow);
$newAuditLogs = array_merge($singleAuditLogs, $collectionAuditLogs);
if (!empty($newAuditLogs)) {
$auditLogMetadata = $em->getClassMetadata(Entity\AuditLog::class);
foreach ($newAuditLogs as $auditLog) {
$uow->persist($auditLog);
$uow->computeChangeSet($auditLogMetadata, $auditLog);
}
}
}
/** @return Entity\AuditLog[] */
protected function handleSingleUpdates(
EntityManagerInterface $em,
UnitOfWork $uow
): array {
$newRecords = [];
$collections = [
Entity\AuditLog::OPER_INSERT => $uow->getScheduledEntityInsertions(),
Entity\AuditLog::OPER_UPDATE => $uow->getScheduledEntityUpdates(),
@ -77,10 +96,10 @@ class AuditLog implements EventSubscriber
// Check if either field value is an object.
if ($this->isEntity($em, $fieldPrev)) {
$fieldPrev = $this->getIdentifier(new ReflectionObject($fieldPrev), $fieldPrev);
$fieldPrev = $this->getIdentifier($fieldPrev);
}
if ($this->isEntity($em, $fieldNow)) {
$fieldNow = $this->getIdentifier(new ReflectionObject($fieldNow), $fieldNow);
$fieldNow = $this->getIdentifier($fieldNow);
}
$changes[$changeField] = [$fieldPrev, $fieldNow];
@ -91,12 +110,12 @@ class AuditLog implements EventSubscriber
}
// Find the identifier method or property.
$identifier = $this->getIdentifier($reflectionClass, $entity);
$identifier = $this->getIdentifier($entity);
if (null === $identifier) {
continue;
}
$newAuditLogs[] = new Entity\AuditLog(
$newRecords[] = new Entity\AuditLog(
$changeType,
get_class($entity),
$identifier,
@ -107,7 +126,14 @@ class AuditLog implements EventSubscriber
}
}
// Handle changes to collections.
return $newRecords;
}
/** @return Entity\AuditLog[] */
protected function handleCollectionUpdates(
UnitOfWork $uow
): array {
$newRecords = [];
$associated = [];
$disassociated = [];
@ -126,7 +152,7 @@ class AuditLog implements EventSubscriber
continue;
}
$ownerIdentifier = $this->getIdentifier($reflectionClass, $owner);
$ownerIdentifier = $this->getIdentifier($owner);
foreach ($collection->getInsertDiff() as $entity) {
$targetReflectionClass = new ReflectionObject($entity);
@ -134,7 +160,7 @@ class AuditLog implements EventSubscriber
continue;
}
$entityIdentifier = $this->getIdentifier($targetReflectionClass, $entity);
$entityIdentifier = $this->getIdentifier($entity);
$associated[] = [$owner, $ownerIdentifier, $entity, $entityIdentifier];
}
foreach ($collection->getDeleteDiff() as $entity) {
@ -143,7 +169,7 @@ class AuditLog implements EventSubscriber
continue;
}
$entityIdentifier = $this->getIdentifier($targetReflectionClass, $entity);
$entityIdentifier = $this->getIdentifier($entity);
$disassociated[] = [$owner, $ownerIdentifier, $entity, $entityIdentifier];
}
}
@ -167,7 +193,7 @@ class AuditLog implements EventSubscriber
continue;
}
$ownerIdentifier = $this->getIdentifier($reflectionClass, $owner);
$ownerIdentifier = $this->getIdentifier($owner);
foreach ($collection->toArray() as $entity) {
$targetReflectionClass = new ReflectionObject($entity);
@ -175,13 +201,13 @@ class AuditLog implements EventSubscriber
continue;
}
$entityIdentifier = $this->getIdentifier($targetReflectionClass, $entity);
$entityIdentifier = $this->getIdentifier($entity);
$disassociated[] = [$owner, $ownerIdentifier, $entity, $entityIdentifier];
}
}
foreach ($associated as [$owner, $ownerIdentifier, $entity, $entityIdentifier]) {
$newAuditLogs[] = new Entity\AuditLog(
$newRecords[] = new Entity\AuditLog(
Entity\AuditLog::OPER_INSERT,
get_class($owner),
$ownerIdentifier,
@ -192,7 +218,7 @@ class AuditLog implements EventSubscriber
}
foreach ($disassociated as [$owner, $ownerIdentifier, $entity, $entityIdentifier]) {
$newAuditLogs[] = new Entity\AuditLog(
$newRecords[] = new Entity\AuditLog(
Entity\AuditLog::OPER_DELETE,
get_class($owner),
$ownerIdentifier,
@ -202,17 +228,9 @@ class AuditLog implements EventSubscriber
);
}
$auditLogMetadata = $em->getClassMetadata(Entity\AuditLog::class);
foreach ($newAuditLogs as $auditLog) {
$uow->persist($auditLog);
$uow->computeChangeSet($auditLogMetadata, $auditLog);
}
return $newRecords;
}
/**
* @param EntityManagerInterface $em
* @param mixed $class
*/
protected function isEntity(EntityManagerInterface $em, mixed $class): bool
{
if (is_object($class)) {
@ -239,10 +257,9 @@ class AuditLog implements EventSubscriber
/**
* Get the identifier string for an entity, if it's set or fetchable.
*
* @param ReflectionClass $reflectionClass
* @param object $entity
*/
protected function getIdentifier(ReflectionClass $reflectionClass, object $entity): ?string
protected function getIdentifier(object $entity): ?string
{
if ($entity instanceof \Stringable) {
return (string)$entity;

View File

@ -2,8 +2,8 @@
namespace App\Doctrine\Event;
use App\Annotations\AuditLog\AuditIgnore;
use App\Entity;
use App\Entity\Attributes\AuditIgnore;
use Doctrine\Common\Annotations\Reader;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;

View File

@ -4,14 +4,13 @@
namespace App\Entity;
use App\Annotations\AuditLog;
use App\Security\SplitToken;
use Doctrine\ORM\Mapping as ORM;
use JsonSerializable;
use Stringable;
#[
AuditLog\Auditable,
Attributes\Auditable,
ORM\Table(name: 'api_keys'),
ORM\Entity(readOnly: true)
]

View File

@ -1,6 +1,6 @@
<?php
namespace App\Annotations\AuditLog;
namespace App\Entity\Attributes;
use Attribute;

View File

@ -4,7 +4,6 @@
namespace App\Entity;
use App\Annotations\AuditLog;
use Doctrine\ORM\Mapping as ORM;
use OpenApi\Annotations as OA;
use Symfony\Component\Validator\Constraints as Assert;
@ -13,7 +12,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[
ORM\Entity,
ORM\Table(name: 'custom_field'),
AuditLog\Auditable
Attributes\Auditable
]
class CustomField implements \Stringable
{

View File

@ -13,7 +13,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[
ORM\Entity,
ORM\Table(name: 'podcast'),
AuditLog\Auditable
Attributes\Auditable
]
class Podcast
{
@ -42,7 +42,7 @@ class Podcast
protected string $language;
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $art_updated_at = 0;
#[ORM\OneToMany(mappedBy: 'podcast', targetEntity: PodcastCategory::class)]

View File

@ -4,14 +4,13 @@ declare(strict_types=1);
namespace App\Entity;
use App\Annotations\AuditLog;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[
ORM\Entity,
ORM\Table(name: 'podcast_category'),
AuditLog\Auditable
Attributes\Auditable
]
class PodcastCategory
{

View File

@ -12,7 +12,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[
ORM\Entity,
ORM\Table(name: 'podcast_episode'),
AuditLog\Auditable
Attributes\Auditable
]
class PodcastEpisode
{
@ -49,7 +49,7 @@ class PodcastEpisode
protected int $created_at;
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $art_updated_at = 0;
public function __construct(Podcast $podcast)

View File

@ -12,7 +12,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[
ORM\Entity,
ORM\Table(name: 'podcast_media'),
AuditLog\Auditable
Attributes\Auditable
]
class PodcastMedia
{
@ -50,7 +50,7 @@ class PodcastMedia
protected int $modified_time = 0;
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $art_updated_at = 0;
public function __construct(StorageLocation $storageLocation)

View File

@ -4,7 +4,6 @@
namespace App\Entity;
use App\Annotations\AuditLog;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
@ -17,7 +16,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[
ORM\Entity,
ORM\Table(name: 'role'),
AuditLog\Auditable
Attributes\Auditable
]
class Role implements JsonSerializable, Stringable
{

View File

@ -15,7 +15,7 @@ use Symfony\Component\Validator\Constraints as Assert;
/** @OA\Schema(type="object", schema="Settings") */
#[ORM\Entity, ORM\Table(name: 'settings')]
#[AuditLog\Auditable]
#[Attributes\Auditable]
class Settings implements Stringable
{
use Entity\Traits\TruncateStrings;
@ -229,7 +229,7 @@ class Settings implements Stringable
* @var mixed[]|null
*/
#[ORM\Column(type: 'json', nullable: true)]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?array $update_results = null;
/**
@ -252,7 +252,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $update_last_run = 0;
public function getUpdateLastRun(): int
@ -585,7 +585,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $backup_last_run = 0;
public function getBackupLastRun(): int
@ -610,7 +610,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column(type: 'text')]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?string $backup_last_result = null;
public function getBackupLastResult(): ?string
@ -630,7 +630,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column(type: 'text')]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?string $backup_last_output = null;
public function getBackupLastOutput(): ?string
@ -680,7 +680,7 @@ class Settings implements Stringable
* @var mixed[]|null
*/
#[ORM\Column(type: 'json')]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?array $nowplaying = null;
/**
@ -703,7 +703,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $sync_nowplaying_last_run = 0;
public function getSyncNowplayingLastRun(): int
@ -723,7 +723,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $sync_short_last_run = 0;
public function getSyncShortLastRun(): int
@ -743,7 +743,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $sync_medium_last_run = 0;
public function getSyncMediumLastRun(): int
@ -763,7 +763,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $sync_long_last_run = 0;
public function getSyncLongLastRun(): int
@ -816,7 +816,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column(length: 45)]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?string $external_ip = null;
public function getExternalIp(): ?string
@ -857,7 +857,7 @@ class Settings implements Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $geolite_last_run = 0;
public function getGeoliteLastRun(): int

View File

@ -4,7 +4,7 @@
namespace App\Entity;
use App\Annotations\AuditLog\Auditable;
use App\Entity\Attributes\Auditable;
use App\Validator\Constraints\UniqueEntity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

View File

@ -28,7 +28,7 @@ use Symfony\Component\Validator\Constraints as Assert;
ORM\Table(name: 'station'),
ORM\Index(columns: ['short_name'], name: 'idx_short_name'),
ORM\HasLifecycleCallbacks,
AuditLog\Auditable,
Attributes\Auditable,
AppAssert\StationPortChecker,
AppAssert\UniqueEntity(fields: ['short_name'])
]
@ -112,7 +112,7 @@ class Station implements Stringable
protected ?array $backend_config = null;
#[ORM\Column(length: 150)]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?string $adapter_api_key = null;
/** @OA\Property(example="A sample radio station.") */
@ -132,11 +132,11 @@ class Station implements Stringable
protected ?string $radio_base_dir = null;
#[ORM\Column(type: 'array', nullable: true)]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected mixed $nowplaying;
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?int $nowplaying_timestamp = null;
/** @OA\Property(type="array", @OA\Items()) */
@ -144,7 +144,7 @@ class Station implements Stringable
protected ?array $automation_settings = null;
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?int $automation_timestamp = 0;
/**
@ -184,7 +184,7 @@ class Station implements Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected bool $is_streamer_live = false;
/**
@ -215,11 +215,11 @@ class Station implements Stringable
protected bool $enable_on_demand_download = true;
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected bool $needs_restart = false;
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected bool $has_started = false;
/**

View File

@ -17,7 +17,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[
ORM\Entity,
ORM\Table(name: 'station_mounts'),
AuditLog\Auditable
Attributes\Auditable
]
class StationMount implements StationMountInterface, \Stringable
{
@ -91,7 +91,7 @@ class StationMount implements StationMountInterface, \Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $listeners_unique = 0;
/**
@ -101,7 +101,7 @@ class StationMount implements StationMountInterface, \Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $listeners_total = 0;
public function __construct(Station $station)

View File

@ -19,7 +19,7 @@ use Symfony\Component\Validator\Constraints as Assert;
ORM\Entity,
ORM\Table(name: 'station_playlists'),
ORM\HasLifecycleCallbacks,
AuditLog\Auditable
Attributes\Auditable
]
class StationPlaylist implements Stringable
{
@ -156,7 +156,7 @@ class StationPlaylist implements Stringable
protected bool $avoid_duplicates = true;
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $played_at = 0;
#[ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationPlaylistMedia::class, fetch: 'EXTRA_LAZY')]

View File

@ -21,7 +21,7 @@ use const PHP_URL_PORT;
#[
ORM\Entity,
ORM\Table(name: 'station_remotes'),
AuditLog\Auditable
Attributes\Auditable
]
class StationRemote implements StationMountInterface, Stringable
{
@ -110,7 +110,7 @@ class StationRemote implements StationMountInterface, Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $listeners_unique = 0;
/**
@ -120,7 +120,7 @@ class StationRemote implements StationMountInterface, Stringable
* )
*/
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $listeners_total = 0;
public function __construct(Station $station)

View File

@ -4,7 +4,6 @@
namespace App\Entity;
use App\Annotations\AuditLog;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use DateTimeZone;
@ -16,7 +15,7 @@ use OpenApi\Annotations as OA;
#[
ORM\Entity,
ORM\Table(name: 'station_schedules'),
AuditLog\Auditable
Attributes\Auditable
]
class StationSchedule
{

View File

@ -27,7 +27,7 @@ use const PASSWORD_ARGON2ID;
ORM\UniqueConstraint(name: 'username_unique_idx', columns: ['station_id', 'streamer_username']),
ORM\HasLifecycleCallbacks,
UniqueEntity(fields: ['station', 'streamer_username']),
AuditLog\Auditable
Attributes\Auditable
]
class StationStreamer implements \Stringable
{
@ -49,7 +49,7 @@ class StationStreamer implements \Stringable
/** @OA\Property(example="") */
#[ORM\Column(length: 255)]
#[Assert\NotBlank]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected string $streamer_password;
/** @OA\Property(example="Test DJ") */
@ -70,7 +70,7 @@ class StationStreamer implements \Stringable
/** @OA\Property(example=SAMPLE_TIMESTAMP) */
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?int $reactivate_at = null;
/**

View File

@ -16,7 +16,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[
ORM\Entity,
ORM\Table(name: 'station_webhooks', options: ['charset' => 'utf8mb4', 'collate' => 'utf8mb4_unicode_ci']),
AuditLog\Auditable
Attributes\Auditable
]
class StationWebhook implements Stringable
{
@ -92,7 +92,7 @@ class StationWebhook implements Stringable
* )
*/
#[ORM\Column(type: 'json', nullable: true)]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?array $metadata = null;
public function __construct(Station $station, $type)

View File

@ -28,7 +28,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[
ORM\Entity,
ORM\Table(name: 'storage_location'),
AuditLog\Auditable,
Attributes\Auditable,
AppAssert\StorageLocation
]
class StorageLocation implements Stringable
@ -94,7 +94,7 @@ class StorageLocation implements Stringable
protected ?string $storageQuotaBytes = null;
#[ORM\Column(name: 'storage_used', type: 'bigint')]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?string $storageUsed = null;
// Used for API generation.

View File

@ -13,7 +13,7 @@ trait HasSplitTokenFields
protected string $id;
#[ORM\Column(length: 128)]
#[AuditLog\AuditIgnore]
#[\App\Entity\Attributes\AuditIgnore]
protected string $verifier;
protected function setFromToken(SplitToken $token): void

View File

@ -25,7 +25,7 @@ use const PASSWORD_BCRYPT;
ORM\Table(name: 'users'),
ORM\HasLifecycleCallbacks,
ORM\UniqueConstraint(name: 'email_idx', columns: ['email']),
AuditLog\Auditable,
Attributes\Auditable,
UniqueEntity(fields: ['email'])
]
class User implements Stringable
@ -40,7 +40,7 @@ class User implements Stringable
protected ?string $email = null;
#[ORM\Column(length: 255)]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?string $auth_password = null;
/** @OA\Property(example="") */
@ -56,22 +56,22 @@ class User implements Stringable
/** @OA\Property(example="dark") */
#[ORM\Column(length: 25)]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?string $theme = null;
/** @OA\Property(example="A1B2C3D4") */
#[ORM\Column(length: 255)]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected ?string $two_factor_secret = null;
/** @OA\Property(example=SAMPLE_TIMESTAMP) */
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $created_at;
/** @OA\Property(example=SAMPLE_TIMESTAMP) */
#[ORM\Column]
#[AuditLog\AuditIgnore]
#[Attributes\AuditIgnore]
protected int $updated_at;
/**