all(); return $all[$destinationId] ?? new DestinationHealth(); } /** * @return array */ public function all(): array { $stored = $this->options->get(self::OPTION, []); $health = []; foreach (is_array($stored) ? $stored : [] as $id => $entry) { if (is_array($entry)) { $health[(string) $id] = DestinationHealth::fromArray($entry); } } return $health; } public function landed(string $destinationId, string $at): void { $this->write($destinationId, $this->of($destinationId)->landed($at)); } /** * Record an outage. * * **Only a retryable failure reaches here.** A terminal one is * Lead-specific and must leave `consecutive_failures` at zero — that is * the distinction ADR 0008 is built on, and the caller * ({@see PushWorker}) is where it is decided. */ public function failed(string $destinationId, string $error, string $at): void { $this->write($destinationId, $this->of($destinationId)->failed(DiagnosticSanitizer::message($error), $at)); } /** * Record a capture that was never enqueued, because this Destination's * type is not `ready` here. * * **This is the "recorded" half of "skipped and recorded, never * enqueued"** (#4). Without it a deactivated MailPoet or a lapsed licence * drops every push with no trace anywhere — the Optin keeps converting, * the Leads keep landing, and nothing says the pushes stopped, which is * precisely the support case Destination health exists for. * * A write on the capture path, and only in the degraded case: a * Destination whose type IS ready never reaches here. */ public function skipped(string $destinationId, string $at): void { $this->write($destinationId, $this->of($destinationId)->skipped($at)); } /** * Forget a Destination's health — what deleting the Destination does. * * Health keyed by an id nothing references is a row that never goes away * on a site that has reconfigured its integrations a few times. */ public function forget(string $destinationId): void { $all = $this->all(); unset($all[$destinationId]); $this->options->set(self::OPTION, array_map( static fn (DestinationHealth $health): array => $health->toArray(), $all )); } private function write(string $destinationId, DestinationHealth $health): void { $all = $this->all(); $all[$destinationId] = $health; $this->options->set(self::OPTION, array_map( static fn (DestinationHealth $entry): array => $entry->toArray(), $all )); } }