Lines 100.00% 17 / 17
Functions and Methods 100.00% 3 / 3
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
RegistryEditRequester 100.00% 17 / 17 100.00% 3 / 3 8 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 requestEntityUpdate 100.00% 11 / 11 100.00% 1 / 1 3
 sendRequest 100.00% 5 / 5 100.00% 1 / 1 4
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Runtime\Worker\Registry;
6
7use Amp\CancelledException;
8use Stewart\Contracts\Entity\EntityId;
9use Stewart\Contracts\Exception\RegistryEditException;
10use Stewart\Contracts\Registry\RegisteredEntity;
11use Stewart\Contracts\Registry\Update\EntityRegistryUpdate;
12use Stewart\Runtime\Exception\TransportError;
13use Stewart\Runtime\Exception\TransportException;
14use Stewart\Runtime\Ipc\Message\RegistryEntityUpdateRequest;
15use Stewart\Runtime\Ipc\Transport;
16use Stewart\Runtime\Ipc\WorkerSettings;
17use Stewart\Runtime\Model\ResourceScope;
18use Stewart\Runtime\Worker\ConnectionStatus;
19use Stewart\Runtime\Worker\PendingRequests;
20use Stewart\Support\Time\Deadlines;
21use Throwable;
22
23final readonly class RegistryEditRequester
24{
25    public function __construct(
26        private Transport $transport,
27        private PendingRequests $pending,
28        private ConnectionStatus $connection,
29        private Deadlines $deadlines,
30        private WorkerSettings $workerSettings,
31    ) {}
32
33    /** @throws RegistryEditException */
34    public function requestEntityUpdate(ResourceScope $scope, EntityId $entityId, EntityRegistryUpdate $update): RegisteredEntity
35    {
36        if (!$this->connection->connected) {
37            throw RegistryEditException::unreachable($entityId, 'Home Assistant is disconnected');
38        }
39
40        $edit = $this->pending->open(
41            RegisteredEntity::class,
42            static fn(string $detail): RegistryEditException => RegistryEditException::unreachable($entityId, $detail),
43        );
44
45        try {
46            $this->sendRequest(new RegistryEntityUpdateRequest($edit->correlationId, $scope, $entityId, $update));
47
48            return $edit->getFuture()->await($this->deadlines->timeout($this->workerSettings->callTimeout));
49        } catch (CancelledException $e) {
50            throw RegistryEditException::timedOut($entityId, \sprintf('no answer from the broker within %s', $this->workerSettings->callTimeout), $e);
51        } finally {
52            $this->pending->forget($edit->correlationId);
53        }
54    }
55
56    /** @throws RegistryEditException */
57    private function sendRequest(RegistryEntityUpdateRequest $request): void
58    {
59        try {
60            $this->transport->send($request);
61        } catch (Throwable $e) {
62            throw $e instanceof TransportException && $e->reason === TransportError::Unencodable
63                ? RegistryEditException::rejected($request->entityId, $e->getMessage(), null, $e)
64                : RegistryEditException::unreachable($request->entityId, 'the broker is gone: ' . $e->getMessage(), $e);
65        }
66    }
67}