feat(infra): attach HasUuidV7CreatedAt trait to Brand, ApiClient, and ClientAuditLog models
This commit is contained in:
parent
6d63e68908
commit
5c49b2cc4f
9 changed files with 125 additions and 10 deletions
|
|
@ -14,6 +14,7 @@ use Infra\Casts\ClientNameCast;
|
|||
use Infra\Casts\ClientSecretCast;
|
||||
use Infra\Casts\UtcDateTimeCast;
|
||||
use Infra\Casts\UuidCast;
|
||||
use Infra\Database\Concerns\HasUuidV7CreatedAt;
|
||||
|
||||
/**
|
||||
* @property \Ramsey\Uuid\UuidInterface $id
|
||||
|
|
@ -22,6 +23,7 @@ use Infra\Casts\UuidCast;
|
|||
* @property \Application\Security\Models\ValueObjects\ClientIdentifier $identifier ENCRYPTED, Unique identifier that should not change
|
||||
* @property \Application\Security\Models\ValueObjects\ClientSecret $secret ENCRYPTED, api secret that can be regenerated
|
||||
* @property bool $is_active Defines if the client can be used in order to gain access to the system
|
||||
* @property \DateTimeImmutable $created_at
|
||||
* @property \DateTimeImmutable $updated_at
|
||||
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
|
||||
*
|
||||
|
|
@ -35,6 +37,7 @@ final class ApiClient extends Authenticatable
|
|||
use HasFactory;
|
||||
|
||||
use HasUuids;
|
||||
use HasUuidV7CreatedAt;
|
||||
use Notifiable;
|
||||
|
||||
protected static function newFactory(): ApiClientFactory
|
||||
|
|
@ -72,6 +75,7 @@ final class ApiClient extends Authenticatable
|
|||
'identifier' => ClientIdentifierCast::class,
|
||||
'secret' => ClientSecretCast::class,
|
||||
'is_active' => 'boolean',
|
||||
'created_at' => UtcDateTimeCast::class,
|
||||
'updated_at' => UtcDateTimeCast::class,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Infra\Casts\ArcadiaIdCast;
|
||||
use Infra\Casts\BrandCodeCast;
|
||||
use Infra\Casts\UtcDateTimeCast;
|
||||
use Infra\Database\Concerns\HasUuidV7CreatedAt;
|
||||
|
||||
/**
|
||||
* @phpcs:disable Generic.Files.LineLength.TooLong
|
||||
|
|
@ -18,6 +20,7 @@ use Infra\Casts\BrandCodeCast;
|
|||
* @property string $name The human readable brand name
|
||||
* @property \Domain\Models\ValueObjects\Brand\BrandCode $uwil_public_id A unique public id that is generated spefically for the UWIL eco system
|
||||
* @property \Domain\Models\ValueObjects\Brand\ArcadiaId $arcadia_id An unique external autoincrement id for the brand, it may be exposed but should not be used
|
||||
* @property \DateTimeImmutable $created_at
|
||||
*
|
||||
* @method static \Database\Factories\BrandFactory factory($count = null, $state = [])
|
||||
*
|
||||
|
|
@ -29,6 +32,7 @@ final class Brand extends Model
|
|||
use HasFactory;
|
||||
|
||||
use HasUuids;
|
||||
use HasUuidV7CreatedAt;
|
||||
|
||||
protected static function newFactory(): BrandFactory
|
||||
{
|
||||
|
|
@ -54,6 +58,7 @@ final class Brand extends Model
|
|||
return [
|
||||
'arcadia_id' => ArcadiaIdCast::class,
|
||||
'uwil_public_id' => BrandCodeCast::class,
|
||||
'created_at' => UtcDateTimeCast::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use Infra\Casts\ClientIdentifierCast;
|
|||
use Infra\Casts\SecurityAuditLogDetailsCast;
|
||||
use Infra\Casts\UtcDateTimeCast;
|
||||
use Infra\Casts\UuidCast;
|
||||
use Infra\Database\Concerns\HasUuidV7CreatedAt;
|
||||
|
||||
/**
|
||||
* @property \Ramsey\Uuid\UuidInterface $id
|
||||
|
|
@ -24,6 +25,7 @@ use Infra\Casts\UuidCast;
|
|||
final class ClientAuditLog extends Model
|
||||
{
|
||||
use HasUuids;
|
||||
use HasUuidV7CreatedAt;
|
||||
|
||||
protected $table = 'client_audit_logs';
|
||||
|
||||
|
|
|
|||
|
|
@ -34,12 +34,11 @@ final readonly class RecordClientSecurityAudit
|
|||
};
|
||||
|
||||
ClientAuditLog::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'id' => Uuid::uuid7($occurredAt)->toString(),
|
||||
'client_id' => $event->clientId->value,
|
||||
'client_identifier' => $event->clientIdentifier->value,
|
||||
'event_type' => $eventType,
|
||||
'details' => $details,
|
||||
'created_at' => $occurredAt,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ describe('ExportSecurityAuditLogsCommand', function (): void {
|
|||
'client_identifier' => $clientIdentifier,
|
||||
'event_type' => 'client_secret_generated',
|
||||
'details' => null,
|
||||
'created_at' => '2026-08-20 10:00:00',
|
||||
]);
|
||||
|
||||
// WHEN
|
||||
|
|
@ -80,7 +79,6 @@ describe('ExportSecurityAuditLogsCommand', function (): void {
|
|||
'client_identifier' => $clientIdentifier,
|
||||
'event_type' => 'client_status_toggled',
|
||||
'details' => ['is_active' => 'false'],
|
||||
'created_at' => '2026-08-18 15:30:00',
|
||||
]);
|
||||
|
||||
// WHEN
|
||||
|
|
|
|||
|
|
@ -27,30 +27,27 @@ describe('EloquentClientAuditLogDirectory Integration Tests', function (): void
|
|||
$clientIdentifier2 = 'arcadia_id_' . str_repeat('2', 32);
|
||||
|
||||
ClientAuditLog::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'id' => Uuid::uuid7(new \DateTimeImmutable('2026-08-01 10:00:00 UTC'))->toString(),
|
||||
'client_id' => $clientId1,
|
||||
'client_identifier' => $clientIdentifier1,
|
||||
'event_type' => 'client_secret_generated',
|
||||
'details' => null,
|
||||
'created_at' => '2026-08-01 10:00:00',
|
||||
]);
|
||||
|
||||
ClientAuditLog::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'id' => Uuid::uuid7(new \DateTimeImmutable('2026-08-10 14:00:00 UTC'))->toString(),
|
||||
'client_id' => $clientId1,
|
||||
'client_identifier' => $clientIdentifier1,
|
||||
'event_type' => 'client_status_toggled',
|
||||
'details' => ['is_active' => 'false'],
|
||||
'created_at' => '2026-08-10 14:00:00',
|
||||
]);
|
||||
|
||||
ClientAuditLog::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'id' => Uuid::uuid7(new \DateTimeImmutable('2026-08-15 12:00:00 UTC'))->toString(),
|
||||
'client_id' => $clientId2,
|
||||
'client_identifier' => $clientIdentifier2,
|
||||
'event_type' => 'client_secret_generated',
|
||||
'details' => null,
|
||||
'created_at' => '2026-08-15 12:00:00',
|
||||
]);
|
||||
|
||||
$directory = new EloquentClientAuditLogDirectory();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Integration\Infra\Database\Models;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Infra\Database\Models\ApiClient;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Tests\TestCase;
|
||||
|
||||
covers(ApiClient::class);
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
describe('ApiClient Eloquent Model & Factory', function (): void {
|
||||
it('creates and persists an ApiClient record with created_at automatically derived from UUIDv7', function (): void {
|
||||
// GIVEN
|
||||
$expectedDate = new \DateTimeImmutable('2026-08-21 08:30:00 UTC');
|
||||
$uuid7 = Uuid::uuid7($expectedDate)->toString();
|
||||
|
||||
// WHEN
|
||||
$client = ApiClient::factory()->create([
|
||||
'id' => $uuid7,
|
||||
]);
|
||||
|
||||
$retrieved = ApiClient::query()->find($client->id);
|
||||
|
||||
// THEN
|
||||
expect($retrieved)->not->toBeNull()
|
||||
->and($retrieved?->id->toString())->toBe($uuid7)
|
||||
->and($retrieved?->created_at)->toBeInstanceOf(\DateTimeImmutable::class)
|
||||
->and($retrieved?->created_at->format('Y-m-d H:i:s'))->toBe('2026-08-21 08:30:00');
|
||||
});
|
||||
});
|
||||
35
tests/Integration/Infra/Database/Models/BrandModelTest.php
Normal file
35
tests/Integration/Infra/Database/Models/BrandModelTest.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Integration\Infra\Database\Models;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Infra\Database\Models\Brand;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Tests\TestCase;
|
||||
|
||||
covers(Brand::class);
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
describe('Brand Eloquent Model & Factory', function (): void {
|
||||
it('creates and persists a Brand record with computed created_at from UUIDv7', function (): void {
|
||||
// GIVEN
|
||||
$expectedDate = new \DateTimeImmutable('2026-08-21 10:30:00 UTC');
|
||||
$uuid7 = Uuid::uuid7($expectedDate)->toString();
|
||||
|
||||
// WHEN
|
||||
$brand = Brand::factory()->create([
|
||||
'id' => $uuid7,
|
||||
]);
|
||||
|
||||
$retrieved = Brand::query()->find($brand->id);
|
||||
|
||||
// THEN
|
||||
expect($retrieved)->not->toBeNull()
|
||||
->and($retrieved?->id)->toBe($uuid7)
|
||||
->and($retrieved?->created_at)->toBeInstanceOf(\DateTimeImmutable::class)
|
||||
->and($retrieved?->created_at->format('Y-m-d H:i:s'))->toBe('2026-08-21 10:30:00');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Integration\Infra\Database\Models;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Infra\Database\Models\ClientAuditLog;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Tests\TestCase;
|
||||
|
||||
covers(ClientAuditLog::class);
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
describe('ClientAuditLog Eloquent Model', function (): void {
|
||||
it('creates and persists a ClientAuditLog record with computed created_at from UUIDv7', function (): void {
|
||||
// GIVEN
|
||||
$expectedDate = new \DateTimeImmutable('2026-08-20 14:15:00 UTC');
|
||||
$uuid7 = Uuid::uuid7($expectedDate)->toString();
|
||||
$clientId = Uuid::uuid7()->toString();
|
||||
|
||||
// WHEN
|
||||
$log = ClientAuditLog::query()->create([
|
||||
'id' => $uuid7,
|
||||
'client_id' => $clientId,
|
||||
'client_identifier' => 'arcadia_id_' . str_repeat('a', 32),
|
||||
'event_type' => 'client_secret_generated',
|
||||
'details' => null,
|
||||
]);
|
||||
|
||||
$retrieved = ClientAuditLog::query()->find($log->id);
|
||||
|
||||
// THEN
|
||||
expect($retrieved)->not->toBeNull()
|
||||
->and($retrieved?->id->toString())->toBe($uuid7)
|
||||
->and($retrieved?->created_at)->toBeInstanceOf(\DateTimeImmutable::class)
|
||||
->and($retrieved?->created_at?->format('Y-m-d H:i:s'))->toBe('2026-08-20 14:15:00');
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue