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

Improve route cloning action (#8)

* Allow cloning routes when only kernel identification is used, explicitly enable specific cloning modes

* Explicitly enable needed clone modes in tests, use "clone" instead of "reregister"

* Fix code style (php-cs-fixer)

* Use  "cloning" instead of "re-registration" in UniversalRouteTest

* Only clone routes using path identification

* Revert clone mode changes

* Fix code style (php-cs-fixer)

* Update comment

* Skip cloning 'stancl.tenancy.asset' by default

* Decide which routes should get cloned in the filtering step, improve method organization

* Return `RouteMode::UNIVERSAL` in getMiddlewareContext if route is universal

* Give universal route the path ID MW so that it gets cloned

* Fix code style (php-cs-fixer)

* Simplify UsableWithEarlyIdentification code

* Handle universal route mode in ForgetTenantParameter

* Fix code style (php-cs-fixer)

* Rename getMiddlewareContext to getRouteMode

* Append '/' to the route prefix

* Rename variable

* Wrap part of condition in parentheses

* Refresh name lookups after cloning routes

* Test giving tenant flag to cloned routes

* Add routeIsUniversal method

* Correct ForgetTenantParameter condition

* Improve tenant flag giving logic

* Improve test name

* Delete leftover testing code

* Put part of condition into `()`

* Improve CloneRoutesAsTenant code + comments

* Extract route mode-related code into methods, refactor and improve code

* Improve ForgetTenantParameter, test tenant parameter removing in universal routes

* Fix code style (php-cs-fixer)

* Fix test

* Simplify adding tenant flag

* Don't skip stancl.tenancy.asset route cloning

* clean up comment

* fix in_array() argument

* Fix code style (php-cs-fixer)

---------

Co-authored-by: PHP CS Fixer <phpcsfixer@example.com>
Co-authored-by: Samuel Štancl <samuel.stancl@gmail.com>
This commit is contained in:
lukinovec 2023-08-28 13:17:17 +02:00 committed by GitHub
parent 4d4639450e
commit f7d9f02fd4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 222 additions and 111 deletions

View file

@ -263,6 +263,14 @@ test('the tenant parameter is only removed from tenant routes when using path id
->middleware('tenant')
->name('tenant-route');
RouteFacade::get($pathIdentification ? '/universal-route' : '/universal-route/{tenant?}', [ControllerWithMiddleware::class, 'routeHasTenantParameter'])
->middleware('universal')
->name('universal-route');
/** @var CloneRoutesAsTenant */
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
$cloneRoutesAction->handle();
$tenant = Tenant::create();
$tenantKey = $tenant->getTenantKey();
@ -274,12 +282,24 @@ test('the tenant parameter is only removed from tenant routes when using path id
// Tenant parameter is removed from tenant routes using kernel path identification (Stancl\Tenancy\Listeners\ForgetTenantParameter)
$response = pest()->get($tenantKey . '/tenant-route')->assertOk();
expect((bool) $response->getContent())->toBeFalse();
// The tenant parameter gets removed from the cloned universal route
$response = pest()->get($tenantKey . '/universal-route')->assertOk();
expect((bool) $response->getContent())->toBeFalse();
} else {
// Tenant parameter is not removed from tenant routes using other kernel identification MW
$tenant->domains()->create(['domain' => $domain = $tenantKey . '.localhost']);
$response = pest()->get("http://{$domain}/{$tenantKey}/tenant-route")->assertOk();
expect((bool) $response->getContent())->toBeTrue();
// The tenant parameter does not get removed from the universal route when accessing it through the central domain
$response = pest()->get("http://localhost/universal-route/$tenantKey")->assertOk();
expect((bool) $response->getContent())->toBeTrue();
// The tenant parameter gets removed from the universal route when accessing it through the tenant domain
$response = pest()->get("http://{$domain}/universal-route")->assertOk();
expect((bool) $response->getContent())->toBeFalse();
}
} else {
RouteFacade::middlewareGroup('tenant', [$pathIdentification ? InitializeTenancyByPath::class : InitializeTenancyByDomain::class]);
@ -370,7 +390,7 @@ test('route level identification is prioritized over kernel identification', fun
'default to central routes' => RouteMode::CENTRAL,
]);
test('routes with path identification middleware can get prefixed using the reregister action', function() {
test('routes with path identification middleware can get prefixed using the clone action', function() {
$tenantKey = Tenant::create()->getTenantKey();
RouteFacade::get('/home', fn () => tenant()?->getTenantKey())->name('home')->middleware(InitializeTenancyByPath::class);

View file

@ -169,10 +169,10 @@ test('a route can be universal using path identification', function (array $rout
: 'Tenancy is not initialized.';
})->middleware($routeMiddleware);
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
/** @var CloneRoutesAsTenant $cloneRoutesAction */
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
$reregisterRoutesAction->handle();
$cloneRoutesAction->handle();
$tenantKey = Tenant::create()->getTenantKey();
@ -234,10 +234,10 @@ test('correct exception is thrown when route is universal and tenant could not b
RouteFacade::get('/foo', fn () => tenant() ? 'Tenancy is initialized.' : 'Tenancy is not initialized.')->middleware($routeMiddleware)->name('foo');
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
/** @var CloneRoutesAsTenant $cloneRoutesAction */
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
$reregisterRoutesAction->handle();
$cloneRoutesAction->handle();
pest()->expectException(TenantCouldNotBeIdentifiedByPathException::class);
$this->withoutExceptionHandling()->get('http://localhost/non_existent/foo');
@ -309,7 +309,7 @@ test('a route can be flagged as universal in both route modes', function (RouteM
'default to central routes' => RouteMode::CENTRAL,
]);
test('ReregisterRoutesAsTenant registers prefixed duplicates of universal routes correctly', function (bool $kernelIdentification, bool $useController) {
test('CloneRoutesAsTenant registers prefixed duplicates of universal routes correctly', function (bool $kernelIdentification, bool $useController) {
$routeMiddleware = ['universal'];
if ($kernelIdentification) {
@ -328,10 +328,10 @@ test('ReregisterRoutesAsTenant registers prefixed duplicates of universal routes
expect($routes = RouteFacade::getRoutes()->get())->toContain($universalRoute);
expect($routes)->toContain($centralRoute);
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
/** @var CloneRoutesAsTenant $cloneRoutesAction */
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
$reregisterRoutesAction->handle();
$cloneRoutesAction->handle();
expect($routesAfterRegisteringDuplicates = RouteFacade::getRoutes()->get())
->toContain($universalRoute)
@ -340,6 +340,7 @@ test('ReregisterRoutesAsTenant registers prefixed duplicates of universal routes
$newRoute = collect($routesAfterRegisteringDuplicates)->filter(fn ($route) => ! in_array($route, $routes))->first();
expect($newRoute->uri())->toBe('{' . $tenantParameterName . '}' . '/' . $universalRoute->uri());
expect(tenancy()->getRouteMiddleware($newRoute))->toBe(array_merge(tenancy()->getRouteMiddleware($universalRoute), ['tenant']));
$tenant = Tenant::create();
@ -386,51 +387,55 @@ test('CloneRoutesAsTenant only clones routes with path identification by default
expect($currentRouteCount())->toBe($newRouteCount = $initialRouteCount + 2);
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
/** @var CloneRoutesAsTenant $cloneRoutesAction */
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
$reregisterRoutesAction->handle();
$cloneRoutesAction->handle();
// Only one of the two routes gets cloned
expect($currentRouteCount())->toBe($newRouteCount + 1);
});
test('custom callbacks can be used for reregistering universal routes', function () {
RouteFacade::get('/home', fn () => tenant() ? 'Tenancy initialized.' : 'Tenancy not initialized.')->middleware('universal')->name($routeName = 'home');
test('custom callbacks can be used for cloning universal routes', function () {
RouteFacade::get('/home', fn () => tenant() ? 'Tenancy initialized.' : 'Tenancy not initialized.')->middleware(['universal', InitializeTenancyByPath::class])->name($routeName = 'home');
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
/** @var CloneRoutesAsTenant $cloneRoutesAction */
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
$currentRouteCount = fn () => count(RouteFacade::getRoutes()->get());
$initialRouteCount = $currentRouteCount();
$cloneRoutesAction;
// Skip cloning the 'home' route
$reregisterRoutesAction->cloneUsing($routeName, function (Route $route) {
$cloneRoutesAction->cloneUsing($routeName, function (Route $route) {
return;
})->handle();
// Expect route count to stay the same because the 'home' route re-registration gets skipped
// Expect route count to stay the same because the 'home' route cloning gets skipped
expect($initialRouteCount)->toEqual($currentRouteCount());
// Modify the 'home' route re-registration so that a different route is registered
$reregisterRoutesAction->cloneUsing($routeName, function (Route $route) {
RouteFacade::get('/newly-registered-route', fn() => true)->name('new.home');
// Modify the 'home' route cloning so that a different route is cloned
$cloneRoutesAction->cloneUsing($routeName, function (Route $route) {
RouteFacade::get('/cloned-route', fn () => true)->name('new.home');
})->handle();
expect($currentRouteCount())->toEqual($initialRouteCount + 1);
});
test('reregistration of specific routes can get skipped', function () {
test('cloning of specific routes can get skipped', function () {
RouteFacade::get('/home', fn () => tenant() ? 'Tenancy initialized.' : 'Tenancy not initialized.')->middleware('universal')->name($routeName = 'home');
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
/** @var CloneRoutesAsTenant $cloneRoutesAction */
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
$currentRouteCount = fn () => count(RouteFacade::getRoutes()->get());
$initialRouteCount = $currentRouteCount();
// Skip cloning the 'home' route
$reregisterRoutesAction->skipRoute($routeName)->handle();
$cloneRoutesAction->skipRoute($routeName);
// Expect route count to stay the same because the 'home' route re-registration gets skipped
$cloneRoutesAction->handle();
// Expect route count to stay the same because the 'home' route cloning gets skipped
expect($initialRouteCount)->toEqual($currentRouteCount());
});
@ -458,6 +463,31 @@ test('identification middleware works with universal routes only when it impleme
$this->withoutExceptionHandling()->get('http://localhost/custom-mw-universal-route');
});
test('routes except nonuniversal routes with path id mw are given the tenant flag after cloning', function (array $routeMiddleware, array $globalMiddleware) {
foreach ($globalMiddleware as $middleware) {
if ($middleware === 'universal') {
config(['tenancy.default_route_mode' => RouteMode::UNIVERSAL]);
} else {
app(Kernel::class)->pushMiddleware($middleware);
}
}
$route = RouteFacade::get('/home', fn () => tenant() ? 'Tenancy initialized.' : 'Tenancy not initialized.')
->middleware($routeMiddleware)
->name($routeName = 'home');
app(CloneRoutesAsTenant::class)->handle();
$clonedRoute = RouteFacade::getRoutes()->getByName('tenant.' . $routeName);
// Non-universal routes with identification middleware are already considered tenant, so they don't get the tenant flag
if (! tenancy()->routeIsUniversal($route) && tenancy()->routeHasIdentificationMiddleware($clonedRoute)) {
expect($clonedRoute->middleware())->not()->toContain('tenant');
} else {
expect($clonedRoute->middleware())->toContain('tenant');
}
})->with('path identification types');
foreach ([
'domain identification types' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomain::class],
'subdomain identification types' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class],