Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Delivery
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
2 / 2
12
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 handle
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
11
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Webhooks;
6
7use Cosray\Contract\Job;
8use Cosray\Exception\JobFailed;
9use Cosray\Exception\RuntimeException;
10use Override;
11
12/**
13 * Posts one queued webhook event to one hook. A delivery that gets no
14 * response or a server error runs again later; one the hook refuses with
15 * a client error, other than a timeout or a rate limit, fails at once.
16 *
17 * @internal
18 */
19final class Delivery implements Job
20{
21    private const array RETRYABLE = [408, 429];
22
23    public function __construct(
24        private readonly Client $client,
25    ) {}
26
27    #[Override]
28    public function handle(array $payload): void
29    {
30        $url = $payload['url'] ?? null;
31        $headers = $payload['headers'] ?? null;
32        $body = $payload['body'] ?? null;
33
34        if (!is_string($url) || !is_array($headers) || !is_string($body)) {
35            throw new JobFailed('A queued webhook delivery lacks its url, headers or body.');
36        }
37
38        /** @var array<string, string> $headers */
39        $status = $this->client->post($url, $headers, $body);
40
41        if ($status !== null && $status >= 200 && $status < 300) {
42            return;
43        }
44
45        $event = $headers['X-Cosray-Event'] ?? 'event';
46        $message = "Webhook {$event} to {$url} failed with " . ($status ?? 'no response');
47
48        if ($status !== null && $status >= 400 && $status < 500 && !in_array($status, self::RETRYABLE, true)) {
49            throw new JobFailed($message);
50        }
51
52        throw new RuntimeException($message);
53    }
54}