119 lines
4.2 KiB
PHP
119 lines
4.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\Baselines\Compare;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
final class CompareSubjectResult
|
|
{
|
|
/**
|
|
* @param array<string, mixed> $diagnostics
|
|
*/
|
|
public function __construct(
|
|
public readonly CompareSubjectIdentity $subjectIdentity,
|
|
public readonly CompareSubjectProjection $projection,
|
|
public readonly string $baselineAvailability,
|
|
public readonly string $currentStateAvailability,
|
|
public readonly CompareState $compareState,
|
|
public readonly string $trustLevel,
|
|
public readonly string $evidenceQuality,
|
|
public readonly ?string $severityRecommendation = null,
|
|
public readonly ?CompareFindingCandidate $findingCandidate = null,
|
|
public readonly array $diagnostics = [],
|
|
) {
|
|
if (trim($this->baselineAvailability) === '' || trim($this->currentStateAvailability) === '' || trim($this->trustLevel) === '' || trim($this->evidenceQuality) === '') {
|
|
throw new InvalidArgumentException('Compare subject results require non-empty availability, trust level, and evidence quality values.');
|
|
}
|
|
|
|
if ($this->compareState === CompareState::Drift && ! $this->findingCandidate instanceof CompareFindingCandidate) {
|
|
throw new InvalidArgumentException('Drift compare subject results require a finding candidate.');
|
|
}
|
|
|
|
if ($this->compareState !== CompareState::Drift && $this->findingCandidate instanceof CompareFindingCandidate) {
|
|
throw new InvalidArgumentException('Only drift compare subject results may carry a finding candidate.');
|
|
}
|
|
}
|
|
|
|
public function hasFindingCandidate(): bool
|
|
{
|
|
return $this->findingCandidate instanceof CompareFindingCandidate;
|
|
}
|
|
|
|
public function isGapState(): bool
|
|
{
|
|
return in_array($this->compareState, [
|
|
CompareState::Unsupported,
|
|
CompareState::Incomplete,
|
|
CompareState::Ambiguous,
|
|
CompareState::Failed,
|
|
], true);
|
|
}
|
|
|
|
public function gapReasonCode(): ?string
|
|
{
|
|
$reasonCode = $this->diagnostics['reason_code'] ?? null;
|
|
|
|
return is_string($reasonCode) && trim($reasonCode) !== '' ? trim($reasonCode) : null;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
public function gapRecord(): ?array
|
|
{
|
|
$gapRecord = $this->diagnostics['gap_record'] ?? null;
|
|
|
|
return is_array($gapRecord) ? $gapRecord : null;
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* subject_identity: array{
|
|
* domain_key: string,
|
|
* subject_class: string,
|
|
* subject_type_key: string,
|
|
* external_subject_id: ?string,
|
|
* subject_key: string
|
|
* },
|
|
* projection: array{
|
|
* platform_subject_class: string,
|
|
* domain_key: string,
|
|
* subject_type_key: string,
|
|
* operator_label: string,
|
|
* summary_kind: ?string,
|
|
* additional_labels: array<string, string>
|
|
* },
|
|
* baseline_availability: string,
|
|
* current_state_availability: string,
|
|
* compare_state: string,
|
|
* trust_level: string,
|
|
* evidence_quality: string,
|
|
* severity_recommendation: ?string,
|
|
* finding_candidate: ?array{
|
|
* change_type: string,
|
|
* severity: string,
|
|
* fingerprint_basis: array<string, mixed>,
|
|
* evidence_payload: array<string, mixed>,
|
|
* auto_close_eligible: bool
|
|
* },
|
|
* diagnostics: array<string, mixed>
|
|
* }
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return [
|
|
'subject_identity' => $this->subjectIdentity->toArray(),
|
|
'projection' => $this->projection->toArray(),
|
|
'baseline_availability' => $this->baselineAvailability,
|
|
'current_state_availability' => $this->currentStateAvailability,
|
|
'compare_state' => $this->compareState->value,
|
|
'trust_level' => $this->trustLevel,
|
|
'evidence_quality' => $this->evidenceQuality,
|
|
'severity_recommendation' => $this->severityRecommendation,
|
|
'finding_candidate' => $this->findingCandidate?->toArray(),
|
|
'diagnostics' => $this->diagnostics,
|
|
];
|
|
}
|
|
} |