Files
Enelix-EMS/libs/StoerungsSnapshot.php
2026-09-27 15:41:15 +00:00

132 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Belevo\EnelixEMS;
use InvalidArgumentException;
final class StoerungsSnapshot
{
public const VERSION = '1.0';
private const SCHWEREGRADE = [
'critical' => 0,
'error' => 1,
'warning' => 2,
'info' => 3,
];
/**
* @param list<array<string, mixed>> $meldungen
* @return array<string, mixed>
*/
public static function erstellen(
string $installationID,
int $managerID,
string $rolle,
bool $managerAktiv,
array $meldungen,
?int $zeitpunkt = null
): array {
if (preg_match(
'/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i',
$installationID
) !== 1) {
throw new InvalidArgumentException('Die Installations-ID muss eine UUIDv4 sein.');
}
if ($managerID <= 0) {
throw new InvalidArgumentException('Die Manager-ID muss positiv sein.');
}
if (!in_array($rolle, ['standalone', 'main', 'submanager'], true)) {
throw new InvalidArgumentException('Die Managerrolle ist ungueltig.');
}
$eindeutig = [];
foreach ($meldungen as $meldung) {
$normalisiert = self::normalisiereMeldung($meldung);
$schluessel = $normalisiert['sourceId'] . "\0" . $normalisiert['code'];
$eindeutig[$schluessel] = $normalisiert;
}
$meldungen = array_values($eindeutig);
usort($meldungen, static function (array $links, array $rechts): int {
$schweregrad = self::SCHWEREGRADE[$links['severity']]
<=> self::SCHWEREGRADE[$rechts['severity']];
if ($schweregrad !== 0) {
return $schweregrad;
}
return [$links['sourceName'], $links['code']]
<=> [$rechts['sourceName'], $rechts['code']];
});
// Das Portal akzeptiert hoechstens 100 Meldungen; die hoechste Prioritaet bleibt erhalten.
$meldungen = array_slice($meldungen, 0, 100);
return [
'version' => self::VERSION,
'installationId' => strtolower($installationID),
'capturedAt' => gmdate('Y-m-d\TH:i:s\Z', $zeitpunkt ?? time()),
'manager' => [
'instanceId' => $managerID,
'role' => $rolle,
'active' => $managerAktiv,
],
'faults' => $meldungen,
];
}
/** @param array<string, mixed> $snapshot */
public static function zustandHash(array $snapshot): string
{
unset($snapshot['capturedAt']);
return hash('sha256', json_encode(
$snapshot,
JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES
));
}
/**
* @param array<string, mixed> $meldung
* @return array<string, string>
*/
private static function normalisiereMeldung(array $meldung): array
{
$felder = ['sourceType', 'sourceId', 'sourceName', 'code', 'severity', 'message'];
if (array_diff($felder, array_keys($meldung)) !== []
|| array_diff(array_keys($meldung), $felder) !== []
) {
throw new InvalidArgumentException('Eine Stoerungsmeldung hat ungueltige Felder.');
}
$normalisiert = [];
foreach ($felder as $feld) {
if (!is_string($meldung[$feld])) {
throw new InvalidArgumentException('Stoerungsfelder muessen Texte sein.');
}
$normalisiert[$feld] = trim((string) preg_replace('/\s+/', ' ', $meldung[$feld]));
}
if ($normalisiert['sourceType'] === ''
|| $normalisiert['sourceId'] === ''
|| $normalisiert['sourceName'] === ''
|| $normalisiert['code'] === ''
|| $normalisiert['message'] === ''
) {
throw new InvalidArgumentException('Stoerungsfelder duerfen nicht leer sein.');
}
if (!array_key_exists($normalisiert['severity'], self::SCHWEREGRADE)) {
throw new InvalidArgumentException('Der Stoerungsschweregrad ist ungueltig.');
}
if (strlen($normalisiert['sourceType']) > 40
|| strlen($normalisiert['sourceId']) > 120
|| strlen($normalisiert['sourceName']) > 160
|| strlen($normalisiert['code']) > 120
|| strlen($normalisiert['message']) > 500
) {
throw new InvalidArgumentException('Mindestens ein Stoerungsfeld ist zu lang.');
}
return $normalisiert;
}
}