1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 10:54:04 +00:00

Make database creation queueable (#17)

* SOLIDify database creation

* Return the result of fclose in SQLiteDBCreator

* Make database creation queueable

* Add DatabaseCreationTest

* Make comment more descriptive [ci skip]

* Add composer script to copy .env.example to .env

* Minor tweaks

* Fix env section

* Fix DB_PASSWORD for Travis
This commit is contained in:
Samuel Štancl 2019-02-07 18:12:24 +01:00 committed by GitHub
parent 136862369d
commit c53a05cff4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 186 additions and 24 deletions

3
.env.example Normal file
View file

@ -0,0 +1,3 @@
# DB_DATABASE=travis_tenancy
# DB_USERNAME=foo
# DB_PASSWORD=bar

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
.env
composer.lock composer.lock
vendor/ vendor/

View file

@ -1,3 +1,6 @@
env:
- DB_USERNAME=root DB_PASSWORD="" DB_DATABASE=travis_tenancy CODECOV_TOKEN="24382d15-84e7-4a55-bea4-c4df96a24a9b"
language: php language: php
php: php:
- '7.2' - '7.2'
@ -8,6 +11,7 @@ branches:
- master - master
services: services:
- mysql
- redis-server - redis-server
before_install: before_install:
@ -16,8 +20,10 @@ before_install:
install: install:
- travis_retry composer install --no-interaction - travis_retry composer install --no-interaction
script: vendor/bin/phpunit --coverage-clover=coverage.xml before_script:
- mysql -e 'CREATE DATABASE travis_tenancy;'
script: vendor/bin/phpunit -v --coverage-clover=coverage.xml
after_success: after_success:
- export CODECOV_TOKEN="24382d15-84e7-4a55-bea4-c4df96a24a9b"
- bash <(curl -s https://codecov.io/bash) - bash <(curl -s https://codecov.io/bash)

View file

@ -15,7 +15,8 @@
}, },
"require-dev": { "require-dev": {
"orchestra/testbench": "~3.0", "orchestra/testbench": "~3.0",
"laravel/framework": "5.7.*" "laravel/framework": "5.7.*",
"vlucas/phpdotenv": "^2.2"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View file

@ -29,6 +29,5 @@
<env name="QUEUE_CONNECTION" value="sync"/> <env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/> <env name="SESSION_DRIVER" value="array"/>
<env name="DB_CONNECTION" value="sqlite"/> <env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
</php> </php>
</phpunit> </phpunit>

View file

@ -0,0 +1,14 @@
<?php
namespace Stancl\Tenancy\DatabaseCreators;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Interfaces\DatabaseCreator;
class MySQLDatabaseCreator implements DatabaseCreator
{
public function createDatabase(string $name): bool
{
return DB::statement("CREATE DATABASE `$name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Stancl\Tenancy\DatabaseCreators;
use Stancl\Tenancy\Interfaces\DatabaseCreator;
class SQLiteDatabaseCreator implements DatabaseCreator
{
public function createDatabase(string $name): bool
{
return fclose(fopen(database_path($name), 'w'));
}
}

View file

@ -3,6 +3,7 @@
namespace Stancl\Tenancy; namespace Stancl\Tenancy;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Jobs\QueuedDatabaseCreator;
use Illuminate\Database\DatabaseManager as BaseDatabaseManager; use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
class DatabaseManager class DatabaseManager
@ -34,14 +35,18 @@ class DatabaseManager
{ {
$this->createTenantConnection($name); $this->createTenantConnection($name);
$driver = $driver ?: $this->getDriver(); $driver = $driver ?: $this->getDriver();
if ($driver === "sqlite") {
$f = fopen(database_path($name), 'w'); $databaseCreators = config('tenancy.database_creators');
fclose($f);
if (! array_key_exists($driver, $databaseCreators)) {
return; throw new \Exception("Database could not be created: no database creator for driver $driver is registered.");
} }
return DB::statement("CREATE DATABASE `$name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); if (config('tenancy.queue_database_creation', false)) {
QueuedDatabaseCreator::dispatch(app($databaseCreators[$driver]), $name);
} else {
app($databaseCreators[$driver])->createDatabase($name);
}
} }
public function delete() public function delete()

View file

@ -0,0 +1,14 @@
<?php
namespace Stancl\Tenancy\Interfaces;
interface DatabaseCreator
{
/**
* Create a database.
*
* @param string $name Name of the database.
* @return void
*/
public function createDatabase(string $name): bool;
}

View file

@ -0,0 +1,38 @@
<?php
namespace Stancl\Tenancy\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Stancl\Tenancy\Interfaces\DatabaseCreator;
class QueuedDatabaseCreator implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @param DatabaseCreator $databaseCreator
* @param string $databaseName
* @return void
*/
public function __construct(DatabaseCreator $databaseCreator, string $databaseName)
{
$this->databaseCreator = $databaseCreator;
$this->databaseName = $databaseName;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$this->databaseCreator->createDatabase($databaseName);
}
}

View file

@ -21,7 +21,6 @@ return [
'cache' => [ 'cache' => [
'prefix_base' => 'tenant', 'prefix_base' => 'tenant',
], ],
'filesystem' => [ 'filesystem' => [
'suffix_base' => 'tenant', 'suffix_base' => 'tenant',
// Disks which should be suffixed with the suffix_base + tenant UUID. // Disks which should be suffixed with the suffix_base + tenant UUID.
@ -30,4 +29,9 @@ return [
// 's3', // 's3',
], ],
], ],
'database_creators' => [
'sqlite' => 'Stancl\Tenancy\DatabaseCreators\SQLiteDatabaseCreator',
'mysql' => 'Stancl\Tenancy\DatabaseCreators\MySQLDatabaseCreator',
],
'queue_database_creation' => false,
]; ];

View file

@ -0,0 +1,45 @@
<?php
namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\DatabaseManager;
use Illuminate\Support\Facades\Queue;
use Stancl\Tenancy\Jobs\QueuedDatabaseCreator;
class DatabaseCreationTest extends TestCase
{
/** @test */
public function sqlite_database_is_created()
{
$db_name = 'testdatabase' . $this->randomString(10) . '.sqlite';
app(DatabaseManager::class)->create($db_name, 'sqlite');
$this->assertFileExists(database_path($db_name));
}
/** @test */
public function mysql_database_is_created()
{
if (! $this->isTravis()) {
$this->markTestSkipped('As to not bloat your MySQL instance with test databases, this test is not run by default.');
}
config()->set('database.default', 'mysql');
$db_name = 'testdatabase' . $this->randomString(10);
app(DatabaseManager::class)->create($db_name, 'mysql');
$this->assertNotEmpty(DB::select("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$db_name'"));
}
/** @test */
public function database_creation_can_be_queued()
{
Queue::fake();
config()->set('tenancy.queue_database_creation', true);
$db_name = 'testdatabase' . $this->randomString(10) . '.sqlite';
app(DatabaseManager::class)->create($db_name, 'sqlite');
Queue::assertPushed(QueuedDatabaseCreator::class);
}
}

View file

@ -30,19 +30,26 @@ class TestCase extends \Orchestra\Testbench\TestCase
*/ */
protected function getEnvironmentSetUp($app) protected function getEnvironmentSetUp($app)
{ {
$app['config']->set('database.redis.client', 'phpredis'); if (file_exists(__DIR__ . '/../.env')) {
$app['config']->set('database.redis.tenancy', [ (new \Dotenv\Dotenv(__DIR__ . '/..'))->load();
'host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'), }
'password' => env('TENANCY_TEST_REDIS_PASSWORD', null),
'port' => env('TENANCY_TEST_REDIS_PORT', 6379), $app['config']->set([
// Use the #14 Redis database unless specified otherwise. 'database.redis.client' => 'phpredis',
// Make sure you don't store anything in this db! 'database.redis.tenancy' => [
'database' => env('TENANCY_TEST_REDIS_DB', 14), 'host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'),
]); 'password' => env('TENANCY_TEST_REDIS_PASSWORD', null),
$app['config']->set('tenancy.database', [ 'port' => env('TENANCY_TEST_REDIS_PORT', 6379),
'based_on' => 'sqlite', // Use the #14 Redis database unless specified otherwise.
'prefix' => 'tenant', // Make sure you don't store anything in this db!
'suffix' => '.sqlite', 'database' => env('TENANCY_TEST_REDIS_DB', 14),
],
'tenancy.database' => [
'based_on' => 'sqlite',
'prefix' => 'tenant',
'suffix' => '.sqlite',
],
'database.connections.sqlite.database' => ':memory:',
]); ]);
} }
@ -61,4 +68,16 @@ class TestCase extends \Orchestra\Testbench\TestCase
{ {
$app->singleton('Illuminate\Contracts\Http\Kernel', HttpKernel::class); $app->singleton('Illuminate\Contracts\Http\Kernel', HttpKernel::class);
} }
public function randomString(int $length = 10)
{
return substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length);
}
public function isTravis()
{
// Multiple, just to make sure. Someone might accidentally
// set one of these environment vars on their computer.
return env('CI') && env('TRAVIS') && env('CONTINUOUS_INTEGRATION');
}
} }