1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-02-05 06:54:05 +00:00

Merge branch 'archtechx:3.x' into 3.x

This commit is contained in:
Leandro Gehlen 2024-05-15 09:12:38 -03:00 committed by GitHub
commit 394d6ad338
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 88 additions and 26 deletions

View file

@ -20,6 +20,8 @@ jobs:
php: "8.0" php: "8.0"
- laravel: 10 - laravel: 10
php: "8.1" php: "8.1"
- laravel: 11
php: "8.3"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

1
.gitignore vendored
View file

@ -5,6 +5,7 @@ vendor/
.idea .idea
psysh psysh
.phpunit.result.cache .phpunit.result.cache
.phpunit.cache
phpunit_var_*.xml phpunit_var_*.xml
coverage/ coverage/
clover.xml clover.xml

View file

@ -120,10 +120,12 @@ class TenancyServiceProvider extends ServiceProvider
protected function mapRoutes() protected function mapRoutes()
{ {
$this->app->booted(function () {
if (file_exists(base_path('routes/tenant.php'))) { if (file_exists(base_path('routes/tenant.php'))) {
Route::namespace(static::$controllerNamespace) Route::namespace(static::$controllerNamespace)
->group(base_path('routes/tenant.php')); ->group(base_path('routes/tenant.php'));
} }
});
} }
protected function makeTenancyMiddlewareHighestPriority() protected function makeTenancyMiddlewareHighestPriority()

View file

@ -12,15 +12,15 @@
"require": { "require": {
"php": "^8.0", "php": "^8.0",
"ext-json": "*", "ext-json": "*",
"illuminate/support": "^9.0|^10.0", "illuminate/support": "^9.0|^10.0|^11.0",
"facade/ignition-contracts": "^1.0.2", "facade/ignition-contracts": "^1.0.2",
"ramsey/uuid": "^4.7.3", "ramsey/uuid": "^4.7.3",
"stancl/jobpipeline": "^1.6.2", "stancl/jobpipeline": "^1.6.2",
"stancl/virtualcolumn": "^1.3.1" "stancl/virtualcolumn": "^1.3.1"
}, },
"require-dev": { "require-dev": {
"laravel/framework": "^9.0|^10.0", "laravel/framework": "^9.0|^10.0|^11.0",
"orchestra/testbench": "^7.0|^8.0", "orchestra/testbench": "^7.0|^8.0|^9.0",
"league/flysystem-aws-s3-v3": "^3.12.2", "league/flysystem-aws-s3-v3": "^3.12.2",
"doctrine/dbal": "^3.6.0", "doctrine/dbal": "^3.6.0",
"spatie/valuestore": "^1.3.2" "spatie/valuestore": "^1.3.2"

View file

@ -1,13 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false"> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<coverage> <coverage>
<include>
<directory suffix=".php">./src</directory>
</include>
<exclude>
<file>./src/routes.php</file>
<file>./src/Vite.php</file>
</exclude>
<report> <report>
<clover outputFile="coverage/phpunit/clover.xml"/> <clover outputFile="coverage/phpunit/clover.xml"/>
<html outputDirectory="coverage/phpunit/html" lowUpperBound="35" highLowerBound="70"/> <html outputDirectory="coverage/phpunit/html" lowUpperBound="35" highLowerBound="70"/>
@ -29,4 +22,13 @@
<env name="AWS_DEFAULT_REGION" value="us-west-2"/> <env name="AWS_DEFAULT_REGION" value="us-west-2"/>
</php> </php>
<logging/> <logging/>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
<exclude>
<file>./src/routes.php</file>
<file>./src/Vite.php</file>
</exclude>
</source>
</phpunit> </phpunit>

View file

@ -28,8 +28,8 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper
$prefix = $this->config['tenancy.redis.prefix_base'] . $tenant->getTenantKey(); $prefix = $this->config['tenancy.redis.prefix_base'] . $tenant->getTenantKey();
$client = Redis::connection($connection)->client(); $client = Redis::connection($connection)->client();
$this->originalPrefixes[$connection] = $client->getOption($client::OPT_PREFIX); $this->originalPrefixes[$connection] = $client->getOption(\Redis::OPT_PREFIX);
$client->setOption($client::OPT_PREFIX, $prefix); $client->setOption(\Redis::OPT_PREFIX, $prefix);
} }
} }
@ -38,7 +38,7 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper
foreach ($this->prefixedConnections() as $connection) { foreach ($this->prefixedConnections() as $connection) {
$client = Redis::connection($connection)->client(); $client = Redis::connection($connection)->client();
$client->setOption($client::OPT_PREFIX, $this->originalPrefixes[$connection]); $client->setOption(\Redis::OPT_PREFIX, $this->originalPrefixes[$connection]);
} }
$this->originalPrefixes = []; $this->originalPrefixes = [];

View file

@ -25,6 +25,7 @@ final class MigrateFresh extends Command
parent::__construct(); parent::__construct();
$this->addOption('--drop-views', null, InputOption::VALUE_NONE, 'Drop views along with tenant tables.', null); $this->addOption('--drop-views', null, InputOption::VALUE_NONE, 'Drop views along with tenant tables.', null);
$this->addOption('--step', null, InputOption::VALUE_NONE, 'Force the migrations to be run so they can be rolled back individually.');
$this->setName('tenants:migrate-fresh'); $this->setName('tenants:migrate-fresh');
} }
@ -47,6 +48,7 @@ final class MigrateFresh extends Command
$this->info('Migrating.'); $this->info('Migrating.');
$this->callSilent('tenants:migrate', [ $this->callSilent('tenants:migrate', [
'--tenants' => [$tenant->getTenantKey()], '--tenants' => [$tenant->getTenantKey()],
'--step' => $this->option('step'),
'--force' => true, '--force' => true,
]); ]);
}); });

View file

@ -10,8 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface;
trait TenantAwareCommand trait TenantAwareCommand
{ {
/** @return int */ protected function execute(InputInterface $input, OutputInterface $output): int
protected function execute(InputInterface $input, OutputInterface $output)
{ {
$tenants = $this->getTenants(); $tenants = $this->getTenants();
$exitCode = 0; $exitCode = 0;

View file

@ -35,7 +35,7 @@ class UniversalRoutes implements Feature
public static function routeHasMiddleware(Route $route, $middleware): bool public static function routeHasMiddleware(Route $route, $middleware): bool
{ {
if (in_array($middleware, $route->middleware(), true)) { if (in_array($middleware, $route->computedMiddleware ?? $route->middleware(), true)) {
return true; return true;
} }

View file

@ -37,6 +37,14 @@ class PathTenantResolver extends Contracts\CachedTenantResolver
throw new TenantCouldNotBeIdentifiedByPathException($id); throw new TenantCouldNotBeIdentifiedByPathException($id);
} }
public function resolved(Tenant $tenant, ...$args): void
{
/** @var Route $route */
$route = $args[0];
$route->forgetParameter(static::$tenantParameterName);
}
public function getArgsForTenant(Tenant $tenant): array public function getArgsForTenant(Tenant $tenant): array
{ {
return [ return [

View file

@ -15,6 +15,6 @@ class Vite extends BaseVite // todo move to a different directory in v4
*/ */
protected function assetPath($path, $secure = null) protected function assetPath($path, $secure = null)
{ {
return global_asset($path); return $this->assetPathResolver ? ($this->assetPathResolver)($path, $secure) : global_asset($path);
} }
} }

View file

@ -196,10 +196,12 @@ class CommandsTest extends TestCase
{ {
$tenantId1 = Tenant::create()->getTenantKey(); $tenantId1 = Tenant::create()->getTenantKey();
$tenantId2 = Tenant::create()->getTenantKey(); $tenantId2 = Tenant::create()->getTenantKey();
$tenantId3 = Tenant::create()->getTenantKey();
Artisan::call('tenants:migrate-fresh'); Artisan::call('tenants:migrate-fresh');
$this->artisan("tenants:run foo --tenants=$tenantId1 --tenants=$tenantId2 --argument='a=foo' --option='b=bar' --option='c=xyz'") $this->artisan("tenants:run foo --tenants=$tenantId1 --tenants=$tenantId2 --argument='a=foo' --option='b=bar' --option='c=xyz'")
->expectsOutput('Tenant: ' . $tenantId1) ->expectsOutput('Tenant: ' . $tenantId1)
->expectsOutput('Tenant: ' . $tenantId2); ->expectsOutput('Tenant: ' . $tenantId2)
->doesntExpectOutput('Tenant: ' . $tenantId3);
} }
} }

View file

@ -4,9 +4,7 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests; namespace Stancl\Tenancy\Tests;
use Closure;
use Exception; use Exception;
use Illuminate\Support\Str;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Spatie\Valuestore\Valuestore; use Spatie\Valuestore\Valuestore;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -25,7 +23,6 @@ use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing; use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use PDO;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext; use Stancl\Tenancy\Listeners\RevertToCentralContext;
@ -59,6 +56,7 @@ class QueueTest extends TestCase
public function tearDown(): void public function tearDown(): void
{ {
parent::tearDown();
$this->valuestore->flush(); $this->valuestore->flush();
} }

View file

@ -57,9 +57,11 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
$app['config']->set([ $app['config']->set([
'database.default' => 'central', 'database.default' => 'central',
'cache.default' => 'redis',
'database.redis.cache.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'), 'database.redis.cache.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'),
'database.redis.default.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'), 'database.redis.default.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'),
'database.redis.options.prefix' => 'foo', 'database.redis.options.prefix' => 'foo',
'database.redis.client' => 'predis',
'database.connections.central' => [ 'database.connections.central' => [
'driver' => 'mysql', 'driver' => 'mysql',
'url' => env('DATABASE_URL'), 'url' => env('DATABASE_URL'),
@ -80,6 +82,8 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
]) : [], ]) : [],
], ],
'database.connections.sqlite.database' => ':memory:', 'database.connections.sqlite.database' => ':memory:',
'database.connections.mysql.charset' => 'utf8mb4',
'database.connections.mysql.collation' => 'utf8mb4_unicode_ci',
'database.connections.mysql.host' => env('TENANCY_TEST_MYSQL_HOST', '127.0.0.1'), 'database.connections.mysql.host' => env('TENANCY_TEST_MYSQL_HOST', '127.0.0.1'),
'database.connections.pgsql.host' => env('TENANCY_TEST_PGSQL_HOST', '127.0.0.1'), 'database.connections.pgsql.host' => env('TENANCY_TEST_PGSQL_HOST', '127.0.0.1'),
'tenancy.filesystem.disks' => [ 'tenancy.filesystem.disks' => [

View file

@ -63,4 +63,46 @@ class UniversalRouteTest extends TestCase
->assertSuccessful() ->assertSuccessful()
->assertSee('acme'); ->assertSee('acme');
} }
/** @test */
public function universal_route_works_when_middleware_is_inserted_via_controller_middleware()
{
Route::middlewareGroup('universal', []);
config(['tenancy.features' => [UniversalRoutes::class]]);
Route::get('/foo', [UniversalRouteController::class, 'show']);
$this->get('http://localhost/foo')
->assertSuccessful()
->assertSee('Tenancy is not initialized.');
$tenant = Tenant::create([
'id' => 'acme',
]);
$tenant->domains()->create([
'domain' => 'acme.localhost',
]);
$this->get('http://acme.localhost/foo')
->assertSuccessful()
->assertSee('Tenancy is initialized.');
}
}
class UniversalRouteController
{
public function getMiddleware()
{
return array_map(fn($middleware) => [
'middleware' => $middleware,
'options' => [],
], ['universal', InitializeTenancyByDomain::class]);
}
public function show()
{
return tenancy()->initialized
? 'Tenancy is initialized.'
: 'Tenancy is not initialized.';
}
} }