diff --git a/src/Commands/Migrate.php b/src/Commands/Migrate.php index bf92dfcd9..c67d35981 100644 --- a/src/Commands/Migrate.php +++ b/src/Commands/Migrate.php @@ -8,32 +8,26 @@ use Illuminate\Database\Console\Migrations\MigrateCommand; use Illuminate\Database\Migrations\Migrator; use Stancl\Tenancy\Concerns\DealsWithMigrations; +use Stancl\Tenancy\Concerns\ExtendsLaravelCommand; use Stancl\Tenancy\Concerns\HasATenantsOption; use Stancl\Tenancy\Events\DatabaseMigrated; use Stancl\Tenancy\Events\MigratingDatabase; class Migrate extends MigrateCommand { - use HasATenantsOption, DealsWithMigrations; + use HasATenantsOption, DealsWithMigrations, ExtendsLaravelCommand; - /** - * The console command description. - * - * @var string - */ protected $description = 'Run migrations for tenant(s)'; - /** - * Create a new command instance. - * - * @param Migrator $migrator - * @param Dispatcher $dispatcher - */ + protected static function getTenantCommandName(): string + { + return 'tenants:migrate'; + } + public function __construct(Migrator $migrator, Dispatcher $dispatcher) { parent::__construct($migrator, $dispatcher); - $this->setName('tenants:migrate'); $this->specifyParameters(); } diff --git a/src/Commands/MigrateFresh.php b/src/Commands/MigrateFresh.php index 4d003db01..283d70b0f 100644 --- a/src/Commands/MigrateFresh.php +++ b/src/Commands/MigrateFresh.php @@ -23,7 +23,7 @@ final class MigrateFresh extends Command public function __construct() { parent::__construct(); - + $this->addOption('--drop-views', null, InputOption::VALUE_NONE, 'Drop views along with tenant tables.', null); $this->setName('tenants:migrate-fresh'); diff --git a/src/Commands/Rollback.php b/src/Commands/Rollback.php index 081872c86..e60d974bb 100644 --- a/src/Commands/Rollback.php +++ b/src/Commands/Rollback.php @@ -7,13 +7,19 @@ use Illuminate\Database\Console\Migrations\RollbackCommand; use Illuminate\Database\Migrations\Migrator; use Stancl\Tenancy\Concerns\DealsWithMigrations; +use Stancl\Tenancy\Concerns\ExtendsLaravelCommand; use Stancl\Tenancy\Concerns\HasATenantsOption; use Stancl\Tenancy\Events\DatabaseRolledBack; use Stancl\Tenancy\Events\RollingBackDatabase; class Rollback extends RollbackCommand { - use HasATenantsOption, DealsWithMigrations; + use HasATenantsOption, DealsWithMigrations, ExtendsLaravelCommand; + + protected static function getTenantCommandName(): string + { + return 'tenants:rollback'; + } /** * The console command description. @@ -31,8 +37,7 @@ public function __construct(Migrator $migrator) { parent::__construct($migrator); - $this->setName('tenants:rollback'); - $this->specifyParameters(); + $this->specifyTenantSignature(); } /** diff --git a/src/Concerns/ExtendsLaravelCommand.php b/src/Concerns/ExtendsLaravelCommand.php new file mode 100644 index 000000000..bdafc8f75 --- /dev/null +++ b/src/Concerns/ExtendsLaravelCommand.php @@ -0,0 +1,23 @@ +specifyParameters(); + } + + public function getName(): ?string + { + return static::getTenantCommandName(); + } + + public static function getDefaultName(): ?string + { + return static::getTenantCommandName(); + } + + abstract protected static function getTenantCommandName(): string; +} diff --git a/src/Database/DatabaseManager.php b/src/Database/DatabaseManager.php index e85fd6596..6242ffa9f 100644 --- a/src/Database/DatabaseManager.php +++ b/src/Database/DatabaseManager.php @@ -7,10 +7,12 @@ use Illuminate\Config\Repository; use Illuminate\Contracts\Foundation\Application; use Illuminate\Database\DatabaseManager as BaseDatabaseManager; +use Stancl\Tenancy\Contracts\ManagesDatabaseUsers; use Stancl\Tenancy\Contracts\TenantCannotBeCreatedException; use Stancl\Tenancy\Contracts\TenantWithDatabase; use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException; use Stancl\Tenancy\Exceptions\TenantDatabaseAlreadyExistsException; +use Stancl\Tenancy\Exceptions\TenantDatabaseUserAlreadyExistsException; /** * @internal Class is subject to breaking changes in minor and patch versions. @@ -90,8 +92,14 @@ public function purgeTenantConnection() */ public function ensureTenantCanBeCreated(TenantWithDatabase $tenant): void { - if ($tenant->database()->manager()->databaseExists($database = $tenant->database()->getName())) { + $manager = $tenant->database()->manager(); + + if ($manager->databaseExists($database = $tenant->database()->getName())) { throw new TenantDatabaseAlreadyExistsException($database); } + + if ($manager instanceof ManagesDatabaseUsers && $manager->userExists($username = $tenant->database()->getUsername())) { + throw new TenantDatabaseUserAlreadyExistsException($username); + } } } diff --git a/src/Jobs/CreateDatabase.php b/src/Jobs/CreateDatabase.php index 3a74534d2..3cb2a6b44 100644 --- a/src/Jobs/CreateDatabase.php +++ b/src/Jobs/CreateDatabase.php @@ -36,8 +36,8 @@ public function handle(DatabaseManager $databaseManager) return false; } - $databaseManager->ensureTenantCanBeCreated($this->tenant); $this->tenant->database()->makeCredentials(); + $databaseManager->ensureTenantCanBeCreated($this->tenant); $this->tenant->database()->manager()->createDatabase($this->tenant); event(new DatabaseCreated($this->tenant)); diff --git a/src/TenantDatabaseManagers/PermissionControlledMySQLDatabaseManager.php b/src/TenantDatabaseManagers/PermissionControlledMySQLDatabaseManager.php index f8bedc977..918601a8a 100644 --- a/src/TenantDatabaseManagers/PermissionControlledMySQLDatabaseManager.php +++ b/src/TenantDatabaseManagers/PermissionControlledMySQLDatabaseManager.php @@ -7,7 +7,6 @@ use Stancl\Tenancy\Concerns\CreatesDatabaseUsers; use Stancl\Tenancy\Contracts\ManagesDatabaseUsers; use Stancl\Tenancy\DatabaseConfig; -use Stancl\Tenancy\Exceptions\TenantDatabaseUserAlreadyExistsException; class PermissionControlledMySQLDatabaseManager extends MySQLDatabaseManager implements ManagesDatabaseUsers { @@ -26,10 +25,6 @@ public function createUser(DatabaseConfig $databaseConfig): bool $hostname = $databaseConfig->connection()['host']; $password = $databaseConfig->getPassword(); - if ($this->userExists($username)) { - throw new TenantDatabaseUserAlreadyExistsException($username); - } - $this->database()->statement("CREATE USER `{$username}`@`%` IDENTIFIED BY '{$password}'"); $grants = implode(', ', static::$grants); diff --git a/tests/DatabaseUsersTest.php b/tests/DatabaseUsersTest.php index 0b095024b..344239d16 100644 --- a/tests/DatabaseUsersTest.php +++ b/tests/DatabaseUsersTest.php @@ -10,6 +10,7 @@ use Stancl\JobPipeline\JobPipeline; use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper; use Stancl\Tenancy\Contracts\ManagesDatabaseUsers; +use Stancl\Tenancy\Events\DatabaseCreated; use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Exceptions\TenantDatabaseUserAlreadyExistsException; @@ -67,14 +68,18 @@ public function a_tenants_database_cannot_be_created_when_the_user_already_exist $this->assertTrue($manager->databaseExists($tenant->database()->getName())); $this->expectException(TenantDatabaseUserAlreadyExistsException::class); + Event::fake([DatabaseCreated::class]); + $tenant2 = Tenant::create([ 'tenancy_db_username' => $username, ]); /** @var ManagesDatabaseUsers $manager */ - $manager = $tenant2->database()->manager(); + $manager2 = $tenant2->database()->manager(); + // database was not created because of DB transaction - $this->assertFalse($manager->databaseExists($tenant2->database()->getName())); + $this->assertFalse($manager2->databaseExists($tenant2->database()->getName())); + Event::assertNotDispatched(DatabaseCreated::class); } /** @test */ diff --git a/tests/Etc/tmp/queuetest.json b/tests/Etc/tmp/queuetest.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/QueueTest.php b/tests/QueueTest.php index afe64feab..a3df9cd7b 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -4,6 +4,7 @@ namespace Stancl\Tenancy\Tests; +use Closure; use Exception; use Illuminate\Support\Str; use Illuminate\Bus\Queueable; @@ -24,6 +25,7 @@ use Illuminate\Queue\Events\JobProcessing; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use PDO; use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\RevertToCentralContext; @@ -52,7 +54,7 @@ public function setUp(): void Event::listen(TenancyInitialized::class, BootstrapTenancy::class); Event::listen(TenancyEnded::class, RevertToCentralContext::class); - $this->valuestore = Valuestore::make(__DIR__ . '/Etc/tmp/queuetest.json')->flush(); + $this->createValueStore(); } public function tearDown(): void @@ -60,6 +62,22 @@ public function tearDown(): void $this->valuestore->flush(); } + protected function createValueStore(): void + { + $valueStorePath = __DIR__ . '/Etc/tmp/queuetest.json'; + + if (! file_exists($valueStorePath)) { + // The directory sometimes goes missing as well when the file is deleted in git + if (! is_dir(__DIR__ . '/Etc/tmp')) { + mkdir(__DIR__ . '/Etc/tmp'); + } + + file_put_contents($valueStorePath, ''); + } + + $this->valuestore = Valuestore::make($valueStorePath)->flush(); + } + protected function withFailedJobs() { Schema::connection('central')->create('failed_jobs', function (Blueprint $table) {