Skip to content

Commit

Permalink
Log Update results to database (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
SniperSister authored Jan 18, 2025
1 parent ecdd86c commit 1089b02
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 3 deletions.
24 changes: 24 additions & 0 deletions app/Exceptions/UpdateException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Exceptions;

class UpdateException extends \Exception
{
public readonly string $updateStep;

public function __construct(
string $updateStep,
string $message,
int $code = 0,
?\Throwable $previous = null
) {
$this->updateStep = $updateStep;

parent::__construct($message, $code, $previous);
}

public function getStep(): string
{
return $this->updateStep;
}
}
43 changes: 40 additions & 3 deletions app/Jobs/UpdateSite.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace App\Jobs;

use App\Exceptions\UpdateException;
use App\Models\Site;
use App\Models\Update;
use App\RemoteSite\Connection;
use App\RemoteSite\Responses\PrepareUpdate;
use Illuminate\Contracts\Queue\ShouldQueue;
Expand Down Expand Up @@ -65,16 +67,31 @@ public function handle(): void
$prepareResult = $connection->prepareUpdate(["targetVersion" => $this->targetVersion]);

// Perform the actual extraction
$this->performExtraction($prepareResult);
try {
$this->performExtraction($prepareResult);
} catch (\Throwable $e) {
throw new UpdateException(
'extract',
$e->getMessage(),
(int) $e->getCode(),
$e instanceof \Exception ? $e : null
);
}

// Run the postupdate steps
if (!$connection->finalizeUpdate()->success) {
throw new \Exception("Update for site failed in postprocessing: " . $this->site->id);
throw new UpdateException(
"finalize",
"Update for site failed in postprocessing: " . $this->site->id
);
}

// Compare codes
if ($this->site->getFrontendStatus() !== $this->preUpdateCode) {
throw new \Exception("Status code has changed after update for site: " . $this->site->id);
throw new UpdateException(
"afterUpdate",
"Status code has changed after update for site: " . $this->site->id
);
}
}

Expand Down Expand Up @@ -121,5 +138,25 @@ protected function performExtraction(PrepareUpdate $prepareResult): void
"task" => "finalizeUpdate"
]
);

// Done, log successful update!
$this->site->updates()->create([
'old_version' => $this->site->cms_version,
'new_version' => $this->targetVersion,
'result' => true
]);
}

public function failed(\Exception $exception): void
{
// We log any issues during the update to the DB
$this->site->updates()->create([
'old_version' => $this->site->cms_version,
'new_version' => $this->targetVersion,
'result' => false,
'failed_step' => $exception instanceof UpdateException ? $exception->getStep() : null,
'failed_message' => $exception->getMessage(),
'failed_trace' => $exception->getTraceAsString()
]);
}
}
9 changes: 9 additions & 0 deletions app/Models/Site.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\RemoteSite\Connection;
use GuzzleHttp\Client;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\App;

class Site extends Model
Expand Down Expand Up @@ -55,4 +56,12 @@ public function getFrontendStatus(): int

return $httpClient->get($this->url)->getStatusCode();
}

/**
* @return HasMany<Update, $this>
*/
public function updates(): HasMany
{
return $this->hasMany(Update::class, 'site_id', 'id');
}
}
26 changes: 26 additions & 0 deletions app/Models/Update.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Update extends Model
{
protected $fillable = [
'old_version',
'new_version',
'result',
'failed_step',
'failed_message',
'failed_trace'
];

protected function casts(): array
{
return [
'result' => 'bool'
];
}
}
33 changes: 33 additions & 0 deletions database/migrations/2025_01_18_114039_add_updates_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('updates', function (Blueprint $table) {
$table->id();
$table->foreignId('site_id');
$table->string('old_version');
$table->string('new_version');
$table->boolean('result');
$table->string('failed_step')->nullable();
$table->text('failed_message')->nullable();
$table->text('failed_trace')->nullable();
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('updates');
}
};
52 changes: 52 additions & 0 deletions tests/Unit/Jobs/UpdateSiteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@

namespace Tests\Unit\Jobs;

use App\Exceptions\UpdateException;
use App\Jobs\UpdateSite;
use App\Models\Site;
use App\Models\Update;
use App\RemoteSite\Connection;
use App\RemoteSite\Responses\FinalizeUpdate;
use App\RemoteSite\Responses\GetUpdate;
use App\RemoteSite\Responses\HealthCheck;
use App\RemoteSite\Responses\PrepareUpdate;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
use Tests\TestCase;

class UpdateSiteTest extends TestCase
{
use RefreshDatabase;

public function testJobQuitsIfTargetVersionIsEqualOrNewer()
{
$site = $this->getSiteMock(['checkHealth' => $this->getHealthCheckMock(["cms_version" => "1.0.0"])]);
Expand Down Expand Up @@ -98,6 +103,52 @@ public function testJobFailsIfFinalizeUpdateReturnsFalse()
$object->handle();
}

public function testJobWritesFailLogOnFailing()
{
$siteMock = $this->getMockBuilder(Site::class)
->onlyMethods(['getConnectionAttribute', 'getFrontendStatus'])
->getMock();

$siteMock->id = 1;
$siteMock->url = "http://example.org";
$siteMock->cms_version = "1.0.0";

$object = new UpdateSite($siteMock, "1.0.1");
$object->failed(new UpdateException("finalize", "This is a test"));

$failedUpdate = Update::first();

$this->assertEquals(false, $failedUpdate->result);
$this->assertEquals("1.0.0", $failedUpdate->old_version);
$this->assertEquals("1.0.1", $failedUpdate->new_version);
$this->assertEquals("finalize", $failedUpdate->failed_step);
$this->assertEquals("This is a test", $failedUpdate->failed_message);
$this->assertNotEmpty($failedUpdate->failed_trace);
}

public function testJobWritesSuccessLogForSuccessfulJobs()
{
$site = $this->getSiteMock(
[
'checkHealth' => $this->getHealthCheckMock(),
'getUpdate' => $this->getGetUpdateMock("1.0.1"),
'prepareUpdate' => $this->getPrepareUpdateMock(),
'finalizeUpdate' => $this->getFinalizeUpdateMock(true)
]
);

App::bind(Connection::class, fn () => $this->getSuccessfulExtractionMock());

$object = new UpdateSite($site, "1.0.1");
$object->handle();

$updateRow = Update::first();

$this->assertEquals(true, $updateRow->result);
$this->assertEquals("1.0.0", $updateRow->old_version);
$this->assertEquals("1.0.1", $updateRow->new_version);
}

protected function getSiteMock(array $responses)
{
$connectionMock = $this->getMockBuilder(Connection::class)
Expand All @@ -120,6 +171,7 @@ function ($method) use ($responses) {
$siteMock->method('getFrontendStatus')->willReturn(200);
$siteMock->id = 1;
$siteMock->url = "http://example.org";
$siteMock->cms_version = "1.0.0";

return $siteMock;
}
Expand Down

0 comments on commit 1089b02

Please sign in to comment.