Skip to content

Commit

Permalink
Merge pull request #492 from masterix21/fix-queued-closures
Browse files Browse the repository at this point in the history
Add support to queued closures
  • Loading branch information
masterix21 authored Sep 27, 2023
2 parents 5985146 + 3f68dee commit c376185
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- laravel: 9.*
testbench: 7.*
- laravel: 8.*
testbench: ^6.23
testbench: ^6.35
exclude:
- laravel: 10.*
php: 8.0
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"require-dev": {
"laravel/legacy-factories": "^1.0.4",
"laravel/octane": "^1.0",
"laravel/serializable-closure": "^1.1",
"mockery/mockery": "^1.4",
"orchestra/testbench": "^6.23|^7.0|^8.0",
"pestphp/pest": "^1.22",
Expand Down
2 changes: 2 additions & 0 deletions config/multitenancy.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Illuminate\Events\CallQueuedListener;
use Illuminate\Mail\SendQueuedMailable;
use Illuminate\Notifications\SendQueuedNotifications;
use Illuminate\Queue\CallQueuedClosure;
use Spatie\Multitenancy\Actions\ForgetCurrentTenantAction;
use Spatie\Multitenancy\Actions\MakeQueueTenantAwareAction;
use Spatie\Multitenancy\Actions\MakeTenantCurrentAction;
Expand Down Expand Up @@ -95,6 +96,7 @@
'queueable_to_job' => [
SendQueuedMailable::class => 'mailable',
SendQueuedNotifications::class => 'notification',
CallQueuedClosure::class => 'closure',
CallQueuedListener::class => 'class',
BroadcastEvent::class => 'event',
],
Expand Down
13 changes: 13 additions & 0 deletions docs/basic-usage/making-queues-tenant-aware.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ or, using the config `multitenancy.php`:
],
```

## Queueing Closures

Dispatch a closure is slightly different from a job class because here, you can't implement `TenantAware` or `NotTenantAware` interfaces. The package can handle the queue closures by enabling the `queues_are_tenant_aware_by_default`, but if you enjoy keeping to `false` parameter, you can dispatch a tenant-aware job closure like so:

```php
$tenant = Tenant::current();

dispatch(function () use ($tenant) {
$tenant->execute(function () {
// Your job
});
});
```

## When the tenant cannot be retrieved

Expand Down
28 changes: 26 additions & 2 deletions docs/installation/base-installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use Illuminate\Broadcasting\BroadcastEvent;
use Illuminate\Events\CallQueuedListener;
use Illuminate\Mail\SendQueuedMailable;
use Illuminate\Notifications\SendQueuedNotifications;
use Illuminate\Queue\CallQueuedClosure;
use Spatie\Multitenancy\Actions\ForgetCurrentTenantAction;
use Spatie\Multitenancy\Actions\MakeQueueTenantAwareAction;
use Spatie\Multitenancy\Actions\MakeTenantCurrentAction;
Expand Down Expand Up @@ -55,7 +56,9 @@ return [
* A valid task is any class that implements Spatie\Multitenancy\Tasks\SwitchTenantTask
*/
'switch_tenant_tasks' => [
// add tasks here
// \Spatie\Multitenancy\Tasks\PrefixCacheTask::class,
// \Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask::class,
// \Spatie\Multitenancy\Tasks\SwitchRouteCacheTask::class,
],

/*
Expand Down Expand Up @@ -89,8 +92,14 @@ return [
*/
'current_tenant_container_key' => 'currentTenant',

/**
* Set it to `true` if you like to cache the tenant(s) routes
* in a shared file using the `SwitchRouteCacheTask`.
*/
'shared_routes_cache' => false,

/*
* You can customize some of the behavior of this package by using our own custom action.
* You can customize some of the behavior of this package by using your own custom action.
* Your custom action should always extend the default one.
*/
'actions' => [
Expand All @@ -109,9 +118,24 @@ return [
'queueable_to_job' => [
SendQueuedMailable::class => 'mailable',
SendQueuedNotifications::class => 'notification',
CallQueuedClosure::class => 'closure',
CallQueuedListener::class => 'class',
BroadcastEvent::class => 'event',
],

/*
* Jobs tenant aware even if these don't implement the TenantAware interface.
*/
'tenant_aware_jobs' => [
// ...
],

/*
* Jobs not tenant aware even if these don't implement the NotTenantAware interface.
*/
'not_tenant_aware_jobs' => [
// ...
],
];
```

Expand Down
5 changes: 0 additions & 5 deletions tests/Feature/Commands/TenantAwareCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,11 @@
use Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask;

beforeEach(function () {
config(['database.default' => 'tenant']);
config()->set('multitenancy.switch_tenant_tasks', [SwitchTenantDatabaseTask::class]);

$this->tenant = Tenant::factory()->create(['database' => 'laravel_mt_tenant_1']);
$this->tenant->makeCurrent();

$this->anotherTenant = Tenant::factory()->create(['database' => 'laravel_mt_tenant_2']);
$this->anotherTenant->makeCurrent();

Tenant::forgetCurrent();
});

it('fails with a non-existing tenant')
Expand Down
18 changes: 6 additions & 12 deletions tests/Feature/Commands/TenantsArtisanCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,26 @@
use Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask;

beforeEach(function () {
config(['database.default' => 'tenant']);

config()->set('multitenancy.switch_tenant_tasks', [SwitchTenantDatabaseTask::class]);

$this->tenant = Tenant::factory()->create(['database' => 'laravel_mt_tenant_1']);
$this->tenant->makeCurrent();
Schema::connection('tenant')->dropIfExists('migrations');
$this->tenant->execute(fn () => Schema::connection('tenant')->dropIfExists('migrations'));

$this->anotherTenant = Tenant::factory()->create(['database' => 'laravel_mt_tenant_2']);
$this->anotherTenant->makeCurrent();
Schema::connection('tenant')->dropIfExists('migrations');

Tenant::forgetCurrent();
$this->anotherTenant->execute(fn () => Schema::connection('tenant')->dropIfExists('migrations'));
});

it('can migrate all tenant databases', function () {
$this
->artisan('tenants:artisan migrate')
->artisan('tenants:artisan "migrate --database=tenant"')
->assertExitCode(0);

assertTenantDatabaseHasTable($this->tenant, 'migrations');
assertTenantDatabaseHasTable($this->anotherTenant, 'migrations');
});

it('can migrate a specific tenant', function () {
$this->artisan('tenants:artisan migrate --tenant="' . $this->anotherTenant->id . '"')->assertExitCode(0);
$this->artisan('tenants:artisan "migrate --database=tenant" --tenant="' . $this->anotherTenant->id . '"')->assertExitCode(0);

assertTenantDatabaseDoesNotHaveTable($this->tenant, 'migrations');
assertTenantDatabaseHasTable($this->anotherTenant, 'migrations');
Expand All @@ -40,7 +34,7 @@
config(['multitenancy.tenant_artisan_search_fields' => 'domain']);

$this->artisan('tenants:artisan', [
'artisanCommand' => 'migrate',
'artisanCommand' => 'migrate --database=tenant',
'--tenant' => $this->anotherTenant->id,
])
->expectsOutput("No tenant(s) found.")
Expand All @@ -51,7 +45,7 @@
config(['multitenancy.tenant_artisan_search_fields' => 'domain']);

$this->artisan('tenants:artisan', [
'artisanCommand' => 'migrate',
'artisanCommand' => 'migrate --database=tenant',
'--tenant' => $this->anotherTenant->domain,
])->assertExitCode(0);

Expand Down
70 changes: 70 additions & 0 deletions tests/Feature/TenantAwareJobs/QueuedClosuresTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

use Spatie\Multitenancy\Models\Tenant;
use Spatie\Valuestore\Valuestore;

beforeEach(function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', false);

$this->tenant = Tenant::factory()->create();
});

it('succeeds with closure job when queues are tenant aware by default', function () {
$valuestore = Valuestore::make(tempFile('tenantAware.json'))->flush();

config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

$this->tenant->makeCurrent();

dispatch(function () use ($valuestore) {
$tenant = Tenant::current();

$valuestore->put('tenantId', $tenant?->getKey());
$valuestore->put('tenantName', $tenant?->name);
});

$this->artisan('queue:work --once')->assertExitCode(0);

expect($valuestore->get('tenantId'))->toBe($this->tenant->getKey())
->and($valuestore->get('tenantName'))->toBe($this->tenant->name);
});

it('fails with closure job when queues are not tenant aware by default', function () {
$valuestore = Valuestore::make(tempFile('tenantAware.json'))->flush();

$this->tenant->makeCurrent();

dispatch(function () use ($valuestore) {
$tenant = Tenant::current();

$valuestore->put('tenantId', $tenant?->getKey());
$valuestore->put('tenantName', $tenant?->name);
});

$this->artisan('queue:work --once')->assertExitCode(0);

expect($valuestore->get('tenantId'))->toBeNull()
->and($valuestore->get('tenantName'))->toBeNull();
});

it('succeeds with closure job when a tenant is specified', function () {
$valuestore = Valuestore::make(tempFile('tenantAware.json'))->flush();

$currentTenant = $this->tenant;

dispatch(function () use ($valuestore, $currentTenant) {
$currentTenant->makeCurrent();

$tenant = Tenant::current();

$valuestore->put('tenantId', $tenant?->getKey());
$valuestore->put('tenantName', $tenant?->name);

$currentTenant->forget();
});

$this->artisan('queue:work --once')->assertExitCode(0);

expect($valuestore->get('tenantId'))->toBe($this->tenant->getKey())
->and($valuestore->get('tenantName'))->toBe($this->tenant->name);
});

0 comments on commit c376185

Please sign in to comment.