Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
DeliveryVerdict
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
2 / 2
10
100.00% covered (success)
100.00%
1 / 1
 of
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 forStatus
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3declare(strict_types=1);
4
5namespace LambdaTwelve\OneRecord\Client;
6
7use Psr\Http\Client\ClientExceptionInterface;
8use Psr\Http\Client\RequestExceptionInterface;
9use Throwable;
10
11/**
12 * What a host's outbox worker does after a failed delivery. Every host needs
13 * the same classification, so it lives here: transport failures and the
14 * statuses that say "not now" (5xx, 408, 429) are retried with backoff,
15 * whether they came from the notification itself or from fetching the token
16 * for it; every other HTTP answer, refused credentials, and a request PSR-18
17 * reports as unusable are final; anything else is a defect on the sending
18 * side and not worth a retry either.
19 *
20 * Pass whatever you caught. The SDK client wraps transport failures in a
21 * ClientException with the PSR exception as its cause, so the chain of
22 * previous exceptions is walked and the first cause that says something
23 * decides.
24 */
25enum DeliveryVerdict
26{
27    case Retry;
28    case Reject;
29
30    public static function of(Throwable $failure): self
31    {
32        for ($cause = $failure; $cause !== null; $cause = $cause->getPrevious()) {
33            if ($cause instanceof OneRecordHttpException) {
34                return self::forStatus($cause->status);
35            }
36            if ($cause instanceof TokenEndpointException) {
37                return self::forStatus($cause->status);
38            }
39            if ($cause instanceof RequestExceptionInterface) {
40                // PSR-18: the request itself is unusable; repeating it unchanged cannot help (R7-008).
41                return self::Reject;
42            }
43            if ($cause instanceof ClientExceptionInterface) {
44                // A network failure, or a client that says only "failed": transient until proven otherwise.
45                return self::Retry;
46            }
47        }
48
49        return self::Reject;
50    }
51
52    private static function forStatus(int $status): self
53    {
54        return $status >= 500 || $status === 408 || $status === 429 ? self::Retry : self::Reject;
55    }
56}