CluPilotCloud/app/Models/Incident.php

104 lines
3.1 KiB
PHP

<?php
namespace App\Models;
use App\Models\Concerns\HasUuid;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* A disruption, written down.
*
* The point of the record is that it outlives the disruption. "Alle Dienste in
* Betrieb" the morning after an outage is true and useless to somebody who was
* locked out the evening before; what answers them is the timeline — when it
* was noticed, what it turned out to be, when it was fixed.
*/
class Incident extends Model
{
/** @use HasFactory<\Database\Factories\IncidentFactory> */
use HasFactory, HasUuid;
/** How the incident colours the components it touches. */
public const IMPACT_DEGRADED = 'degraded';
public const IMPACT_DOWN = 'down';
public const IMPACT_MAINTENANCE = 'maintenance';
/** The stages of an update, in the order they normally happen. */
public const STATUSES = ['investigating', 'identified', 'monitoring', 'resolved'];
protected $fillable = [
'title', 'impact', 'components', 'started_at', 'resolved_at', 'published_at', 'created_by',
];
protected function casts(): array
{
return [
'components' => 'array',
'started_at' => 'datetime',
'resolved_at' => 'datetime',
'published_at' => 'datetime',
];
}
public function updates(): HasMany
{
return $this->hasMany(IncidentUpdate::class)->orderByDesc('created_at')->orderByDesc('id');
}
/** Only what a visitor may see. Drafts are working notes, not statements. */
public function scopePublished(Builder $query): Builder
{
return $query->whereNotNull('published_at');
}
public function scopeOngoing(Builder $query): Builder
{
return $query->whereNull('resolved_at');
}
public function isResolved(): bool
{
return $this->resolved_at !== null;
}
public function isPublished(): bool
{
return $this->published_at !== null;
}
/**
* The stage the incident is at, taken from its newest update.
*
* Derived rather than stored: a stored column and an update log are two
* records of the same fact, and they drift the first time somebody posts
* an update without touching the column.
*/
public function stage(): string
{
if ($this->isResolved()) {
return 'resolved';
}
return $this->updates->first()?->status ?? 'investigating';
}
/**
* How long it lasted, in whole minutes; null while it is still running.
*
* Start first, end second. diffInMinutes is SIGNED — written the other way
* round it produced "Dauer -86 Minuten" on the public page, which is the
* kind of detail that costs a status page the only thing it has.
*/
public function durationMinutes(): ?int
{
return $this->resolved_at === null
? null
: (int) $this->started_at->diffInMinutes($this->resolved_at);
}
}