mirror of
https://github.com/Kovah/LinkAce.git
synced 2025-01-17 13:18:21 +01:00
Add migration of existing revisions (#467)
This commit is contained in:
parent
65298fe9aa
commit
cce410eac3
@ -166,4 +166,15 @@ return [
|
||||
*/
|
||||
|
||||
'console' => env('AUDIT_CONSOLE_EVENTS', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Revision Migration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Whether the revisions table should be deleted after successful migration.
|
||||
|
|
||||
*/
|
||||
|
||||
'delete_revisions_table' => true,
|
||||
];
|
||||
|
@ -1,43 +1,124 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Link;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use OwenIt\Auditing\Models\Audit;
|
||||
|
||||
class CreateAuditsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('audits', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('user_type')->nullable();
|
||||
$table->unsignedBigInteger('user_id')->nullable();
|
||||
$table->string('event');
|
||||
$table->morphs('auditable');
|
||||
$table->text('old_values')->nullable();
|
||||
$table->text('new_values')->nullable();
|
||||
$table->text('url')->nullable();
|
||||
$table->ipAddress('ip_address')->nullable();
|
||||
$table->string('user_agent', 1023)->nullable();
|
||||
$table->string('tags')->nullable();
|
||||
$table->timestamps();
|
||||
if (!Schema::hasTable('audits')) {
|
||||
Schema::create('audits', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('user_type')->nullable();
|
||||
$table->unsignedBigInteger('user_id')->nullable();
|
||||
$table->string('event');
|
||||
$table->morphs('auditable');
|
||||
$table->text('old_values')->nullable();
|
||||
$table->text('new_values')->nullable();
|
||||
$table->text('url')->nullable();
|
||||
$table->ipAddress()->nullable();
|
||||
$table->string('user_agent', 1023)->nullable();
|
||||
$table->string('tags')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['user_id', 'user_type']);
|
||||
});
|
||||
$table->index(['user_id', 'user_type']);
|
||||
});
|
||||
}
|
||||
|
||||
$this->migrateExistingRevisions();
|
||||
|
||||
if (config('audit.delete_revisions_table')) {
|
||||
Schema::drop('revisions');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
public function down(): void
|
||||
{
|
||||
Schema::drop('audits');
|
||||
}
|
||||
|
||||
protected function migrateExistingRevisions(): void
|
||||
{
|
||||
$entries = DB::table('revisions')->orderBy('created_at')->get();
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$newAudit = new Audit();
|
||||
$newAudit->user_type = $entry->user_id ? User::class : null;
|
||||
$newAudit->user_id = $entry->user_id ?? null;
|
||||
$newAudit->auditable_type = $entry->revisionable_type;
|
||||
$newAudit->auditable_id = $entry->revisionable_id;
|
||||
$newAudit->url = null;
|
||||
$newAudit->ip_address = null;
|
||||
$newAudit->user_agent = null;
|
||||
$newAudit->tags = null;
|
||||
$newAudit->created_at = $entry->created_at;
|
||||
$newAudit->updated_at = $entry->updated_at;
|
||||
|
||||
$newAudit->event = $this->processAuditEvent($entry);
|
||||
|
||||
[$newAudit->old_values, $newAudit->new_values] = match ($newAudit->event) {
|
||||
Link::AUDIT_RELATION_EVENT => $this->buildModelRelationValues($entry),
|
||||
'deleted' => $this->buildDeletionValues($entry),
|
||||
'restored' => $this->buildRestoreValues($entry),
|
||||
default => $this->buildValues($entry),
|
||||
};
|
||||
|
||||
$newAudit->save(['timestamps' => false]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processAuditEvent($entry): string
|
||||
{
|
||||
if (in_array($entry->key, [Link::AUDIT_TAGS_NAME, Link::AUDIT_LISTS_NAME])) {
|
||||
return Link::AUDIT_RELATION_EVENT;
|
||||
}
|
||||
|
||||
if ($entry->key === 'deleted_at' && $entry->old_value === null) {
|
||||
return 'deleted';
|
||||
}
|
||||
|
||||
if ($entry->key === 'deleted_at' && $entry->new_value === null) {
|
||||
return 'restored';
|
||||
}
|
||||
|
||||
return 'updated';
|
||||
}
|
||||
|
||||
protected function buildValues($entry): array
|
||||
{
|
||||
$old = $entry->old_value ? [$entry->key => $entry->old_value] : [];
|
||||
$new = $entry->new_value ? [$entry->key => $entry->new_value] : [];
|
||||
|
||||
return [$old, $new];
|
||||
}
|
||||
|
||||
protected function buildModelRelationValues($entry): array
|
||||
{
|
||||
$old = $entry->old_value ? [$entry->key => array_map('intval', explode(',', $entry->old_value))] : [];
|
||||
$new = $entry->new_value ? [$entry->key => array_map('intval', explode(',', $entry->new_value))] : [];
|
||||
|
||||
return [$old, $new];
|
||||
}
|
||||
|
||||
protected function buildDeletionValues($entry): array
|
||||
{
|
||||
$old = Link::withTrashed()->find($entry->revisionable_id) ?: [];
|
||||
$new = [];
|
||||
|
||||
return [$old, $new];
|
||||
}
|
||||
|
||||
protected function buildRestoreValues($entry): array
|
||||
{
|
||||
$old = [];
|
||||
$new = Link::withTrashed()->find($entry->revisionable_id) ?: [];
|
||||
|
||||
return [$old, $new];
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,9 @@
|
||||
<testsuite name="Helper">
|
||||
<directory suffix="Test.php">./tests/Helper</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Migrations">
|
||||
<directory suffix="Test.php">./tests/Migrations</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Models">
|
||||
<directory suffix="Test.php">./tests/Models</directory>
|
||||
</testsuite>
|
||||
|
173
tests/Migrations/RevisionsToAuditsMigrationTest.php
Normal file
173
tests/Migrations/RevisionsToAuditsMigrationTest.php
Normal file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Migrations;
|
||||
|
||||
use App\Models\Link;
|
||||
use App\Models\User;
|
||||
use CreateAuditsTable;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use OwenIt\Auditing\Models\Audit;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RevisionsToAuditsMigrationTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function beforeRefreshingDatabase(): void
|
||||
{
|
||||
config()->set('audit.delete_revisions_table', false);
|
||||
}
|
||||
|
||||
public function testRevisionMigration(): void
|
||||
{
|
||||
$date = now()->subDay()->startOfSecond();
|
||||
|
||||
DB::table('revisions')->insert([
|
||||
'revisionable_type' => Link::class,
|
||||
'revisionable_id' => 5,
|
||||
'user_id' => null,
|
||||
'key' => 'url',
|
||||
'old_value' => 'https://example.com',
|
||||
'new_value' => 'https://test.com',
|
||||
'created_at' => $date,
|
||||
'updated_at' => $date,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseCount('revisions', 1);
|
||||
|
||||
$migrator = new CreateAuditsTable();
|
||||
$migrator->up();
|
||||
|
||||
$audit = Audit::first();
|
||||
|
||||
$this->assertNull($audit->user_type);
|
||||
$this->assertNull($audit->user_id);
|
||||
$this->assertEquals('https://example.com', $audit->getModified()['url']['old']);
|
||||
$this->assertEquals('https://test.com', $audit->getModified()['url']['new']);
|
||||
$this->assertTrue(
|
||||
$date->equalTo($audit->created_at),
|
||||
sprintf('Created at date should be %s but is %s', $date, $audit->created_at)
|
||||
);
|
||||
$this->assertTrue(
|
||||
$date->equalTo($audit->updated_at),
|
||||
sprintf('Created at date should be %s but is %s', $date, $audit->updated_at)
|
||||
);
|
||||
}
|
||||
|
||||
public function testRevisionMigrationWithUser(): void
|
||||
{
|
||||
DB::table('revisions')->insert([
|
||||
'revisionable_type' => Link::class,
|
||||
'revisionable_id' => 5,
|
||||
'user_id' => 7,
|
||||
'key' => 'url',
|
||||
'old_value' => 'https://example.com',
|
||||
'new_value' => 'https://test.com',
|
||||
'created_at' => now()->subDay(),
|
||||
'updated_at' => now()->subDay(),
|
||||
]);
|
||||
|
||||
$migrator = new CreateAuditsTable();
|
||||
$migrator->up();
|
||||
|
||||
$audit = Audit::first();
|
||||
|
||||
$this->assertEquals(User::class, $audit->user_type);
|
||||
$this->assertEquals(7, $audit->user_id);
|
||||
}
|
||||
|
||||
public function testRevisionMigrationWithoutOldValue(): void
|
||||
{
|
||||
DB::table('revisions')->insert([
|
||||
'revisionable_type' => Link::class,
|
||||
'revisionable_id' => 5,
|
||||
'user_id' => 7,
|
||||
'key' => 'url',
|
||||
'old_value' => null,
|
||||
'new_value' => 'https://test.com',
|
||||
'created_at' => now()->subDay(),
|
||||
'updated_at' => now()->subDay(),
|
||||
]);
|
||||
|
||||
$migrator = new CreateAuditsTable();
|
||||
$migrator->up();
|
||||
|
||||
$audit = Audit::first();
|
||||
|
||||
$this->assertArrayNotHasKey('old', $audit->getModified()['url']);
|
||||
$this->assertArrayHasKey('new', $audit->getModified()['url']);
|
||||
}
|
||||
|
||||
public function testRevisionMigrationWithRelations(): void
|
||||
{
|
||||
DB::table('revisions')->insert([
|
||||
'revisionable_type' => Link::class,
|
||||
'revisionable_id' => 5,
|
||||
'user_id' => 7,
|
||||
'key' => 'revtags',
|
||||
'old_value' => null,
|
||||
'new_value' => '4,8,12',
|
||||
'created_at' => now()->subDay(),
|
||||
'updated_at' => now()->subDay(),
|
||||
]);
|
||||
|
||||
$migrator = new CreateAuditsTable();
|
||||
$migrator->up();
|
||||
|
||||
$audit = Audit::first();
|
||||
|
||||
$this->assertArrayHasKey('revtags', $audit->getModified());
|
||||
$this->assertEquals([4, 8, 12], $audit->getModified()['revtags']['new']);
|
||||
}
|
||||
|
||||
public function testRevisionMigrationWithDeletion(): void
|
||||
{
|
||||
Link::factory()->createQuietly(['url' => 'https://deleted.com']);
|
||||
|
||||
DB::table('revisions')->insert([
|
||||
'revisionable_type' => Link::class,
|
||||
'revisionable_id' => 1,
|
||||
'user_id' => 7,
|
||||
'key' => 'deleted_at',
|
||||
'old_value' => null,
|
||||
'new_value' => '2020-07-02 09:23:34',
|
||||
'created_at' => now()->subDay(),
|
||||
'updated_at' => now()->subDay(),
|
||||
]);
|
||||
|
||||
$migrator = new CreateAuditsTable();
|
||||
$migrator->up();
|
||||
|
||||
$audit = Audit::first();
|
||||
|
||||
$this->assertEquals('deleted', $audit->event);
|
||||
$this->assertArrayHasKey('url', $audit->getModified());
|
||||
$this->assertEquals('https://deleted.com', $audit->getModified()['url']['old']);
|
||||
}
|
||||
|
||||
public function testRevisionMigrationWithRestore(): void
|
||||
{
|
||||
Link::factory()->createQuietly(['url' => 'https://restored.com']);
|
||||
|
||||
DB::table('revisions')->insert([
|
||||
'revisionable_type' => Link::class,
|
||||
'revisionable_id' => 1,
|
||||
'user_id' => 7,
|
||||
'key' => 'deleted_at',
|
||||
'old_value' => '2020-07-02 09:23:34',
|
||||
'new_value' => null,
|
||||
'created_at' => now()->subDay(),
|
||||
'updated_at' => now()->subDay(),
|
||||
]);
|
||||
|
||||
$migrator = new CreateAuditsTable();
|
||||
$migrator->up();
|
||||
|
||||
$audit = Audit::first();
|
||||
|
||||
$this->assertEquals('restored', $audit->event);
|
||||
$this->assertArrayHasKey('url', $audit->getModified());
|
||||
$this->assertEquals('https://restored.com', $audit->getModified()['url']['new']);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user