Skip to content

Commit

Permalink
Merge pull request #85 from patchlevel/database-commands
Browse files Browse the repository at this point in the history
add database commands
  • Loading branch information
DanielBadura authored Nov 29, 2021
2 parents e68a9a7 + 2ec8ad3 commit 7ec9686
Show file tree
Hide file tree
Showing 11 changed files with 626 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ infection: vendor
vendor/bin/infection

.PHONY: static
static: phpstan psalm phpcs-check ## run static analyser
static: psalm phpstan phpcs-check ## run static analyser

test: phpunit ## run tests

Expand Down
2 changes: 1 addition & 1 deletion infection.json.dist
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"mutators": {
"@default": true
},
"minMsi": 56,
"minMsi": 58,
"minCoveredMsi": 90
}
5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
parameters:
ignoreErrors:
-
message: "#^Method Patchlevel\\\\EventSourcing\\\\Console\\\\DoctrineHelper\\:\\:databaseName\\(\\) should return string but returns mixed\\.$#"
count: 2
path: src/Console/DoctrineHelper.php

-
message: "#^While loop condition is always true\\.$#"
count: 1
Expand Down
83 changes: 83 additions & 0 deletions src/Console/Command/DatabaseCreateCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Console\Command;

use Patchlevel\EventSourcing\Console\DoctrineHelper;
use Patchlevel\EventSourcing\Console\InputHelper;
use Patchlevel\EventSourcing\Store\DoctrineStore;
use Patchlevel\EventSourcing\Store\Store;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;

use function sprintf;

class DatabaseCreateCommand extends Command
{
private Store $store;
private DoctrineHelper $helper;

public function __construct(Store $store, DoctrineHelper $helper)
{
parent::__construct();

$this->store = $store;
$this->helper = $helper;
}

protected function configure(): void
{
$this
->setName('event-sourcing:database:create')
->setDescription('create eventstore database')
->addOption('if-not-exists', null, InputOption::VALUE_NONE, 'Don\'t trigger an error, when the database already exists');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$console = new SymfonyStyle($input, $output);
$store = $this->store;

if (!$store instanceof DoctrineStore) {
$console->error('Store is not supported!');

return 1;
}

$connection = $store->connection();

$databaseName = $this->helper->databaseName($connection);
$tempConnection = $this->helper->copyConnectionWithoutDatabase($connection);

$ifNotExists = InputHelper::bool($input->getOption('if-not-exists'));
$hasDatabase = $this->helper->hasDatabase($tempConnection, $databaseName);

if ($ifNotExists && $hasDatabase) {
$console->warning(sprintf('Database "%s" already exists. Skipped.', $databaseName));
$tempConnection->close();

return 0;
}

try {
$this->helper->createDatabase($tempConnection, $databaseName);
$console->success(sprintf('Created database "%s"', $databaseName));
} catch (Throwable $e) {
$console->error(sprintf('Could not create database "%s"', $databaseName));
$console->error($e->getMessage());

$tempConnection->close();

return 2;
}

$tempConnection->close();

return 0;
}
}
87 changes: 87 additions & 0 deletions src/Console/Command/DatabaseDropCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Console\Command;

use Patchlevel\EventSourcing\Console\DoctrineHelper;
use Patchlevel\EventSourcing\Console\InputHelper;
use Patchlevel\EventSourcing\Store\DoctrineStore;
use Patchlevel\EventSourcing\Store\Store;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;

use function sprintf;

class DatabaseDropCommand extends Command
{
private Store $store;
private DoctrineHelper $helper;

public function __construct(Store $store, DoctrineHelper $helper)
{
parent::__construct();

$this->store = $store;
$this->helper = $helper;
}

protected function configure(): void
{
$this
->setName('event-sourcing:database:drop')
->setDescription('drop eventstore database')
->addOption('if-exists', null, InputOption::VALUE_NONE, 'Don\'t trigger an error, when the database doesn\'t exist')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Set this parameter to execute this action');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$console = new SymfonyStyle($input, $output);
$store = $this->store;

if (!$store instanceof DoctrineStore) {
$console->error('Store is not supported!');

return 1;
}

$connection = $store->connection();
$databaseName = $this->helper->databaseName($connection);

$force = InputHelper::bool($input->getOption('force'));

if (!$force) {
$console->caution('This operation should not be executed in a production environment.');
$console->warning(sprintf('Would drop the database "%s". Please run the operation with --force to execute.', $databaseName));
$console->caution('All data will be lost!');

return 2;
}

$ifExists = InputHelper::bool($input->getOption('if-exists'));
$hasDatabase = $this->helper->hasDatabase($connection, $databaseName);

if ($ifExists && !$hasDatabase) {
$console->warning(sprintf('Database "%s" doesn\'t exist. Skipped.', $databaseName));

return 0;
}

try {
$this->helper->dropDatabase($connection, $databaseName);
$console->success(sprintf('Dropped database "%s"', $databaseName));

return 0;
} catch (Throwable $e) {
$console->error(sprintf('Could not drop database "%s"', $databaseName));
$console->error($e->getMessage());

return 3;
}
}
}
2 changes: 1 addition & 1 deletion src/Console/Command/ProjectionRebuildCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$middlewares
);

$console->caution('rebuild projections');
$console->warning('rebuild projections');
$console->progressStart($pipeline->count());

$pipeline->run(static function () use ($console): void {
Expand Down
70 changes: 70 additions & 0 deletions src/Console/DoctrineHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Console;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use InvalidArgumentException;

use function in_array;

class DoctrineHelper
{
public function databaseName(Connection $connection): string
{
/**
* @psalm-suppress InternalMethod
*/
$params = $connection->getParams();

if (isset($params['path'])) {
return $params['path'];
}

if (isset($params['dbname'])) {
return $params['dbname'];
}

throw new InvalidArgumentException(
"Connection does not contain a 'path' or 'dbname' parameter and cannot be created."
);
}

/**
* @codeCoverageIgnore
*/
public function copyConnectionWithoutDatabase(Connection $connection): Connection
{
/**
* @psalm-suppress InternalMethod
*/
$params = $connection->getParams();

/**
* @psalm-suppress InvalidArrayOffset
*/
unset($params['dbname'], $params['path'], $params['url']);

$tmpConnection = DriverManager::getConnection($params);
$tmpConnection->connect();

return $tmpConnection;
}

public function hasDatabase(Connection $connection, string $databaseName): bool
{
return in_array($databaseName, $connection->createSchemaManager()->listDatabases());
}

public function createDatabase(Connection $connection, string $databaseName): void
{
$connection->createSchemaManager()->createDatabase($databaseName);
}

public function dropDatabase(Connection $connection, string $databaseName): void
{
$connection->createSchemaManager()->dropDatabase($databaseName);
}
}
Loading

0 comments on commit 7ec9686

Please sign in to comment.