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

Change default tenant model, write more tests, cleanup

This commit is contained in:
Samuel Štancl 2020-05-13 06:23:41 +02:00
parent c32f229dd5
commit de53b81c0e
33 changed files with 210 additions and 90 deletions

View file

@ -2,11 +2,12 @@
namespace App\Providers; namespace App\Providers;
use Stancl\Tenancy\Contracts\Tenant;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Events\DatabaseCreated; use Stancl\Tenancy\Events\DatabaseCreated;
use Stancl\Tenancy\Events\DatabaseDeleted; use Stancl\Tenancy\Events\DatabaseDeleted;
use Stancl\Tenancy\Events\DatabaseMigrated; use Stancl\Tenancy\Events\DatabaseMigrated;
@ -20,7 +21,6 @@ use Stancl\Tenancy\Events\RevertedToCentralContext;
use Stancl\Tenancy\Events\TenancyBootstrapped; use Stancl\Tenancy\Events\TenancyBootstrapped;
use Stancl\Tenancy\Events\TenancyEnded; use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Events\TenantDeleted; use Stancl\Tenancy\Events\TenantDeleted;
use Stancl\Tenancy\Events\TenantSaved; use Stancl\Tenancy\Events\TenantSaved;
@ -29,9 +29,6 @@ use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Jobs\DeleteDatabase; use Stancl\Tenancy\Jobs\DeleteDatabase;
use Stancl\Tenancy\Jobs\MigrateDatabase; use Stancl\Tenancy\Jobs\MigrateDatabase;
use Stancl\Tenancy\Jobs\SeedDatabase; use Stancl\Tenancy\Jobs\SeedDatabase;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Tenancy;
class TenancyServiceProvider extends ServiceProvider class TenancyServiceProvider extends ServiceProvider
{ {
@ -49,7 +46,7 @@ class TenancyServiceProvider extends ServiceProvider
])->send(function (TenantCreated $event) { ])->send(function (TenantCreated $event) {
return $event->tenant; return $event->tenant;
})->queue(false), // `false` by default, but you probably want to make this `true` for production. })->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production.
], ],
TenantSaved::class => [], TenantSaved::class => [],
TenantUpdated::class => [], TenantUpdated::class => [],
@ -58,7 +55,7 @@ class TenancyServiceProvider extends ServiceProvider
DeleteDatabase::class, DeleteDatabase::class,
])->send(function (TenantDeleted $event) { ])->send(function (TenantDeleted $event) {
return $event->tenant; return $event->tenant;
})->queue(false), // `false` by default, but you probably want to make this `true` for production. })->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production.
], ],
DomainCreated::class => [], DomainCreated::class => [],
@ -87,30 +84,7 @@ class TenancyServiceProvider extends ServiceProvider
public function register() public function register()
{ {
// Make sure Tenancy is stateful. //
$this->app->singleton(Tenancy::class);
// Make sure features are bootstrapped as soon as Tenancy is instantiated.
$this->app->extend(Tenancy::class, function (Tenancy $tenancy) {
foreach ($this->app['config']['tenancy.features'] as $feature) {
$this->app[$feature]->bootstrap($tenancy);
}
return $tenancy;
});
// Make it possible to inject the current tenant by typehinting the Tenant contract.
$this->app->bind(Tenant::class, function ($app) {
return $app[Tenancy::class]->tenant;
});
// Make sure bootstrappers are stateful (singletons).
foreach ($this->app['config']['tenancy.bootstrappers'] as $bootstrapper) {
$this->app->singleton($bootstrapper);
}
// Bind the class in the tenancy.id_generator config to the UniqueIdentifierGenerator abstract.
$this->app->bind(UniqueIdentifierGenerator::class, $this->app['config']['tenancy.id_generator']);
} }
public function boot() public function boot()
@ -138,8 +112,7 @@ class TenancyServiceProvider extends ServiceProvider
{ {
$this->app->booted(function () { $this->app->booted(function () {
if (file_exists(base_path('routes/tenant.php'))) { if (file_exists(base_path('routes/tenant.php'))) {
Route::middleware(['web']) Route::namespace($this->app['config']['tenancy.tenant_route_namespace'] ?? 'App\Http\Controllers')
->namespace($this->app['config']['tenancy.tenant_route_namespace'] ?? 'App\Http\Controllers')
->group(base_path('routes/tenant.php')); ->group(base_path('routes/tenant.php'));
} }
}); });

View file

@ -1,5 +1,7 @@
<?php <?php
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Tenant Routes | Tenant Routes
@ -14,7 +16,7 @@
*/ */
Route::group([ Route::group([
'middleware' => InitializeTenancyByDomain::class, 'middleware' => ['web', InitializeTenancyByDomain::class],
'prefix' => '/app', 'prefix' => '/app',
], function () { ], function () {
Route::get('/', function () { Route::get('/', function () {

View file

@ -43,7 +43,11 @@ class Install extends Command
$this->info('Found routes/tenant.php.'); $this->info('Found routes/tenant.php.');
} }
// todo tenancy SP stub $this->callSilent('vendor:publish', [
'--provider' => 'Stancl\Tenancy\TenancyServiceProvider',
'--tag' => 'provider',
]);
$this->info('✔️ Created TenancyServiceProvider.php');
$this->callSilent('vendor:publish', [ $this->callSilent('vendor:publish', [
'--provider' => 'Stancl\Tenancy\TenancyServiceProvider', '--provider' => 'Stancl\Tenancy\TenancyServiceProvider',

View file

@ -2,7 +2,7 @@
namespace Stancl\Tenancy\Database\Concerns; namespace Stancl\Tenancy\Database\Concerns;
trait HasADataColumn trait HasDataColumn
{ {
public static $priorityListeners = []; public static $priorityListeners = [];
@ -13,9 +13,9 @@ trait HasADataColumn
* *
* @var string * @var string
*/ */
public $dataEncodingStatus = 'decoded'; // todo write tests for this public $dataEncodingStatus = 'decoded';
public static function bootHasADataColumn() public static function bootHasDataColumn()
{ {
$encode = function (self $model) { $encode = function (self $model) {
if ($model->dataEncodingStatus === 'encoded') { if ($model->dataEncodingStatus === 'encoded') {

View file

@ -0,0 +1,16 @@
<?php
namespace Stancl\Tenancy\Database\Concerns;
use Stancl\Tenancy\Contracts\TenantWithDatabase;
use Stancl\Tenancy\DatabaseConfig;
trait HasDatabase
{
public function database(): DatabaseConfig
{
/** @var TenantWithDatabase $this */
return new DatabaseConfig($this);
}
}

View file

@ -4,7 +4,6 @@ namespace Stancl\Tenancy\Database\Models;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Stancl\Tenancy\DatabaseConfig;
use Stancl\Tenancy\Events; use Stancl\Tenancy\Events;
use Stancl\Tenancy\Contracts; use Stancl\Tenancy\Contracts;
use Stancl\Tenancy\Database\Concerns; use Stancl\Tenancy\Database\Concerns;
@ -15,14 +14,13 @@ use Stancl\Tenancy\Database\Concerns;
* @property Carbon $updated_at * @property Carbon $updated_at
* @property array $data * @property array $data
*/ */
class Tenant extends Model implements Contracts\TenantWithDatabase // todo base model that isn't TenantWithDatabase & domains class Tenant extends Model implements Contracts\Tenant
{ {
use Concerns\CentralConnection, use Concerns\CentralConnection,
Concerns\HasADataColumn, Concerns\HasDataColumn,
Concerns\GeneratesIds, Concerns\GeneratesIds,
Concerns\HasADataColumn, Concerns\HasDataColumn {
Concerns\HasDomains { Concerns\HasDataColumn::getCasts as dataColumnCasts;
Concerns\HasADataColumn::getCasts as dataColumnCasts;
} }
protected $table = 'tenants'; protected $table = 'tenants';
@ -69,11 +67,6 @@ class Tenant extends Model implements Contracts\TenantWithDatabase // todo base
return $this; return $this;
} }
public function database(): DatabaseConfig
{
return new DatabaseConfig($this);
}
public function run(callable $callback) public function run(callable $callback)
{ {
$originalTenant = tenant(); $originalTenant = tenant();

View file

@ -4,16 +4,17 @@ declare(strict_types=1);
namespace Stancl\Tenancy; namespace Stancl\Tenancy;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers; use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException; use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException;
use Stancl\Tenancy\Contracts\TenantWithDatabase as Tenant;
class DatabaseConfig class DatabaseConfig
{ {
/** @var Tenant */ /** @var Tenant|Model */
public $tenant; public $tenant;
/** @var callable */ /** @var callable */
@ -90,6 +91,10 @@ class DatabaseConfig
$this->tenant->setInternal('db_username', $this->getUsername() ?? (static::$usernameGenerator)($this->tenant)); $this->tenant->setInternal('db_username', $this->getUsername() ?? (static::$usernameGenerator)($this->tenant));
$this->tenant->setInternal('db_password', $this->getPassword() ?? (static::$passwordGenerator)($this->tenant)); $this->tenant->setInternal('db_password', $this->getPassword() ?? (static::$passwordGenerator)($this->tenant));
} }
if ($this->tenant->exists) {
$this->tenant->save();
}
} }
public function getTemplateConnectionName(): string public function getTemplateConnectionName(): string

View file

@ -83,7 +83,7 @@ class DatabaseManager
* @throws DatabaseManagerNotRegisteredException * @throws DatabaseManagerNotRegisteredException
* @throws TenantDatabaseAlreadyExistsException * @throws TenantDatabaseAlreadyExistsException
*/ */
public function ensureTenantCanBeCreated(TenantWithDatabase $tenant): void // todo do we need this? public function ensureTenantCanBeCreated(TenantWithDatabase $tenant): void
{ {
if ($tenant->database()->manager()->databaseExists($database = $tenant->database()->getName())) { if ($tenant->database()->manager()->databaseExists($database = $tenant->database()->getName())) {
throw new TenantDatabaseAlreadyExistsException($database); throw new TenantDatabaseAlreadyExistsException($database);

View file

@ -12,6 +12,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Stancl\Tenancy\Contracts\TenantWithDatabase; use Stancl\Tenancy\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Contracts\Tenant; use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\DatabaseManager;
use Stancl\Tenancy\Events\DatabaseCreated; use Stancl\Tenancy\Events\DatabaseCreated;
class CreateDatabase implements ShouldQueue class CreateDatabase implements ShouldQueue
@ -26,9 +27,10 @@ class CreateDatabase implements ShouldQueue
$this->tenant = $tenant; $this->tenant = $tenant;
} }
public function handle() public function handle(DatabaseManager $databaseManager)
{ {
if ($this->tenant->getInternal('create_database') !== false) { if ($this->tenant->getInternal('create_database') !== false) {
$databaseManager->ensureTenantCanBeCreated($this->tenant);
$this->tenant->database()->makeCredentials(); $this->tenant->database()->makeCredentials();
$this->tenant->database()->manager()->createDatabase($this->tenant); $this->tenant->database()->manager()->createDatabase($this->tenant);

View file

@ -8,7 +8,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
class JobPipeline implements ShouldQueue class JobPipeline implements ShouldQueue
{ {
/** @var bool */ /** @var bool */
public static $queueByDefault = false; public static $shouldBeQueuedByDefault = false;
/** @var callable[]|string[] */ /** @var callable[]|string[] */
public $jobs; public $jobs;
@ -22,16 +22,16 @@ class JobPipeline implements ShouldQueue
public $passable; public $passable;
/** @var bool */ /** @var bool */
public $queue; public $shouldBeQueued;
public function __construct($jobs, callable $send = null, bool $queue = null) public function __construct($jobs, callable $send = null, bool $shouldBeQueued = null)
{ {
$this->jobs = $jobs; $this->jobs = $jobs;
$this->send = $send ?? function ($event) { $this->send = $send ?? function ($event) {
// If no $send callback is set, we'll just pass the event through the jobs. // If no $send callback is set, we'll just pass the event through the jobs.
return $event; return $event;
}; };
$this->queue = $queue ?? static::$queueByDefault; $this->shouldBeQueued = $shouldBeQueued ?? static::$shouldBeQueuedByDefault;
} }
/** @param callable[]|string[] $jobs */ /** @param callable[]|string[] $jobs */
@ -47,9 +47,9 @@ class JobPipeline implements ShouldQueue
return $this; return $this;
} }
public function queue(bool $queue) public function shouldBeQueued(bool $shouldBeQueued)
{ {
$this->queue = $queue; $this->shouldBeQueued = $shouldBeQueued;
return $this; return $this;
} }
@ -69,7 +69,7 @@ class JobPipeline implements ShouldQueue
return function (...$args) { return function (...$args) {
$executable = $this->executable($args); $executable = $this->executable($args);
if ($this->queue) { if ($this->shouldBeQueued) {
dispatch($executable); dispatch($executable);
} else { } else {
dispatch_now($executable); dispatch_now($executable);

View file

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Stancl\Tenancy; namespace Stancl\Tenancy;
use Illuminate\Cache\CacheManager; use Illuminate\Cache\CacheManager;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper; use Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper;
use Stancl\Tenancy\Contracts\Tenant; use Stancl\Tenancy\Contracts\Tenant;
@ -23,6 +22,31 @@ class TenancyServiceProvider extends ServiceProvider
$this->app->singleton(DatabaseManager::class); $this->app->singleton(DatabaseManager::class);
// Make sure Tenancy is stateful.
$this->app->singleton(Tenancy::class);
// Make sure features are bootstrapped as soon as Tenancy is instantiated.
$this->app->extend(Tenancy::class, function (Tenancy $tenancy) {
foreach ($this->app['config']['tenancy.features'] as $feature) {
$this->app[$feature]->bootstrap($tenancy);
}
return $tenancy;
});
// Make it possible to inject the current tenant by typehinting the Tenant contract.
$this->app->bind(Tenant::class, function ($app) {
return $app[Tenancy::class]->tenant;
});
// Make sure bootstrappers are stateful (singletons).
foreach ($this->app['config']['tenancy.bootstrappers'] as $bootstrapper) {
$this->app->singleton($bootstrapper);
}
// Bind the class in the tenancy.id_generator config to the UniqueIdentifierGenerator abstract.
$this->app->bind(Contracts\UniqueIdentifierGenerator::class, $this->app['config']['tenancy.id_generator']);
$this->app->singleton(Commands\Migrate::class, function ($app) { $this->app->singleton(Commands\Migrate::class, function ($app) {
return new Commands\Migrate($app['migrator'], $app[DatabaseManager::class]); return new Commands\Migrate($app['migrator'], $app[DatabaseManager::class]);
}); });
@ -63,6 +87,10 @@ class TenancyServiceProvider extends ServiceProvider
__DIR__ . '/../assets/migrations/' => database_path('migrations'), __DIR__ . '/../assets/migrations/' => database_path('migrations'),
], 'migrations'); ], 'migrations');
$this->publishes([
__DIR__ . '/../assets/TenancyServiceProvider.stub.php' => app_path('Providers/TenancyServiceProvider.php'),
], 'migrations');
$this->loadRoutesFrom(__DIR__ . '/routes.php'); $this->loadRoutesFrom(__DIR__ . '/routes.php');
$this->app->singleton('globalUrl', function ($app) { $this->app->singleton('globalUrl', function ($app) {

View file

@ -12,7 +12,7 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
public function createDatabase(TenantWithDatabase $tenant): bool public function createDatabase(TenantWithDatabase $tenant): bool
{ {
try { try {
return fclose(fopen(database_path($tenant->database()->getName()), 'w')); return file_put_contents(database_path($tenant->database()->getName()), '');
} catch (\Throwable $th) { } catch (\Throwable $th) {
return false; return false;
} }

View file

@ -4,7 +4,7 @@ namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Contracts\TenancyBootstrapper; use Stancl\Tenancy\Contracts\TenancyBootstrapper;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext; use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Events\TenancyEnded; use Stancl\Tenancy\Events\TenancyEnded;
@ -74,12 +74,12 @@ class AutomaticModeTest extends TestCase
class MyBootstrapper implements TenancyBootstrapper class MyBootstrapper implements TenancyBootstrapper
{ {
public function start(\Stancl\Tenancy\Contracts\Tenant $tenant) public function bootstrap(\Stancl\Tenancy\Contracts\Tenant $tenant)
{ {
app()->instance('tenancy_initialized_for_tenant', $tenant->getTenantKey()); app()->instance('tenancy_initialized_for_tenant', $tenant->getTenantKey());
} }
public function end() public function revert()
{ {
app()->instance('tenancy_ended', true); app()->instance('tenancy_ended', true);
} }

View file

@ -7,7 +7,7 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Listeners\RevertToCentralContext; use Stancl\Tenancy\Listeners\RevertToCentralContext;

View file

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper; use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper;

View file

@ -9,7 +9,7 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Tests\Etc\ExampleSeeder; use Stancl\Tenancy\Tests\Etc\ExampleSeeder;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Listeners\RevertToCentralContext; use Stancl\Tenancy\Listeners\RevertToCentralContext;
@ -154,6 +154,7 @@ class CommandsTest extends TestCase
$this->artisan('tenancy:install'); $this->artisan('tenancy:install');
$this->assertFileExists(base_path('routes/tenant.php')); $this->assertFileExists(base_path('routes/tenant.php'));
$this->assertFileExists(base_path('config/tenancy.php')); $this->assertFileExists(base_path('config/tenancy.php'));
$this->assertFileExists(app_path('Providers/TenancyServiceProvider.php'));
$this->assertFileExists(database_path('migrations/2019_09_15_000010_create_tenants_table.php')); $this->assertFileExists(database_path('migrations/2019_09_15_000010_create_tenants_table.php'));
$this->assertFileExists(database_path('migrations/2019_09_15_000020_create_domains_table.php')); $this->assertFileExists(database_path('migrations/2019_09_15_000020_create_domains_table.php'));
$this->assertDirectoryExists(database_path('migrations/tenant')); $this->assertDirectoryExists(database_path('migrations/tenant'));

View file

@ -7,7 +7,7 @@ use Illuminate\Foundation\Auth\User as Authenticable;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase; use Stancl\Tenancy\Jobs\CreateDatabase;

View file

@ -11,7 +11,7 @@ use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Exceptions\TenantDatabaseUserAlreadyExistsException; use Stancl\Tenancy\Exceptions\TenantDatabaseUserAlreadyExistsException;
use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PermissionControlledMySQLDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\PermissionControlledMySQLDatabaseManager;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;

13
tests/Etc/Tenant.php Normal file
View file

@ -0,0 +1,13 @@
<?php
namespace Stancl\Tenancy\Tests\Etc;
use Stancl\Tenancy\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Database\Concerns\HasDatabase;
use Stancl\Tenancy\Database\Concerns\HasDomains;
use Stancl\Tenancy\Database\Models;
class Tenant extends Models\Tenant implements TenantWithDatabase
{
use HasDatabase, HasDomains;
}

View file

@ -5,7 +5,7 @@ namespace Stancl\Tenancy\Tests;
use Illuminate\Events\CallQueuedListener; use Illuminate\Events\CallQueuedListener;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Queue;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\QueueableListener; use Stancl\Tenancy\Listeners\QueueableListener;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;

View file

@ -6,7 +6,7 @@ namespace Stancl\Tenancy\Tests\Features;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Features\CrossDomainRedirect; use Stancl\Tenancy\Features\CrossDomainRedirect;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
class RedirectTest extends TestCase class RedirectTest extends TestCase

View file

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests\Features; namespace Stancl\Tenancy\Tests\Features;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Events\TenancyEnded; use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Features\TenantConfig; use Stancl\Tenancy\Features\TenantConfig;

View file

@ -6,7 +6,7 @@ namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Facades\GlobalCache; use Stancl\Tenancy\Facades\GlobalCache;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper; use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper;

View file

@ -5,7 +5,7 @@ namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Queue;
use Spatie\Valuestore\Valuestore; use Spatie\Valuestore\Valuestore;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
@ -51,7 +51,7 @@ class JobPipelineTest extends TestCase
FooJob::class, FooJob::class,
])->send(function () { ])->send(function () {
return $this->valuestore; return $this->valuestore;
})->queue(true)->toListener()); })->shouldBeQueued(true)->toListener());
Queue::assertNothingPushed(); Queue::assertNothingPushed();
@ -70,7 +70,7 @@ class JobPipelineTest extends TestCase
FooJob::class, FooJob::class,
])->send(function () { ])->send(function () {
return $this->valuestore; return $this->valuestore;
})->queue(true)->toListener()); })->shouldBeQueued(true)->toListener());
$this->assertFalse($this->valuestore->has('foo')); $this->assertFalse($this->valuestore->has('foo'));
Tenant::create(); Tenant::create();

View file

@ -3,7 +3,7 @@
namespace Stancl\Tenancy\Tests; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Exceptions\RouteIsMissingTenantParameterException; use Stancl\Tenancy\Exceptions\RouteIsMissingTenantParameterException;
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByPathException; use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByPathException;
use Stancl\Tenancy\Middleware\InitializeTenancyByPath; use Stancl\Tenancy\Middleware\InitializeTenancyByPath;

View file

@ -10,7 +10,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Spatie\Valuestore\Valuestore; use Spatie\Valuestore\Valuestore;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\QueueTenancyBootstrapper; use Stancl\Tenancy\TenancyBootstrappers\QueueTenancyBootstrapper;

View file

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData; use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;

View file

@ -11,8 +11,8 @@ use Stancl\Tenancy\Contracts\Syncable;
use Stancl\Tenancy\Contracts\SyncMaster; use Stancl\Tenancy\Contracts\SyncMaster;
use Stancl\Tenancy\Database\Concerns\CentralConnection; use Stancl\Tenancy\Database\Concerns\CentralConnection;
use Stancl\Tenancy\Database\Concerns\ResourceSyncing; use Stancl\Tenancy\Database\Concerns\ResourceSyncing;
use Stancl\Tenancy\Database\Models;
use Stancl\Tenancy\Database\Models\TenantPivot; use Stancl\Tenancy\Database\Models\TenantPivot;
use Stancl\Tenancy\DatabaseConfig;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Listeners\RevertToCentralContext; use Stancl\Tenancy\Listeners\RevertToCentralContext;
@ -25,7 +25,9 @@ use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Exceptions\ModelNotSyncMaster; use Stancl\Tenancy\Exceptions\ModelNotSyncMaster;
use Stancl\Tenancy\Jobs\CreateDatabase; use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper; use Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
use Illuminate\Support\Str;
class ResourceSyncingTest extends TestCase class ResourceSyncingTest extends TestCase
{ {
@ -41,6 +43,10 @@ class ResourceSyncingTest extends TestCase
return $event->tenant; return $event->tenant;
})->toListener()); })->toListener());
DatabaseConfig::generateDatabaseNamesUsing(function () {
return 'db' . Str::random(16);
});
Event::listen(TenancyInitialized::class, BootstrapTenancy::class); Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
Event::listen(TenancyEnded::class, RevertToCentralContext::class); Event::listen(TenancyEnded::class, RevertToCentralContext::class);
@ -568,7 +574,7 @@ class ResourceSyncingTest extends TestCase
} }
} }
class ResourceTenant extends Models\Tenant class ResourceTenant extends Tenant
{ {
public function users() public function users()
{ {

View file

@ -5,9 +5,10 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Stancl\Tenancy\Controllers\TenantAssetsController; use Stancl\Tenancy\Controllers\TenantAssetsController;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain; use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
@ -17,6 +18,19 @@ use Stancl\Tenancy\Tests\TestCase;
class TenantAssetTest extends TestCase class TenantAssetTest extends TestCase
{ {
public function getEnvironmentSetUp($app)
{
parent::getEnvironmentSetUp($app);
$app->booted(function () {
if (file_exists(base_path('routes/tenant.php'))) {
Route::middleware(['web'])
->namespace($this->app['config']['tenancy.tenant_route_namespace'] ?? 'App\Http\Controllers')
->group(base_path('routes/tenant.php'));
}
});
}
public function setUp(): void public function setUp(): void
{ {
parent::setUp(); parent::setUp();

View file

@ -6,7 +6,7 @@ namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
class TenantAwareCommandTest extends TestCase class TenantAwareCommandTest extends TestCase

View file

@ -5,12 +5,13 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\DatabaseManager; use Stancl\Tenancy\DatabaseManager;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Exceptions\TenantDatabaseAlreadyExistsException;
use Stancl\Tenancy\Jobs\CreateDatabase; use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper; use Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager;
@ -19,6 +20,7 @@ use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager; use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager;
use Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
use Illuminate\Support\Str;
class TenantDatabaseManagerTest extends TestCase class TenantDatabaseManagerTest extends TestCase
{ {
@ -60,8 +62,7 @@ class TenantDatabaseManagerTest extends TestCase
return $event->tenant; return $event->tenant;
})->toListener()); })->toListener());
// todo if the prefix is _tenancy_, this blows up. write a tenantmodel test that the prefix can be _tenancy_ config(['tenancy.internal_prefix' => 'tenancy_']);
config(['tenancy.internal_prefix' => 'tenancy_',]);
$database = 'db' . $this->randomString(); $database = 'db' . $this->randomString();
@ -141,4 +142,25 @@ class TenantDatabaseManagerTest extends TestCase
$this->assertSame($tenant->database()->getName(), config('database.connections.' . config('database.default') . '.schema')); $this->assertSame($tenant->database()->getName(), config('database.connections.' . config('database.default') . '.schema'));
$this->assertSame($originalDatabaseName, config(['database.connections.pgsql.database'])); $this->assertSame($originalDatabaseName, config(['database.connections.pgsql.database']));
} }
/** @test */
public function a_tenants_database_cannot_be_created_when_the_database_already_exists()
{
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
$name = 'foo' . Str::random(8);
$tenant = Tenant::create([
'tenancy_db_name' => $name,
]);
$manager = $tenant->database()->manager();
$this->assertTrue($manager->databaseExists($tenant->database()->getName()));
$this->expectException(TenantDatabaseAlreadyExistsException::class);
$tenant2 = Tenant::create([
'tenancy_db_name' => $name,
]);
}
} }

View file

@ -7,12 +7,18 @@ use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
use Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator; use Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator;
use Stancl\Tenancy\Contracts; use Stancl\Tenancy\Contracts;
use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator; use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper;
use Illuminate\Support\Str;
class TenantModelTest extends TestCase class TenantModelTest extends TestCase
{ {
@ -132,7 +138,40 @@ class TenantModelTest extends TestCase
$this->assertTrue(tenant() instanceof AnotherTenant); $this->assertTrue(tenant() instanceof AnotherTenant);
} }
// todo test that tenant can be created even in another DB context - that the central trait works /** @test */
public function tenant_can_be_created_even_when_we_are_in_another_tenants_context()
{
config(['tenancy.bootstrappers' => [
DatabaseTenancyBootstrapper::class
]]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function ($event) {
return $event->tenant;
})->toListener());
$tenant1 = Tenant::create([
'id' => 'foo',
'tenancy_db_name' => 'db' . Str::random(16),
]);
tenancy()->initialize($tenant1);
$tenant2 = Tenant::create([
'id' => 'bar',
'tenancy_db_name' => 'db' . Str::random(16),
]);
tenancy()->end();
$this->assertSame(2, Tenant::count());
}
/** @test */
public function data_is_never_encoded_or_decoded_twice()
{
// todo. tests for registerPriorityListener
}
} }
class MyTenant extends Tenant class MyTenant extends Tenant

View file

@ -7,6 +7,7 @@ namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Redis;
use Illuminate\Testing\Assert as PHPUnit; use Illuminate\Testing\Assert as PHPUnit;
use Illuminate\Testing\TestResponse; use Illuminate\Testing\TestResponse;
use Stancl\Tenancy\Tests\Etc\Tenant;
abstract class TestCase extends \Orchestra\Testbench\TestCase abstract class TestCase extends \Orchestra\Testbench\TestCase
{ {
@ -83,6 +84,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
'central' => true, 'central' => true,
], ],
'tenancy.seeder_parameters' => [], 'tenancy.seeder_parameters' => [],
'tenancy.tenant_model' => Tenant::class, // Use test tenant w/ DBs & domains
]); ]);
$app->singleton(\Stancl\Tenancy\TenancyBootstrappers\RedisTenancyBootstrapper::class); $app->singleton(\Stancl\Tenancy\TenancyBootstrappers\RedisTenancyBootstrapper::class);