1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 12:14:02 +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
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
php:
- '7.2'
@ -8,6 +11,7 @@ branches:
- master
services:
- mysql
- redis-server
before_install:
@ -16,8 +20,10 @@ before_install:
install:
- 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:
- export CODECOV_TOKEN="24382d15-84e7-4a55-bea4-c4df96a24a9b"
- bash <(curl -s https://codecov.io/bash)

View file

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

View file

@ -29,6 +29,5 @@
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
</php>
</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;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Jobs\QueuedDatabaseCreator;
use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
class DatabaseManager
@ -34,14 +35,18 @@ class DatabaseManager
{
$this->createTenantConnection($name);
$driver = $driver ?: $this->getDriver();
if ($driver === "sqlite") {
$f = fopen(database_path($name), 'w');
fclose($f);
return;
$databaseCreators = config('tenancy.database_creators');
if (! array_key_exists($driver, $databaseCreators)) {
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()

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' => [
'prefix_base' => 'tenant',
],
'filesystem' => [
'suffix_base' => 'tenant',
// Disks which should be suffixed with the suffix_base + tenant UUID.
@ -30,4 +29,9 @@ return [
// '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)
{
$app['config']->set('database.redis.client', 'phpredis');
$app['config']->set('database.redis.tenancy', [
if (file_exists(__DIR__ . '/../.env')) {
(new \Dotenv\Dotenv(__DIR__ . '/..'))->load();
}
$app['config']->set([
'database.redis.client' => 'phpredis',
'database.redis.tenancy' => [
'host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'),
'password' => env('TENANCY_TEST_REDIS_PASSWORD', null),
'port' => env('TENANCY_TEST_REDIS_PORT', 6379),
// Use the #14 Redis database unless specified otherwise.
// Make sure you don't store anything in this db!
'database' => env('TENANCY_TEST_REDIS_DB', 14),
]);
$app['config']->set('tenancy.database', [
],
'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);
}
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');
}
}