Tidy automation backend: run duration, folded migrations, imports
- Add AutomationRun::durationInMilliseconds() as the single source of truth for the Invocations list and metrics, replacing the duplicated inline diff. - Fold the variables and root_run_id columns into their create migrations (this branch isn't in production) and drop the standalone alters. - Import Illuminate\Http\Response (aliased) instead of referencing it inline.
This commit is contained in:
parent
2bd2e72656
commit
730cb9d156
9 changed files with 37 additions and 64 deletions
|
|
@ -43,8 +43,8 @@ public function __invoke(Automation $automation, CarbonInterface $start, CarbonI
|
|||
$successRate = $finished > 0 ? (int) round($completed->count() / $finished * 100) : null;
|
||||
|
||||
$durations = $completed
|
||||
->filter(fn ($run) => $run->started_at !== null && $run->finished_at !== null)
|
||||
->map(fn ($run) => $run->started_at->diffInMilliseconds($run->finished_at));
|
||||
->map(fn ($run) => $run->durationInMilliseconds())
|
||||
->filter(fn ($ms) => $ms !== null);
|
||||
$avgDurationMs = $durations->isNotEmpty() ? (int) round($durations->avg()) : null;
|
||||
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
use App\Models\AutomationRun;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Response as HttpResponse;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
|
|
@ -183,7 +184,7 @@ public function retryRun(
|
|||
RetryRunFromNode $retry,
|
||||
Automation $automation,
|
||||
AutomationRun $run,
|
||||
): \Illuminate\Http\Response {
|
||||
): HttpResponse {
|
||||
$this->authorize('update', $automation);
|
||||
abort_unless($run->automation_id === $automation->id, 404);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,16 +18,12 @@ class AutomationInvocationResource extends JsonResource
|
|||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
$durationMs = $this->started_at !== null && $this->finished_at !== null
|
||||
? $this->started_at->diffInMilliseconds($this->finished_at)
|
||||
: null;
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'status' => $this->status->value,
|
||||
'is_manual' => (bool) $this->is_manual,
|
||||
'node_run_count' => (int) ($this->node_runs_count ?? 0),
|
||||
'duration_ms' => $durationMs,
|
||||
'duration_ms' => $this->durationInMilliseconds(),
|
||||
'error_message' => is_array($this->error) ? ($this->error['message'] ?? null) : $this->error,
|
||||
'created_at' => $this->created_at,
|
||||
'started_at' => $this->started_at,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,19 @@ public function rootId(): string
|
|||
return $this->root_run_id ?? $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wall-clock execution time, or null while the run hasn't both started and
|
||||
* finished. Single source of truth for the Invocations list and metrics.
|
||||
*/
|
||||
public function durationInMilliseconds(): ?int
|
||||
{
|
||||
if ($this->started_at === null || $this->finished_at === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $this->started_at->diffInMilliseconds($this->finished_at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Context for template (`{{ ... }}`) resolution: the run context plus the
|
||||
* automation's workflow variables, merged in-memory. Variables are NEVER
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public function up(): void
|
|||
$table->string('trigger_type')->nullable();
|
||||
$table->json('nodes')->nullable();
|
||||
$table->json('connections')->nullable();
|
||||
$table->json('variables')->nullable();
|
||||
$table->timestamp('activated_at')->nullable();
|
||||
$table->timestamp('paused_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ public function up(): void
|
|||
Schema::create('automation_runs', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignUuid('automation_id')->constrained('automations')->cascadeOnDelete();
|
||||
$table->foreignUuid('root_run_id')->nullable();
|
||||
$table->foreignUuid('trigger_item_id')->nullable()->constrained('automation_trigger_items')->nullOnDelete();
|
||||
$table->string('current_node_id')->nullable();
|
||||
$table->string('status')->default('pending');
|
||||
|
|
@ -32,6 +33,10 @@ public function up(): void
|
|||
$table->index(['automation_id', 'status']);
|
||||
$table->index(['status', 'next_action_at']);
|
||||
});
|
||||
|
||||
Schema::table('automation_runs', function (Blueprint $table) {
|
||||
$table->foreign('root_run_id')->references('id')->on('automation_runs')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
<?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::table('automations', function (Blueprint $table) {
|
||||
$table->json('variables')->nullable()->after('connections');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('automations', function (Blueprint $table) {
|
||||
$table->dropColumn('variables');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('automation_runs', function (Blueprint $table) {
|
||||
// Links every run forked by a fan-out back to the run that started the
|
||||
// execution, so the editor test panel can aggregate all branches of a
|
||||
// single test under one root. Null on the root run itself.
|
||||
$table->foreignUuid('root_run_id')->nullable()->after('automation_id')
|
||||
->constrained('automation_runs')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('automation_runs', function (Blueprint $table) {
|
||||
$table->dropConstrainedForeignId('root_run_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -38,6 +38,19 @@
|
|||
expect($automation->trigger_type)->toBeNull();
|
||||
});
|
||||
|
||||
it('computes run duration only once both timestamps are set', function () {
|
||||
$start = now()->startOfSecond();
|
||||
|
||||
$running = AutomationRun::factory()->make(['started_at' => $start, 'finished_at' => null]);
|
||||
$finished = AutomationRun::factory()->make([
|
||||
'started_at' => $start,
|
||||
'finished_at' => $start->copy()->addSeconds(2),
|
||||
]);
|
||||
|
||||
expect($running->durationInMilliseconds())->toBeNull()
|
||||
->and($finished->durationInMilliseconds())->toBe(2000);
|
||||
});
|
||||
|
||||
it('relates trigger items, runs and node runs', function () {
|
||||
$automation = Automation::factory()->create();
|
||||
$item = AutomationTriggerItem::factory()->for($automation)->create();
|
||||
|
|
|
|||
Loading…
Reference in a new issue