Skip to content
Snippets Groups Projects
Unverified Commit 9871b6c8 authored by Marko Ivancic's avatar Marko Ivancic Committed by GitHub
Browse files

Enable job enqueue and dequeue (#2)


* Add JobsStore setup methods
* Enable job enqueue
* Enable job dequeue
* Code cleanup and coverage

Co-authored-by: default avatarMarko Ivančić <marko.ivancic@srce.hr>
parent 86faf7ea
Branches
No related tags found
1 merge request!1Relate histories
Showing
with 330 additions and 143 deletions
......@@ -30,6 +30,7 @@
"ext-pdo_mysql": "*",
"ext-pdo_sqlite": "*",
"doctrine/dbal": "^3",
"psr/log": "^1|^2|^3",
"simplesamlphp/composer-module-installer": "^1"
},
"require-dev": {
......
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Controller;
use Exception;
use SimpleSAML\Configuration;
use SimpleSAML\Locale\Translate;
use SimpleSAML\Module\accounting\ModuleConfiguration;
......@@ -35,7 +36,7 @@ class Test
/**
* @param Request $request
* @return Template
* @throws \Exception
* @throws Exception
*/
public function test(Request $request): Template
{
......
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Entities;
use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
class AuthenticationEvent extends AbstractPayload
{
protected array $state;
public function __construct(array $state)
{
$this->state = $state;
}
public function getState(): array
{
return $this->state;
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Entities\AuthenticationEvent;
use SimpleSAML\Module\accounting\Entities\AuthenticationEvent;
use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
use SimpleSAML\Module\accounting\Entities\GenericJob;
use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
class Job extends GenericJob
{
public function getPayload(): AuthenticationEvent
{
return $this->validatePayload($this->payload);
}
public function setPayload(AbstractPayload $payload): void
{
$this->payload = $this->validatePayload($payload);
}
protected function validatePayload(AbstractPayload $payload): AuthenticationEvent
{
if (! ($payload instanceof AuthenticationEvent)) {
throw new UnexpectedValueException('Job payload must be of type AuthenticationEvent.');
}
return $payload;
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Entities\Bases;
use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
abstract class AbstractJob implements JobInterface
{
protected AbstractPayload $payload;
public function __construct(AbstractPayload $payload)
{
$this->setPayload($payload);
}
public function getPayload(): AbstractPayload
{
return $this->payload;
}
public function setPayload(AbstractPayload $payload): void
{
$this->payload = $payload;
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Entities\Bases;
abstract class AbstractPayload
{
}
<?php
namespace SimpleSAML\Module\accounting\Entities;
class GenericJob extends Bases\AbstractJob
{
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Entities\Interfaces;
use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
interface JobInterface
{
public function getPayload(): AbstractPayload;
public function setPayload(AbstractPayload $payload): void;
}
......@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Exceptions;
class InvalidConfigurationException extends \ValueError
use ValueError;
class InvalidConfigurationException extends ValueError
{
}
......@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Exceptions;
class InvalidValueException extends \ValueError
use ValueError;
class InvalidValueException extends ValueError
{
}
......@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Exceptions;
class MigrationException extends \Exception
use Exception;
class StoreException extends Exception
{
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Exceptions\StoreException;
use SimpleSAML\Module\accounting\Exceptions\StoreException;
class MigrationException extends StoreException
{
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Exceptions;
class UnexpectedValueException extends \UnexpectedValueException
{
}
......@@ -10,12 +10,12 @@ class FilesystemHelper
{
public static function getRealPath(string $path): string
{
$path = realpath($path);
$realpath = realpath($path);
if ($path === false || ! (is_dir($path) || is_file($path))) {
throw new InvalidValueException('Given path can not be translated to real path.');
if ($realpath === false || ! (is_dir($realpath) || is_file($realpath))) {
throw new InvalidValueException(sprintf('Given path can not be translated to real path (%s).', $path));
}
return $path;
return $realpath;
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Services;
use Psr\Log\AbstractLogger;
use Psr\Log\LogLevel;
use SimpleSAML\Logger;
class LoggerService
class LoggerService extends AbstractLogger
{
/**
* Log an emergency message.
*
* @param string $message The message to log.
*/
public function emergency(string $message): void
{
Logger::emergency($message);
}
/**
* Log a critical message.
*
* @param string $message The message to log.
*/
public function critical(string $message): void
{
Logger::critical($message);
}
/**
* Log an alert message.
*
* @param string $message The message to log.
*/
public function alert(string $message): void
{
Logger::alert($message);
}
/**
* Log an error message.
*
* @param string $message The message to log.
*/
public function error(string $message): void
{
Logger::error($message);
}
/**
* Log a warning message.
*
* @param string $message The message to log.
*/
public function warning(string $message): void
{
Logger::warning($message);
}
/**
* Log a notice message.
*
* @param string $message The message to log.
*/
public function notice(string $message): void
{
Logger::notice($message);
}
/**
* Log an info message (a bit less verbose than debug messages).
*
* @param string $message The message to log.
*/
public function info(string $message): void
{
Logger::info($message);
}
/**
* Log a debug message (very verbose messages).
*
* @param string $message The message to log.
*/
public function debug(string $message): void
{
Logger::debug($message);
}
/**
* Log a statistics message.
public function log($level, $message, array $context = [])
{
if (! empty($context)) {
$message .= ' Context: ' . var_export($context, true);
}
switch ($level) {
case LogLevel::EMERGENCY:
Logger::emergency($message);
break;
case LogLevel::CRITICAL:
Logger::critical($message);
break;
case LogLevel::ALERT:
Logger::alert($message);
break;
case LogLevel::ERROR:
Logger::error($message);
break;
case LogLevel::WARNING:
Logger::warning($message);
break;
case LogLevel::NOTICE:
Logger::notice($message);
break;
case LogLevel::INFO:
Logger::info($message);
break;
case LogLevel::DEBUG:
Logger::debug($message);
break;
}
}
/**
* Log an SSP statistics message.
*
* @param string $message The message to log.
*/
......
......@@ -5,9 +5,10 @@ declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Stores\Connections\Bases;
use SimpleSAML\Module\accounting\Exceptions\InvalidValueException;
use SimpleSAML\Module\accounting\Exceptions\MigrationException;
use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
use SimpleSAML\Module\accounting\Helpers\FilesystemHelper;
use SimpleSAML\Module\accounting\Stores\Interfaces\MigrationInterface;
use Throwable;
abstract class AbstractMigrator
{
......@@ -49,6 +50,7 @@ abstract class AbstractMigrator
/**
* @param class-string[] $migrationClasses
* @return void
* @throws MigrationException
*/
public function runMigrationClasses(array $migrationClasses): void
{
......@@ -59,14 +61,14 @@ abstract class AbstractMigrator
try {
$migration->run();
} catch (\Throwable $exception) {
} catch (Throwable $exception) {
$message = sprintf(
'Could not run migration class %s. Error was: %s',
$migrationClass,
$exception->getMessage()
);
throw new MigrationException($message);
throw new MigrationException($message, (int) $exception->getCode(), $exception);
}
$this->markImplementedMigrationClass($migrationClass);
......@@ -94,6 +96,9 @@ abstract class AbstractMigrator
return ! empty($this->getNonImplementedMigrationClasses($directory, $namespace));
}
/**
* @throws MigrationException
*/
public function runNonImplementedMigrationClasses(string $directory, string $namespace): void
{
$this->runMigrationClasses($this->getNonImplementedMigrationClasses($directory, $namespace));
......
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use SimpleSAML\Module\accounting\Exceptions\StoreException;
use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
use SimpleSAML\Module\accounting\Stores\Interfaces\MigrationInterface;
use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
use Throwable;
abstract class AbstractMigration implements MigrationInterface
{
protected Connection $connection;
protected AbstractSchemaManager $schemaManager;
/**
* @throws StoreException
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
$this->schemaManager = $this->connection->dbal()->createSchemaManager();
try {
$this->schemaManager = $this->connection->dbal()->createSchemaManager();
} catch (Throwable $exception) {
$message = 'Could not create DBAL schema manager.';
throw new StoreException($message, (int) $exception->getCode(), $exception);
}
}
/**
* @throws MigrationException
*/
protected function throwGenericMigrationException(string $contextDetails, Throwable $throwable): void
{
$message = sprintf(
'There was an error running a migration class %s. Context details: %s. Error was: %s.',
static::class,
$contextDetails,
$throwable->getMessage()
);
throw new MigrationException($message, (int) $throwable->getCode(), $throwable);
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal;
use Psr\Log\LoggerInterface;
use SimpleSAML\Module\accounting\ModuleConfiguration;
use SimpleSAML\Module\accounting\Services\LoggerService;
class Factory
{
protected ModuleConfiguration $moduleConfiguration;
protected LoggerService $loggerService;
protected LoggerInterface $loggerService;
public function __construct(ModuleConfiguration $moduleConfiguration, LoggerService $loggerService)
public function __construct(ModuleConfiguration $moduleConfiguration, LoggerInterface $loggerService)
{
$this->moduleConfiguration = $moduleConfiguration;
$this->loggerService = $loggerService;
......@@ -21,7 +23,7 @@ class Factory
return new Connection($this->moduleConfiguration->getStoreConnectionParameters($connectionKey));
}
public function buildMigrator(Connection $connection, LoggerService $loggerService = null): Migrator
public function buildMigrator(Connection $connection, LoggerInterface $loggerService = null): Migrator
{
return new Migrator($connection, $loggerService ?? $this->loggerService);
}
......
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal;
use DateTimeImmutable;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Types;
use Psr\Log\LoggerInterface;
use ReflectionClass;
use ReflectionException;
use SimpleSAML\Module\accounting\Exceptions\InvalidValueException;
use SimpleSAML\Module\accounting\Services\LoggerService;
use SimpleSAML\Module\accounting\Exceptions\StoreException;
use SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator;
use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
use SimpleSAML\Module\accounting\Stores\Interfaces\MigrationInterface;
use Throwable;
class Migrator extends AbstractMigrator
{
......@@ -20,57 +27,87 @@ class Migrator extends AbstractMigrator
public const COLUMN_NAME_CREATED_AT = 'created_at';
protected Connection $connection;
protected LoggerService $loggerService;
protected LoggerInterface $logger;
protected AbstractSchemaManager $schemaManager;
protected string $prefixedTableName;
public function __construct(Connection $connection, LoggerService $loggerService)
/**
* @throws StoreException
*/
public function __construct(Connection $connection, LoggerInterface $logger)
{
$this->connection = $connection;
$this->loggerService = $loggerService;
$this->logger = $logger;
$this->schemaManager = $this->connection->dbal()->createSchemaManager();
try {
$this->schemaManager = $this->connection->dbal()->createSchemaManager();
} catch (Throwable $exception) {
$message = 'Could not create DBAL schema manager.';
throw new StoreException($message, (int) $exception->getCode(), $exception);
}
$this->prefixedTableName = $this->connection->preparePrefixedTableName(self::TABLE_NAME);
}
/**
* @throws StoreException
*/
public function needsSetup(): bool
{
return ! $this->schemaManager->tablesExist([$this->prefixedTableName]);
try {
return ! $this->schemaManager->tablesExist([$this->prefixedTableName]);
} catch (Throwable $exception) {
$message = sprintf('Could not check table %s existence using schema manager.', $this->prefixedTableName);
throw new StoreException($message, (int) $exception->getCode(), $exception);
}
}
/**
* @throws StoreException
*/
public function runSetup(): void
{
if (! $this->needsSetup()) {
$this->loggerService->warning('Migrator setup has been called, however setup is not needed.');
$this->logger->warning('Migrator setup has been called, however setup is not needed.');
return;
}
$this->createMigrationsTable();
}
/**
* @throws StoreException
*/
protected function createMigrationsTable(): void
{
$table = new Table($this->prefixedTableName);
$table->addColumn(self::COLUMN_NAME_ID, Types::BIGINT)
->setAutoincrement(true)
->setUnsigned(true);
$table->addColumn(self::COLUMN_NAME_VERSION, Types::STRING);
$table->addColumn(self::COLUMN_NAME_CREATED_AT, Types::DATETIMETZ_IMMUTABLE);
$table->setPrimaryKey(['id']);
$table->addUniqueIndex([self::COLUMN_NAME_VERSION]);
$this->schemaManager->createTable($table);
try {
$table = new Table($this->prefixedTableName);
$table->addColumn(self::COLUMN_NAME_ID, Types::BIGINT)
->setAutoincrement(true)
->setUnsigned(true);
$table->addColumn(self::COLUMN_NAME_VERSION, Types::STRING);
$table->addColumn(self::COLUMN_NAME_CREATED_AT, Types::DATETIMETZ_IMMUTABLE);
$table->setPrimaryKey(['id']);
$table->addUniqueIndex([self::COLUMN_NAME_VERSION]);
$this->schemaManager->createTable($table);
} catch (Throwable $exception) {
$message = sprintf('Error creating migrations table %s.', $this->prefixedTableName);
throw new StoreException($message, (int) $exception->getCode(), $exception);
}
}
/**
* @throws ReflectionException
*/
protected function buildMigrationClassInstance(string $migrationClass): MigrationInterface
{
$this->validateDoctrineDbalMigrationClass($migrationClass);
/** @var MigrationInterface $migration */
$migration = (new \ReflectionClass($migrationClass))->newInstance($this->connection);
$migration = (new ReflectionClass($migrationClass))->newInstance($this->connection);
return $migration;
}
......@@ -82,40 +119,56 @@ class Migrator extends AbstractMigrator
}
}
/**
* @throws StoreException
*/
protected function markImplementedMigrationClass(string $migrationClass): void
{
$queryBuilder = $this->connection->dbal()->createQueryBuilder();
$queryBuilder->insert($this->prefixedTableName)
->values(
[
self::COLUMN_NAME_VERSION => ':' . self::COLUMN_NAME_VERSION,
self::COLUMN_NAME_CREATED_AT => ':' . self::COLUMN_NAME_CREATED_AT,
]
)
->setParameters(
[
self::COLUMN_NAME_VERSION => $migrationClass,
self::COLUMN_NAME_CREATED_AT => new \DateTimeImmutable(),
],
[
self::COLUMN_NAME_VERSION => Types::STRING,
self::COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE
]
);
$queryBuilder->executeStatement();
try {
$queryBuilder->insert($this->prefixedTableName)
->values(
[
self::COLUMN_NAME_VERSION => ':' . self::COLUMN_NAME_VERSION,
self::COLUMN_NAME_CREATED_AT => ':' . self::COLUMN_NAME_CREATED_AT,
]
)
->setParameters(
[
self::COLUMN_NAME_VERSION => $migrationClass,
self::COLUMN_NAME_CREATED_AT => new DateTimeImmutable(),
],
[
self::COLUMN_NAME_VERSION => Types::STRING,
self::COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE
]
);
$queryBuilder->executeStatement();
} catch (Throwable $exception) {
$message = sprintf('Error marking implemented migrations class %s.', $migrationClass);
throw new StoreException($message, (int) $exception->getCode(), $exception);
}
}
/**
* @throws StoreException
*/
public function getImplementedMigrationClasses(): array
{
$queryBuilder = $this->connection->dbal()->createQueryBuilder();
try {
$queryBuilder = $this->connection->dbal()->createQueryBuilder();
$queryBuilder->select(self::COLUMN_NAME_VERSION)
->from($this->prefixedTableName);
$queryBuilder->select(self::COLUMN_NAME_VERSION)
->from($this->prefixedTableName);
/** @var class-string[] $migrationClasses */
$migrationClasses = $queryBuilder->executeQuery()->fetchFirstColumn();
/** @var class-string[] $migrationClasses */
$migrationClasses = $queryBuilder->executeQuery()->fetchFirstColumn();
} catch (Throwable $exception) {
$message = 'Error getting implemented migration classes.';
throw new StoreException($message, (int) $exception->getCode(), $exception);
}
return $migrationClasses;
}
......
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\accounting\Stores\Interfaces;
interface ConnectionInterface
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment