Add BootstrapTenantController and requests for tenant domain normalization; implement API route and tests
This commit is contained in:
21
app/Domains/Tenant/Controllers/BootstrapTenantController.php
Normal file
21
app/Domains/Tenant/Controllers/BootstrapTenantController.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Tenant\Controllers;
|
||||
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Tenant\Requests\BootstrapTenantRequest;
|
||||
use App\Domains\Tenant\Resources\TenantResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class BootstrapTenantController extends Controller
|
||||
{
|
||||
public function __invoke(BootstrapTenantRequest $request): TenantResource
|
||||
{
|
||||
/** @var string $dominio */
|
||||
$dominio = $request->validated('dominio');
|
||||
|
||||
return TenantResource::make(
|
||||
Tenant::query()->where('dominio', $dominio)->firstOrFail()
|
||||
);
|
||||
}
|
||||
}
|
||||
50
app/Domains/Tenant/Requests/BootstrapTenantRequest.php
Normal file
50
app/Domains/Tenant/Requests/BootstrapTenantRequest.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Tenant\Requests;
|
||||
|
||||
use App\Domains\Tenant\Support\TenantDomainNormalizer;
|
||||
use Closure;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class BootstrapTenantRequest extends FormRequest
|
||||
{
|
||||
protected bool $hasInvalidDomain = false;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$rawDomain = $this->route('dominio');
|
||||
$normalizedDomain = TenantDomainNormalizer::normalize($rawDomain);
|
||||
|
||||
$this->hasInvalidDomain = TenantDomainNormalizer::hasValue($rawDomain)
|
||||
&& $normalizedDomain === null;
|
||||
|
||||
$this->merge([
|
||||
'dominio' => $normalizedDomain,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'dominio' => [
|
||||
'bail',
|
||||
function (string $attribute, mixed $value, Closure $fail): void {
|
||||
if ($this->hasInvalidDomain) {
|
||||
$fail("The {$attribute} field must contain a valid domain or URL.");
|
||||
}
|
||||
},
|
||||
'required',
|
||||
'string',
|
||||
'max:255',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,33 @@
|
||||
namespace App\Domains\Tenant\Requests;
|
||||
|
||||
use App\Domains\Prop\Support\PropRules;
|
||||
use App\Domains\Tenant\Support\TenantDomainNormalizer;
|
||||
use Closure;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreTenantRequest extends FormRequest
|
||||
{
|
||||
protected bool $hasInvalidDomain = false;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$rawDomain = $this->input('dominio');
|
||||
$normalizedDomain = TenantDomainNormalizer::normalize($rawDomain);
|
||||
|
||||
$this->hasInvalidDomain = TenantDomainNormalizer::hasValue($rawDomain)
|
||||
&& $normalizedDomain === null;
|
||||
|
||||
$this->merge([
|
||||
'dominio' => $normalizedDomain,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
@@ -21,7 +38,18 @@ class StoreTenantRequest extends FormRequest
|
||||
return [
|
||||
'codigo' => ['required', 'string', 'max:255', Rule::unique('tenants', 'codigo')],
|
||||
'nombre' => ['required', 'string', 'max:255'],
|
||||
'dominio' => ['nullable', 'string', 'max:255'],
|
||||
'dominio' => [
|
||||
'bail',
|
||||
function (string $attribute, mixed $value, Closure $fail): void {
|
||||
if ($this->hasInvalidDomain) {
|
||||
$fail("The {$attribute} field must contain a valid domain or URL.");
|
||||
}
|
||||
},
|
||||
'nullable',
|
||||
'string',
|
||||
'max:255',
|
||||
Rule::unique('tenants', 'dominio'),
|
||||
],
|
||||
...PropRules::sync(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -4,16 +4,33 @@ namespace App\Domains\Tenant\Requests;
|
||||
|
||||
use App\Domains\Prop\Support\PropRules;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Tenant\Support\TenantDomainNormalizer;
|
||||
use Closure;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateTenantRequest extends FormRequest
|
||||
{
|
||||
protected bool $hasInvalidDomain = false;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$rawDomain = $this->input('dominio');
|
||||
$normalizedDomain = TenantDomainNormalizer::normalize($rawDomain);
|
||||
|
||||
$this->hasInvalidDomain = TenantDomainNormalizer::hasValue($rawDomain)
|
||||
&& $normalizedDomain === null;
|
||||
|
||||
$this->merge([
|
||||
'dominio' => $normalizedDomain,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
@@ -30,7 +47,18 @@ class UpdateTenantRequest extends FormRequest
|
||||
Rule::unique('tenants', 'codigo')->ignore($tenant?->id),
|
||||
],
|
||||
'nombre' => ['required', 'string', 'max:255'],
|
||||
'dominio' => ['nullable', 'string', 'max:255'],
|
||||
'dominio' => [
|
||||
'bail',
|
||||
function (string $attribute, mixed $value, Closure $fail): void {
|
||||
if ($this->hasInvalidDomain) {
|
||||
$fail("The {$attribute} field must contain a valid domain or URL.");
|
||||
}
|
||||
},
|
||||
'nullable',
|
||||
'string',
|
||||
'max:255',
|
||||
Rule::unique('tenants', 'dominio')->ignore($tenant?->id),
|
||||
],
|
||||
...PropRules::sync(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ class TenantResource extends JsonResource
|
||||
'codigo' => $this->codigo,
|
||||
'nombre' => $this->nombre,
|
||||
'dominio' => $this->dominio,
|
||||
'props' => PropValueResource::collapsedFromPropable($this->resource),
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
'props' => PropValueResource::collapsedFromPropable($this->resource)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
36
app/Domains/Tenant/Support/TenantDomainNormalizer.php
Normal file
36
app/Domains/Tenant/Support/TenantDomainNormalizer.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Tenant\Support;
|
||||
|
||||
class TenantDomainNormalizer
|
||||
{
|
||||
public static function hasValue(mixed $domain): bool
|
||||
{
|
||||
return is_string($domain) && trim(urldecode($domain)) !== '';
|
||||
}
|
||||
|
||||
public static function normalize(mixed $domain): ?string
|
||||
{
|
||||
if (! is_string($domain)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decodedDomain = trim(urldecode($domain));
|
||||
|
||||
if ($decodedDomain === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$candidate = str_contains($decodedDomain, '://')
|
||||
? $decodedDomain
|
||||
: "//{$decodedDomain}";
|
||||
|
||||
$host = parse_url($candidate, PHP_URL_HOST);
|
||||
|
||||
if (! is_string($host) || $host === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return strtolower($host);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
<?php
|
||||
|
||||
use App\Domains\Tenant\Controllers\BootstrapTenantController;
|
||||
use App\Domains\Tenant\Controllers\TenantController;
|
||||
use App\Domains\Tenant\Controllers\TenantPropController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('tenants/bootstrap/{dominio}', BootstrapTenantController::class)
|
||||
->where('dominio', '.*');
|
||||
|
||||
Route::apiResource('tenants', TenantController::class);
|
||||
Route::apiResource('tenant-props', TenantPropController::class)
|
||||
->parameters(['tenant-props' => 'tenantProp']);
|
||||
|
||||
Reference in New Issue
Block a user