Skip to content

Commit

Permalink
TASK: Adjust to inlined ProvidesSetupInterface and ProvidesStatusInte…
Browse files Browse the repository at this point in the history
…rface

Adjusts classes `DoctrineEventStore` and `DoctrineCheckpointStorage` to the adjusted `neos/eventstore` package and fixes implementation of `DoctrineEventStore::status()`

Related: neos/eventstore#16
  • Loading branch information
bwaidelich committed Jan 12, 2024
1 parent b1b208c commit 5e1ccaf
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 26 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
],
"require": {
"php": "^8.1",
"neos/eventstore": "1.0.0-beta1",
"neos/eventstore": "@dev",
"doctrine/dbal": "^2",
"webmozart/assert": "^1.10",
"psr/clock": "^1"
Expand Down
10 changes: 3 additions & 7 deletions src/DoctrineCheckpointStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace Neos\EventStore\DoctrineAdapter;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception\DriverException as DBALDriverException;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Exception\DriverException as DBALDriverException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Platforms\MySqlPlatform;
Expand All @@ -16,10 +16,8 @@
use Neos\EventStore\CatchUp\CheckpointStorageInterface;
use Neos\EventStore\Exception\CheckpointException;
use Neos\EventStore\Model\Event\SequenceNumber;
use Neos\EventStore\Model\EventStore\SetupResult;
use Neos\EventStore\ProvidesSetupInterface;

final class DoctrineCheckpointStorage implements CheckpointStorageInterface, ProvidesSetupInterface
final class DoctrineCheckpointStorage implements CheckpointStorageInterface
{

private MySqlPlatform|PostgreSqlPlatform $platform;
Expand Down Expand Up @@ -94,7 +92,7 @@ public function getHighestAppliedSequenceNumber(): SequenceNumber
return SequenceNumber::fromInteger((int)$highestAppliedSequenceNumber);
}

public function setup(): SetupResult
public function setup(): void
{
$schemaManager = $this->connection->getSchemaManager();
if (!$schemaManager instanceof AbstractSchemaManager) {
Expand All @@ -115,7 +113,5 @@ public function setup(): SetupResult
} catch (UniqueConstraintViolationException $e) {
// table and row already exists, ignore
}

return SetupResult::success('');
}
}
48 changes: 30 additions & 18 deletions src/DoctrineEventStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,16 @@
use Neos\EventStore\Model\Event\Version;
use Neos\EventStore\Model\Events;
use Neos\EventStore\Model\EventStore\CommitResult;
use Neos\EventStore\Model\EventStore\SetupResult;
use Neos\EventStore\Model\EventStore\Status;
use Neos\EventStore\Model\EventStream\EventStreamFilter;
use Neos\EventStore\Model\EventStream\EventStreamInterface;
use Neos\EventStore\Model\EventStream\ExpectedVersion;
use Neos\EventStore\Model\EventStream\MaybeVersion;
use Neos\EventStore\Model\EventStream\VirtualStreamName;
use Neos\EventStore\Model\EventStream\VirtualStreamType;
use Neos\EventStore\ProvidesSetupInterface;
use Neos\EventStore\ProvidesStatusInterface;
use Psr\Clock\ClockInterface;

final class DoctrineEventStore implements EventStoreInterface, ProvidesSetupInterface, ProvidesStatusInterface
final class DoctrineEventStore implements EventStoreInterface
{
private readonly ClockInterface $clock;

Expand Down Expand Up @@ -127,27 +124,42 @@ public function deleteStream(StreamName $streamName): void

public function status(): Status
{
return Status::error('not implemented');
try {
$this->connection->connect();
} catch (DbalException $e) {
return Status::error(sprintf('Failed to connect to database: %s', $e->getMessage()));
}
$requiredSqlStatements = $this->determineRequiredSqlStatements();
if ($requiredSqlStatements !== []) {
return Status::setupRequired(sprintf('The following SQL statement%s required: %s', count($requiredSqlStatements) !== 1 ? 's are' : ' is', implode(chr(10), $requiredSqlStatements)));
}
return Status::ok();
}

public function setup(): SetupResult
public function setup(): void
{
$schemaManager = $this->connection->getSchemaManager();
assert($schemaManager !== null);
$fromSchema = $schemaManager->createSchema();
$schemaDiff = (new Comparator())->compare($fromSchema, $this->createEventStoreSchema());

$statements = $schemaDiff->toSaveSql($this->connection->getDatabasePlatform());
if ($statements === []) {
return SetupResult::success('Table schema is up to date, no migration required');
}

foreach ($statements as $statement) {
foreach ($this->determineRequiredSqlStatements() as $statement) {
$this->connection->executeStatement($statement);
}
return SetupResult::success('Event store table created/updated successfully');
}

/**
* @return array<string>
*/
private function determineRequiredSqlStatements(): array
{
$schemaManager = $this->connection->getSchemaManager();
assert($schemaManager !== null);
$platform = $this->connection->getDatabasePlatform();
assert($platform !== null);
if (!$schemaManager->tablesExist($this->eventTableName)) {
return $platform->getCreateTableSQL($this->createEventStoreSchema()->getTable($this->eventTableName));
}
$tableSchema = $schemaManager->listTableDetails($this->eventTableName);
$fromSchema = new Schema([$tableSchema], [], $schemaManager->createSchemaConfig());
$schemaDiff = (new Comparator())->compare($fromSchema, $this->createEventStoreSchema());
return $schemaDiff->toSaveSql($platform);
}

// ----------------------------------

Expand Down
41 changes: 41 additions & 0 deletions tests/Integration/DoctrineEventStoreTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception as DbalException;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Neos\EventStore\DoctrineAdapter\DoctrineEventStore;
use Neos\EventStore\EventStoreInterface;
use Neos\EventStore\Model\EventStore\StatusType;
use Neos\EventStore\Tests\Integration\AbstractEventStoreTestBase;
use PHPUnit\Framework\Attributes\CoversClass;

Expand Down Expand Up @@ -50,4 +52,43 @@ public static function eventTableName(): string
return 'events_test';
}

public function test_setup_throws_exception_if_database_connection_fails(): void
{
$connection = DriverManager::getConnection(['url' => 'mysql://invalid-connection']);
$eventStore = new DoctrineEventStore($connection, self::eventTableName());

$this->expectException(DbalException::class);
$eventStore->setup();
}

public function test_status_returns_error_status_if_database_connection_fails(): void
{
$connection = DriverManager::getConnection(['url' => 'mysql://invalid-connection']);
$eventStore = new DoctrineEventStore($connection, self::eventTableName());
self::assertSame($eventStore->status()->type, StatusType::ERROR);
}

public function test_status_returns_setup_required_status_if_event_table_is_missing(): void
{
$connection = DriverManager::getConnection(['url' => 'sqlite:///:memory:']);
$eventStore = new DoctrineEventStore($connection, self::eventTableName());
self::assertSame($eventStore->status()->type, StatusType::SETUP_REQUIRED);
}

public function test_status_returns_setup_required_status_if_event_table_requires_update(): void
{
$connection = self::connection();
$eventStore = new DoctrineEventStore($connection, self::eventTableName());
$eventStore->setup();
$connection->executeStatement('ALTER TABLE ' . $connection->quote(self::eventTableName()) . ' RENAME COLUMN metadata TO metadata_renamed');
self::assertSame($eventStore->status()->type, StatusType::SETUP_REQUIRED);
}

public function test_status_returns_ok_status_if_event_table_is_up_to_date(): void
{
$connection = self::connection();
$eventStore = new DoctrineEventStore($connection, self::eventTableName());
$eventStore->setup();
self::assertSame($eventStore->status()->type, StatusType::OK);
}
}

0 comments on commit 5e1ccaf

Please sign in to comment.