Skip to content

Commit

Permalink
Merge pull request #2 from Alarich/extra-fields-and-indices
Browse files Browse the repository at this point in the history
DamienHarper#61 Add extra fields and indices to audit tables, based on configuration
  • Loading branch information
Alarich authored May 9, 2022
2 parents 600f64f + 0ac343b commit 9fcd0ca
Show file tree
Hide file tree
Showing 9 changed files with 348 additions and 61 deletions.
97 changes: 93 additions & 4 deletions src/Provider/Doctrine/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace DH\Auditor\Provider\Doctrine;

use DH\Auditor\Provider\ConfigurationInterface;
use DH\Auditor\Provider\Doctrine\Persistence\Helper\SchemaHelper;
use DH\Auditor\Provider\Doctrine\Service\AuditingService;
use Symfony\Component\OptionsResolver\OptionsResolver;

Expand All @@ -19,6 +20,16 @@ class Configuration implements ConfigurationInterface

private string $tableSuffix;

/**
* @var array
*/
private array $extraFields = [];

/**
* @var array
*/
private array $extraIndices = [];

private array $ignoredColumns;

private ?array $entities = null;
Expand Down Expand Up @@ -53,6 +64,20 @@ public function __construct(array $options)
}
}

if (isset($config['extra_fields']) && !empty($config['extra_fields'])) {
// use field names as array keys for easier lookup
foreach ($config['extra_fields'] as $fieldName => $fieldOptions) {
$this->extraFields[$fieldName] = $fieldOptions;
}
}

if (isset($config['extra_indices']) && !empty($config['extra_indices'])) {
// use index names as array keys for easier lookup
foreach ($config['extra_indices'] as $indexName => $indexOptions) {
$this->extraIndices[$indexName] = $indexOptions;
}
}

$this->storageServices = $config['storage_services'];
$this->auditingServices = $config['auditing_services'];
$this->isViewerEnabled = $config['viewer'];
Expand All @@ -68,6 +93,8 @@ public function configureOptions(OptionsResolver $resolver): void
'table_suffix' => '_audit',
'ignored_columns' => [],
'entities' => [],
'extra_fields' => [],
'extra_indices' => [],
'storage_services' => [],
'auditing_services' => [],
'viewer' => true,
Expand All @@ -77,6 +104,8 @@ public function configureOptions(OptionsResolver $resolver): void
->setAllowedTypes('table_suffix', 'string')
->setAllowedTypes('ignored_columns', 'array')
->setAllowedTypes('entities', 'array')
->setAllowedTypes('extra_fields', 'array')
->setAllowedTypes('extra_indices', 'array')
->setAllowedTypes('storage_services', 'array')
->setAllowedTypes('auditing_services', 'array')
->setAllowedTypes('viewer', 'bool')
Expand Down Expand Up @@ -157,6 +186,69 @@ public function getIgnoredColumns(): array
return $this->ignoredColumns;
}

public function getExtraFields(): array
{
return $this->extraFields;
}

public function getAllFields(): array
{
return array_merge(
SchemaHelper::getAuditTableColumns(),
$this->extraFields
);
}

/**
* @param array<string, mixed> $extraFields
*
* @return $this
*/
public function setExtraFields(array $extraFields): self
{
$this->extraFields = $extraFields;

return $this;
}

public function getExtraIndices(): array
{
return $this->extraIndices;
}

public function prepareExtraIndices(string $tablename): array
{
$indices = [];
foreach ($this->extraIndices as $extraIndexField => $extraIndexOptions) {
$indices[$extraIndexField] = [
'type' => $extraIndexOptions['type'] ?? 'index',
'name' => sprintf('%s_%s_idx', $extraIndexOptions['name_prefix'] ?? $extraIndexField, md5($tablename)),
];
}

return $indices;
}

public function getAllIndices(string $tablename): array
{
return array_merge(
SchemaHelper::getAuditTableIndices($tablename),
$this->prepareExtraIndices($tablename)
);
}

/**
* @param array<string, mixed> $extraIndices
*
* @return $this
*/
public function setExtraIndices(array $extraIndices): self
{
$this->extraIndices = $extraIndices;

return $this;
}

/**
* Get the value of entities.
*/
Expand Down Expand Up @@ -216,10 +308,7 @@ public function setStorageMapper(callable $mapper): self
return $this;
}

/**
* @return null|callable|string
*/
public function getStorageMapper()
public function getStorageMapper(): ?callable
{
return $this->storageMapper;
}
Expand Down
22 changes: 2 additions & 20 deletions src/Provider/Doctrine/DoctrineProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ public function getStorageServiceForEntity(string $entity): StorageServiceInterf
return array_values($this->getStorageServices())[0];
}

\assert(\is_callable($storageMapper)); // helps PHPStan

return $storageMapper($entity, $this->getStorageServices());
}

Expand All @@ -99,19 +97,8 @@ public function persist(LifecycleEvent $event): void
$entity = $payload['entity'];
unset($payload['table'], $payload['entity']);

$fields = [
'type' => ':type',
'object_id' => ':object_id',
'discriminator' => ':discriminator',
'transaction_hash' => ':transaction_hash',
'diffs' => ':diffs',
'blame_id' => ':blame_id',
'blame_user' => ':blame_user',
'blame_user_fqdn' => ':blame_user_fqdn',
'blame_user_firewall' => ':blame_user_firewall',
'ip' => ':ip',
'created_at' => ':created_at',
];
$fields = array_combine(array_keys($payload), array_map(function ($x) {return ":{$x}"; }, array_keys($payload)));
\assert(\is_array($fields)); // helps PHPStan

$query = sprintf(
'INSERT INTO %s (%s) VALUES (%s)',
Expand All @@ -129,11 +116,6 @@ public function persist(LifecycleEvent $event): void
}

$statement->execute();

// let's get the last inserted ID from the database so other providers can use that info
$payload = $event->getPayload();
$payload['id'] = (int) $storageService->getEntityManager()->getConnection()->lastInsertId();
$event->setPayload($payload);
}

/**
Expand Down
13 changes: 11 additions & 2 deletions src/Provider/Doctrine/Persistence/Reader/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use DH\Auditor\Exception\InvalidArgumentException;
use DH\Auditor\Model\Entry;
use DH\Auditor\Provider\ConfigurationInterface;
use DH\Auditor\Provider\Doctrine\Configuration;
use DH\Auditor\Provider\Doctrine\Persistence\Helper\SchemaHelper;
use DH\Auditor\Provider\Doctrine\Persistence\Reader\Filter\DateRangeFilter;
use DH\Auditor\Provider\Doctrine\Persistence\Reader\Filter\FilterInterface;
Expand Down Expand Up @@ -37,14 +39,21 @@ class Query

private string $table;

/**
* @var Configuration
*/
private ConfigurationInterface $configuration;

private int $offset = 0;

private int $limit = 0;

public function __construct(string $table, Connection $connection)
public function __construct(string $table, Connection $connection, ConfigurationInterface $configuration)
{
$this->connection = $connection;
$this->table = $table;
\assert($configuration instanceof Configuration);
$this->configuration = $configuration;

foreach ($this->getSupportedFilters() as $filterType) {
$this->filters[$filterType] = [];
Expand Down Expand Up @@ -149,7 +158,7 @@ public function limit(int $limit, int $offset = 0): self

public function getSupportedFilters(): array
{
return array_keys(SchemaHelper::getAuditTableIndices('fake'));
return array_keys($this->configuration->getAllIndices('fake'));
}

public function getFilters(): array
Expand Down
17 changes: 16 additions & 1 deletion src/Provider/Doctrine/Persistence/Reader/Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ public function createQuery(string $entity, array $options = []): Query
/** @var StorageService $storageService */
$storageService = $this->provider->getStorageServiceForEntity($entity);

$query = new Query($this->getEntityAuditTableName($entity), $storageService->getEntityManager()->getConnection());
$query = new Query(
$this->getEntityAuditTableName($entity),
$storageService->getEntityManager()->getConnection(),
$this->provider->getConfiguration()
);
$query
->addOrderBy(Query::CREATED_AT, 'DESC')
->addOrderBy(Query::ID, 'DESC')
Expand Down Expand Up @@ -83,6 +87,12 @@ public function createQuery(string $entity, array $options = []): Query
$query->addFilter(new SimpleFilter(Query::DISCRIMINATOR, $entity));
}

foreach ($this->provider->getConfiguration()->getExtraIndices() as $indexedField => $extraIndexConfig) {
if (null !== $config[$indexedField]) {
$query->addFilter($indexedField, $config[$indexedField]);
}
}

return $query;
}

Expand All @@ -107,6 +117,11 @@ public function configureOptions(OptionsResolver $resolver): void
->setAllowedValues('page', static fn ($value) => null === $value || $value >= 1)
->setAllowedValues('page_size', static fn ($value) => null === $value || $value >= 1)
;

foreach ($this->provider->getConfiguration()->getExtraIndices() as $indexedField => $extraIndexConfig) {
$resolver->setDefault($indexedField, null);
$resolver->setAllowedTypes($indexedField, ['null', 'int', 'string', 'array']);
}
}

/**
Expand Down
11 changes: 6 additions & 5 deletions src/Provider/Doctrine/Persistence/Schema/SchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use DH\Auditor\Provider\Doctrine\DoctrineProvider;
use DH\Auditor\Provider\Doctrine\Persistence\Helper\DoctrineHelper;
use DH\Auditor\Provider\Doctrine\Persistence\Helper\PlatformHelper;
use DH\Auditor\Provider\Doctrine\Persistence\Helper\SchemaHelper;
use DH\Auditor\Provider\Doctrine\Service\AuditingService;
use DH\Auditor\Provider\Doctrine\Service\StorageService;
use Doctrine\DBAL\Connection;
Expand Down Expand Up @@ -190,7 +189,7 @@ public function createAuditTable(string $entity, $table, ?Schema $schema = null)

// Add columns to audit table
$isJsonSupported = PlatformHelper::isJsonSupported($connection);
foreach (SchemaHelper::getAuditTableColumns() as $columnName => $struct) {
foreach ($configuration->getAllFields() as $columnName => $struct) {
if (DoctrineHelper::getDoctrineType('JSON') === $struct['type'] && $isJsonSupported) {
$type = DoctrineHelper::getDoctrineType('TEXT');
} else {
Expand All @@ -201,7 +200,7 @@ public function createAuditTable(string $entity, $table, ?Schema $schema = null)
}

// Add indices to audit table
foreach (SchemaHelper::getAuditTableIndices($auditTablename) as $columnName => $struct) {
foreach ($configuration->getAllIndices($auditTablename) as $columnName => $struct) {
if ('primary' === $struct['type']) {
$auditTable->setPrimaryKey([$columnName]);
} else {
Expand Down Expand Up @@ -234,14 +233,16 @@ public function updateAuditTable(string $entity, Table $table, ?Schema $schema =
$schema = $schemaManager->createSchema();
}

/** @var Configuration $configuration */
$configuration = $this->provider->getConfiguration();
$table = $schema->getTable($table->getName());
$columns = $schemaManager->listTableColumns($table->getName());

// process columns
$this->processColumns($table, $columns, SchemaHelper::getAuditTableColumns(), $connection);
$this->processColumns($table, $columns, $configuration->getAllFields(), $connection);

// process indices
$this->processIndices($table, SchemaHelper::getAuditTableIndices($table->getName()), $connection);
$this->processIndices($table, $configuration->getAllIndices($table->getName()), $connection);

return $schema;
}
Expand Down
71 changes: 71 additions & 0 deletions tests/Provider/Doctrine/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,75 @@ public function testGetEntities(): void

self::assertSame($entities, $configuration->getEntities(), 'AuditConfiguration::getEntities() returns configured entities list.');
}

public function testGetExtraFields(): void
{
$extraFields = [
'example_int_field' => [
'type' => 'integer',
'options' => [
'notnull' => true,
],
],
'example_string_field' => [
'type' => 'string',
'options' => [
'notnull' => false,
'length' => 50,
],
],
];

$configuration = $this->createProviderConfiguration([
'extra_fields' => $extraFields,
]);

self::assertSame($extraFields, $configuration->getExtraFields(), 'AuditConfiguration::getExtraFields() returns configured extra fields list.');
}

public function testGetExtraIndices(): void
{
$extraIndices = [
'example_default_index' => null,
'example_configured_index' => [
'type' => 'primary',
'name_prefix' => 'another_prefix',
],
];

$configuration = $this->createProviderConfiguration([
'extra_indices' => $extraIndices,
]);

self::assertSame($extraIndices, $configuration->getExtraIndices(), 'AuditConfiguration::getExtraIndices() returns configured extra indices list.');
}

public function testPrepareExtraIndices(): void
{
$extraIndicesConfig = [
'example_default_index' => null,
'example_configured_index' => [
'type' => 'primary',
'name_prefix' => 'another_prefix',
],
];
$tableName = 'test_table';

$extraIndicesExpected = [
'example_default_index' => [
'type' => 'index',
'name' => 'example_default_index_'.md5($tableName).'_idx',
],
'example_configured_index' => [
'type' => 'primary',
'name' => 'another_prefix_'.md5($tableName).'_idx',
],
];

$configuration = $this->createProviderConfiguration([
'extra_indices' => $extraIndicesConfig,
]);

self::assertSame($extraIndicesExpected, $configuration->prepareExtraIndices($tableName), 'AuditConfiguration::prepareExtraIndices() returns transformed index list.');
}
}
Loading

0 comments on commit 9fcd0ca

Please sign in to comment.