mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Change order to create initial database to avoid permission errors
This commit is contained in:
parent
cb4ba58b08
commit
4013ae87dd
@ -15,6 +15,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
use Symfony\Component\Lock\LockFactory;
|
use Symfony\Component\Lock\LockFactory;
|
||||||
use Symfony\Component\Process\PhpExecutableFinder;
|
use Symfony\Component\Process\PhpExecutableFinder;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
use function Functional\contains;
|
use function Functional\contains;
|
||||||
use function Functional\map;
|
use function Functional\map;
|
||||||
@ -53,9 +54,7 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand
|
|||||||
{
|
{
|
||||||
$io = new SymfonyStyle($input, $output);
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
|
||||||
$this->checkDbExists();
|
if ($this->databaseTablesExist()) {
|
||||||
|
|
||||||
if ($this->schemaExists()) {
|
|
||||||
$io->success('Database already exists. Run "db:migrate" command to make sure it is up to date.');
|
$io->success('Database already exists. Run "db:migrate" command to make sure it is up to date.');
|
||||||
return ExitCode::EXIT_SUCCESS;
|
return ExitCode::EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -68,30 +67,9 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand
|
|||||||
return ExitCode::EXIT_SUCCESS;
|
return ExitCode::EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkDbExists(): void
|
private function databaseTablesExist(): bool
|
||||||
{
|
{
|
||||||
if ($this->regularConn->getDriver()->getDatabasePlatform() instanceof SqlitePlatform) {
|
$existingTables = $this->ensureDatabaseExistsAndGetTables();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In order to create the new database, we have to use a connection where the dbname was not set.
|
|
||||||
// Otherwise, it will fail to connect and will not be able to create the new database
|
|
||||||
$schemaManager = $this->noDbNameConn->createSchemaManager();
|
|
||||||
$databases = $schemaManager->listDatabases();
|
|
||||||
// We cannot use getDatabase() to get the database name here, because then the driver will try to connect, and
|
|
||||||
// it does not exist yet. We need to read from the raw params instead.
|
|
||||||
$shlinkDatabase = $this->regularConn->getParams()['dbname'] ?? null;
|
|
||||||
|
|
||||||
if ($shlinkDatabase !== null && ! contains($databases, $shlinkDatabase)) {
|
|
||||||
$schemaManager->createDatabase($shlinkDatabase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function schemaExists(): bool
|
|
||||||
{
|
|
||||||
$schemaManager = $this->regularConn->createSchemaManager();
|
|
||||||
$existingTables = $schemaManager->listTableNames();
|
|
||||||
|
|
||||||
$allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
|
$allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
|
||||||
$shlinkTables = map($allMetadata, static fn (ClassMetadata $metadata) => $metadata->getTableName());
|
$shlinkTables = map($allMetadata, static fn (ClassMetadata $metadata) => $metadata->getTableName());
|
||||||
|
|
||||||
@ -99,4 +77,25 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand
|
|||||||
// Any other inconsistency will be taken care of by the migrations.
|
// Any other inconsistency will be taken care of by the migrations.
|
||||||
return some($shlinkTables, static fn (string $shlinkTable) => contains($existingTables, $shlinkTable));
|
return some($shlinkTables, static fn (string $shlinkTable) => contains($existingTables, $shlinkTable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function ensureDatabaseExistsAndGetTables(): array
|
||||||
|
{
|
||||||
|
if ($this->regularConn->getDriver()->getDatabasePlatform() instanceof SqlitePlatform) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Trying to list tables requires opening a connection to configured database.
|
||||||
|
// If it fails, it means it does not exist yet.
|
||||||
|
return $this->regularConn->createSchemaManager()->listTableNames();
|
||||||
|
} catch (Throwable) {
|
||||||
|
// We cannot use getDatabase() to get the database name here, because then the driver will try to connect.
|
||||||
|
// Instead, we read from the raw params.
|
||||||
|
$shlinkDatabase = $this->regularConn->getParams()['dbname'] ?? '';
|
||||||
|
// Create the database using a connection where the dbname was not set.
|
||||||
|
$this->noDbNameConn->createSchemaManager()->createDatabase($shlinkDatabase);
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ use Doctrine\DBAL\Schema\AbstractSchemaManager;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
use Doctrine\Persistence\Mapping\ClassMetadataFactory;
|
use Doctrine\Persistence\Mapping\ClassMetadataFactory;
|
||||||
|
use Exception;
|
||||||
use PHPUnit\Framework\Attributes\DataProvider;
|
use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
@ -69,17 +70,14 @@ class CreateDatabaseCommandTest extends TestCase
|
|||||||
#[Test]
|
#[Test]
|
||||||
public function successMessageIsPrintedIfDatabaseAlreadyExists(): void
|
public function successMessageIsPrintedIfDatabaseAlreadyExists(): void
|
||||||
{
|
{
|
||||||
$shlinkDatabase = 'shlink_database';
|
$this->regularConn->expects($this->never())->method('getParams');
|
||||||
$this->regularConn->expects($this->once())->method('getParams')->willReturn(['dbname' => $shlinkDatabase]);
|
$this->driver->method('getDatabasePlatform')->willReturn($this->createMock(AbstractPlatform::class));
|
||||||
|
|
||||||
$metadataMock = $this->createMock(ClassMetadata::class);
|
$metadataMock = $this->createMock(ClassMetadata::class);
|
||||||
$metadataMock->expects($this->once())->method('getTableName')->willReturn('foo_table');
|
$metadataMock->expects($this->once())->method('getTableName')->willReturn('foo_table');
|
||||||
$this->metadataFactory->method('getAllMetadata')->willReturn([$metadataMock]);
|
$this->metadataFactory->method('getAllMetadata')->willReturn([$metadataMock]);
|
||||||
$this->schemaManager->expects($this->once())->method('listDatabases')->willReturn(
|
|
||||||
['foo', $shlinkDatabase, 'bar'],
|
|
||||||
);
|
|
||||||
$this->schemaManager->expects($this->never())->method('createDatabase');
|
$this->schemaManager->expects($this->never())->method('createDatabase');
|
||||||
$this->schemaManager->expects($this->once())->method('listTableNames')->willReturn(['foo_table', 'bar_table']);
|
$this->schemaManager->expects($this->once())->method('listTableNames')->willReturn(['foo_table', 'bar_table']);
|
||||||
$this->driver->method('getDatabasePlatform')->willReturn($this->createMock(AbstractPlatform::class));
|
|
||||||
|
|
||||||
$this->commandTester->execute([]);
|
$this->commandTester->execute([]);
|
||||||
$output = $this->commandTester->getDisplay();
|
$output = $this->commandTester->getDisplay();
|
||||||
@ -90,15 +88,13 @@ class CreateDatabaseCommandTest extends TestCase
|
|||||||
#[Test]
|
#[Test]
|
||||||
public function databaseIsCreatedIfItDoesNotExist(): void
|
public function databaseIsCreatedIfItDoesNotExist(): void
|
||||||
{
|
{
|
||||||
|
$this->driver->method('getDatabasePlatform')->willReturn($this->createMock(AbstractPlatform::class));
|
||||||
|
|
||||||
$shlinkDatabase = 'shlink_database';
|
$shlinkDatabase = 'shlink_database';
|
||||||
$this->regularConn->expects($this->once())->method('getParams')->willReturn(['dbname' => $shlinkDatabase]);
|
$this->regularConn->expects($this->once())->method('getParams')->willReturn(['dbname' => $shlinkDatabase]);
|
||||||
$this->metadataFactory->method('getAllMetadata')->willReturn([]);
|
$this->metadataFactory->method('getAllMetadata')->willReturn([]);
|
||||||
$this->schemaManager->expects($this->once())->method('listDatabases')->willReturn(['foo', 'bar']);
|
|
||||||
$this->schemaManager->expects($this->once())->method('createDatabase')->with($shlinkDatabase);
|
$this->schemaManager->expects($this->once())->method('createDatabase')->with($shlinkDatabase);
|
||||||
$this->schemaManager->expects($this->once())->method('listTableNames')->willReturn(
|
$this->schemaManager->expects($this->once())->method('listTableNames')->willThrowException(new Exception(''));
|
||||||
['foo_table', 'bar_table'],
|
|
||||||
);
|
|
||||||
$this->driver->method('getDatabasePlatform')->willReturn($this->createMock(AbstractPlatform::class));
|
|
||||||
|
|
||||||
$this->commandTester->execute([]);
|
$this->commandTester->execute([]);
|
||||||
}
|
}
|
||||||
@ -106,14 +102,12 @@ class CreateDatabaseCommandTest extends TestCase
|
|||||||
#[Test, DataProvider('provideEmptyDatabase')]
|
#[Test, DataProvider('provideEmptyDatabase')]
|
||||||
public function tablesAreCreatedIfDatabaseIsEmpty(array $tables): void
|
public function tablesAreCreatedIfDatabaseIsEmpty(array $tables): void
|
||||||
{
|
{
|
||||||
$shlinkDatabase = 'shlink_database';
|
$this->regularConn->expects($this->never())->method('getParams');
|
||||||
$this->regularConn->expects($this->once())->method('getParams')->willReturn(['dbname' => $shlinkDatabase]);
|
$this->driver->method('getDatabasePlatform')->willReturn($this->createMock(AbstractPlatform::class));
|
||||||
|
|
||||||
$metadata = $this->createMock(ClassMetadata::class);
|
$metadata = $this->createMock(ClassMetadata::class);
|
||||||
$metadata->method('getTableName')->willReturn('shlink_table');
|
$metadata->method('getTableName')->willReturn('shlink_table');
|
||||||
$this->metadataFactory->method('getAllMetadata')->willReturn([$metadata]);
|
$this->metadataFactory->method('getAllMetadata')->willReturn([$metadata]);
|
||||||
$this->schemaManager->expects($this->once())->method('listDatabases')->willReturn(
|
|
||||||
['foo', $shlinkDatabase, 'bar'],
|
|
||||||
);
|
|
||||||
$this->schemaManager->expects($this->never())->method('createDatabase');
|
$this->schemaManager->expects($this->never())->method('createDatabase');
|
||||||
$this->schemaManager->expects($this->once())->method('listTableNames')->willReturn($tables);
|
$this->schemaManager->expects($this->once())->method('listTableNames')->willReturn($tables);
|
||||||
$this->processHelper->expects($this->once())->method('run')->with($this->isInstanceOf(OutputInterface::class), [
|
$this->processHelper->expects($this->once())->method('run')->with($this->isInstanceOf(OutputInterface::class), [
|
||||||
@ -122,7 +116,6 @@ class CreateDatabaseCommandTest extends TestCase
|
|||||||
CreateDatabaseCommand::DOCTRINE_CREATE_SCHEMA_COMMAND,
|
CreateDatabaseCommand::DOCTRINE_CREATE_SCHEMA_COMMAND,
|
||||||
'--no-interaction',
|
'--no-interaction',
|
||||||
]);
|
]);
|
||||||
$this->driver->method('getDatabasePlatform')->willReturn($this->createMock(AbstractPlatform::class));
|
|
||||||
|
|
||||||
$this->commandTester->execute([]);
|
$this->commandTester->execute([]);
|
||||||
$output = $this->commandTester->getDisplay();
|
$output = $this->commandTester->getDisplay();
|
||||||
@ -141,12 +134,12 @@ class CreateDatabaseCommandTest extends TestCase
|
|||||||
public function databaseCheckIsSkippedForSqlite(): void
|
public function databaseCheckIsSkippedForSqlite(): void
|
||||||
{
|
{
|
||||||
$this->driver->method('getDatabasePlatform')->willReturn($this->createMock(SqlitePlatform::class));
|
$this->driver->method('getDatabasePlatform')->willReturn($this->createMock(SqlitePlatform::class));
|
||||||
|
|
||||||
$this->regularConn->expects($this->never())->method('getParams');
|
$this->regularConn->expects($this->never())->method('getParams');
|
||||||
$this->metadataFactory->expects($this->once())->method('getAllMetadata')->willReturn([]);
|
|
||||||
$this->schemaManager->expects($this->never())->method('listDatabases');
|
$this->schemaManager->expects($this->never())->method('listDatabases');
|
||||||
$this->schemaManager->expects($this->never())->method('createDatabase');
|
$this->schemaManager->expects($this->never())->method('createDatabase');
|
||||||
$this->schemaManager->expects($this->once())->method('listTableNames')->willReturn(['foo_table', 'bar_table']);
|
$this->schemaManager->expects($this->never())->method('listTableNames');
|
||||||
|
|
||||||
|
$this->metadataFactory->expects($this->once())->method('getAllMetadata')->willReturn([]);
|
||||||
|
|
||||||
$this->commandTester->execute([]);
|
$this->commandTester->execute([]);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user