mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 14:34:04 +00:00
Add broadcasting channel prefixing bootstrapper (#12)
* Rename old broadcast bootstrapper, add new one * Add broadcast tenancy bootstrapper + tests * Fix code style (php-cs-fixer) * Fix prefixing * Work on th bootstrapper's tests (wip – problem with events) * Fix bootstrapper * Test that auth closures of channels work correctly * Fix bootstrapper * Fix code style (php-cs-fixer) * Delete channel cloning bootstrapper * Add bootstrapper that prefixes broadcastOn channels under the hood * Add broadcast channel registering helpers * Update prefixing tests (WIP) * Fix code style (php-cs-fixer) * Improve comment * Fix code style (php-cs-fixer) * Allow customization of Pusher/Ably broadcaster extension * Fix code style (php-cs-fixer) * Implement prefix bootstrapper logic, test channel prefixing using a closure * Work on the prefixing bootstrapper and tests * Fix code style (php-cs-fixer) * Add optional $options param to broadcasting helpers * Test broadcasting helpers * Fix code style (php-cs-fixer) * Broadcasting channel prefixing + testing progress * Improve helper methods * Fix and improve test * Fix extending in bootstrap() * Fix code style (php-cs-fixer) * Add docblocks, name things more accurately * Fix code style (php-cs-fixer) * Delete redundant method from testing broadcaster * Test Pusher channel prefixing (probabaly redundant?) * Test if channels get prefixed correctly when switching tenants * Work with the current broadcast manager instead of overriding it * Give the original channels to the overriden broadcasters * Fix code style (php-cs-fixer) * Simplify channel prefix bootstrapper * Fix code style (php-cs-fixer) * Fix comment * Fix test * Delete annotation * Delete unused classes from test * Delete outdated test * Move broadcasting bootstrapper test to BootstrapperTest * Improve bootstrapper test, delete unused event * Add annotations to the bootstrapper * Fix code style (php-cs-fixer) * Improve wording * Improve comment * Update src/Bootstrappers/BroadcastChannelPrefixBootstrapper.php * Apply suggestions from code review * Optionally skip prefixing of specific channels * Add and test central channel helper, update formatChannels overrides and tests * Fix code style (php-cs-fixer) * minor fixes * Improve annotation * Use "global__" prefix instead of "central__", add comments * Correct tests --------- Co-authored-by: PHP CS Fixer <phpcsfixer@example.com> Co-authored-by: Samuel Štancl <samuel.stancl@gmail.com>
This commit is contained in:
parent
b503dbf33d
commit
c34952f328
7 changed files with 450 additions and 24 deletions
|
|
@ -2,22 +2,27 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Broadcasting\Broadcasters\NullBroadcaster;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
use Stancl\Tenancy\Overrides\TenancyBroadcastManager;
|
||||
use Illuminate\Broadcasting\BroadcastManager;
|
||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Tests\Etc\TestingBroadcaster;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Overrides\TenancyBroadcastManager;
|
||||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\BroadcastingConfigBootstrapper;
|
||||
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
|
||||
use Stancl\Tenancy\Bootstrappers\BroadcastTenancyBootstrapper;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
beforeEach(function () {
|
||||
withTenantDatabases();
|
||||
config(['tenancy.bootstrappers' => [BroadcastTenancyBootstrapper::class]]);
|
||||
TenancyBroadcastManager::$tenantBroadcasters = ['pusher', 'ably'];
|
||||
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
});
|
||||
|
|
@ -27,6 +32,7 @@ afterEach(function () {
|
|||
});
|
||||
|
||||
test('bound broadcaster instance is the same before initializing tenancy and after ending it', function() {
|
||||
config(['tenancy.bootstrappers' => [BroadcastingConfigBootstrapper::class]]);
|
||||
config(['broadcasting.default' => 'null']);
|
||||
TenancyBroadcastManager::$tenantBroadcasters[] = 'null';
|
||||
|
||||
|
|
@ -45,6 +51,7 @@ test('bound broadcaster instance is the same before initializing tenancy and aft
|
|||
});
|
||||
|
||||
test('new broadcasters get the channels from the previously bound broadcaster', function() {
|
||||
config(['tenancy.bootstrappers' => [BroadcastingConfigBootstrapper::class]]);
|
||||
config([
|
||||
'broadcasting.default' => $driver = 'testing',
|
||||
'broadcasting.connections.testing.driver' => $driver,
|
||||
|
|
@ -70,3 +77,111 @@ test('new broadcasters get the channels from the previously bound broadcaster',
|
|||
|
||||
expect($channel)->toBeIn($getCurrentChannels());
|
||||
});
|
||||
|
||||
test('broadcasting channel helpers register channels correctly', function() {
|
||||
config([
|
||||
'broadcasting.default' => $driver = 'testing',
|
||||
'broadcasting.connections.testing.driver' => $driver,
|
||||
]);
|
||||
|
||||
config(['tenancy.bootstrappers' => [DatabaseTenancyBootstrapper::class]]);
|
||||
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
$centralUser = User::create(['name' => 'central', 'email' => 'test@central.cz', 'password' => 'test']);
|
||||
$tenant = Tenant::create();
|
||||
|
||||
migrateTenants();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
// Same ID as $centralUser
|
||||
$tenantUser = User::create(['name' => 'tenant', 'email' => 'test@tenant.cz', 'password' => 'test']);
|
||||
|
||||
tenancy()->end();
|
||||
|
||||
/** @var BroadcastManager $broadcastManager */
|
||||
$broadcastManager = app(BroadcastManager::class);
|
||||
|
||||
// Use a driver with no channels
|
||||
$broadcastManager->extend($driver, fn () => new NullBroadcaster);
|
||||
|
||||
$getChannels = fn (): Collection => $broadcastManager->driver($driver)->getChannels();
|
||||
|
||||
expect($getChannels())->toBeEmpty();
|
||||
|
||||
// Basic channel registration
|
||||
Broadcast::channel($channelName = 'user.{userName}', $channelClosure = function ($user, $userName) {
|
||||
return User::firstWhere('name', $userName)?->is($user) ?? false;
|
||||
});
|
||||
|
||||
// Check if the channel is registered
|
||||
$centralChannelClosure = $getChannels()->first(fn ($closure, $name) => $name === $channelName);
|
||||
expect($centralChannelClosure)->not()->toBeNull();
|
||||
|
||||
// Channel closures work as expected (running in central context)
|
||||
expect($centralChannelClosure($centralUser, $centralUser->name))->toBeTrue();
|
||||
expect($centralChannelClosure($centralUser, $tenantUser->name))->toBeFalse();
|
||||
|
||||
// Register a tenant broadcasting channel (almost identical to the original channel, just able to accept the tenant key)
|
||||
tenant_channel($channelName, $channelClosure);
|
||||
|
||||
// Tenant channel registered – its name is correctly prefixed ("{tenant}.user.{userId}")
|
||||
$tenantChannelClosure = $getChannels()->first(fn ($closure, $name) => $name === "{tenant}.$channelName");
|
||||
expect($tenantChannelClosure)
|
||||
->not()->toBeNull() // Channel registered
|
||||
->not()->toBe($centralChannelClosure); // The tenant channel closure is different – after the auth user, it accepts the tenant ID
|
||||
|
||||
// The tenant channels are prefixed with '{tenant}.'
|
||||
// They accept the tenant key, but their closures only run in tenant context when tenancy is initialized
|
||||
// The regular channels don't accept the tenant key, but they also respect the current context
|
||||
// The tenant key is used solely for the name prefixing – the closures can still run in the central context
|
||||
expect($tenantChannelClosure($centralUser, $tenant->getTenantKey(), $centralUser->name))->toBeTrue();
|
||||
expect($tenantChannelClosure($centralUser, $tenant->getTenantKey(), $tenantUser->name))->toBeFalse();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
// The channel closure runs in the central context
|
||||
// Only the central user is available
|
||||
expect($tenantChannelClosure($centralUser, $tenant->getTenantKey(), $tenantUser->name))->toBeFalse();
|
||||
expect($tenantChannelClosure($tenantUser, $tenant->getTenantKey(), $tenantUser->name))->toBeTrue();
|
||||
|
||||
// Use a new channel instance to delete the previously registered channels before testing the univeresal_channel helper
|
||||
$broadcastManager->purge($driver);
|
||||
$broadcastManager->extend($driver, fn () => new NullBroadcaster);
|
||||
|
||||
expect($getChannels())->toBeEmpty();
|
||||
|
||||
// universal_channel helper registers both the unprefixed and the prefixed broadcasting channel correctly
|
||||
// Using the tenant_channel helper + basic channel registration (Broadcast::channel())
|
||||
universal_channel($channelName, $channelClosure);
|
||||
|
||||
// Regular channel registered correctly
|
||||
$centralChannelClosure = $getChannels()->first(fn ($closure, $name) => $name === $channelName);
|
||||
expect($centralChannelClosure)->not()->toBeNull();
|
||||
|
||||
// Tenant channel registered correctly
|
||||
$tenantChannelClosure = $getChannels()->first(fn ($closure, $name) => $name === "{tenant}.$channelName");
|
||||
expect($tenantChannelClosure)
|
||||
->not()->toBeNull() // Channel registered
|
||||
->not()->toBe($centralChannelClosure); // The tenant channel callback is different – after the auth user, it accepts the tenant ID
|
||||
|
||||
$broadcastManager->purge($driver);
|
||||
$broadcastManager->extend($driver, fn () => new NullBroadcaster);
|
||||
|
||||
expect($getChannels())->toBeEmpty();
|
||||
|
||||
// Central channel prefixes the channel name with 'global__'
|
||||
global_channel($channelName, $channelClosure);
|
||||
|
||||
// Channel prefixed with 'global__' found
|
||||
$foundChannelClosure = $getChannels()->first(fn ($closure, $name) => $name === 'global__' . $channelName);
|
||||
expect($foundChannelClosure)->not()->toBeNull();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue