Lines 100.00% 21 / 21
Functions and Methods 100.00% 7 / 7
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
RegistryEntityUpdateRequestHandler 100.00% 21 / 21 100.00% 7 / 7 11 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 handledMessageClass 100.00% 1 / 1 100.00% 1 / 1 1
 handle 100.00% 1 / 1 100.00% 1 / 1 1
 updateOnHomeAssistant 100.00% 4 / 4 100.00% 1 / 1 3
 warnIfExposed 100.00% 8 / 8 100.00% 1 / 1 2
 sendUpdate 100.00% 4 / 4 100.00% 1 / 1 2
 previewUpdate 100.00% 2 / 2 100.00% 1 / 1 1
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Runtime\Broker\Message;
6
7use Psr\Log\LoggerInterface;
8use Stewart\Client\HaClient;
9use Stewart\Contracts\Exception\RegistryEditException;
10use Stewart\Contracts\Registry\RegisteredEntity;
11use Stewart\Runtime\Broker\Exposure\ExposureLink;
12use Stewart\Runtime\Broker\HaCallSlots;
13use Stewart\Runtime\Broker\HaSession;
14use Stewart\Runtime\Broker\WorkerHandle;
15use Stewart\Runtime\Config\ServiceCallPolicy;
16use Stewart\Runtime\Ipc\Message\RegistryEntityUpdateRequest;
17use Stewart\Runtime\Ipc\Message\RegistryEntityUpdateResult;
18use Stewart\Runtime\Ipc\Message\WorkerMessage;
19
20/** @implements WorkerMessageHandler<RegistryEntityUpdateRequest> */
21final readonly class RegistryEntityUpdateRequestHandler implements WorkerMessageHandler
22{
23    public function __construct(
24        private HaSession $session,
25        private HaClient $client,
26        private ExposureLink $exposures,
27        private HaCallSlots $slots,
28        private ServiceCallPolicy $policy,
29        private LoggerInterface $logger,
30    ) {}
31
32    public function handledMessageClass(): string
33    {
34        return RegistryEntityUpdateRequest::class;
35    }
36
37    /** @param RegistryEntityUpdateRequest $message */
38    public function handle(WorkerHandle $handle, WorkerMessage $message): void
39    {
40        $this->slots->runBounded($handle, $message, fn(): RegistryEntityUpdateResult => $this->updateOnHomeAssistant($message));
41    }
42
43    /** @throws RegistryEditException */
44    private function updateOnHomeAssistant(RegistryEntityUpdateRequest $request): RegistryEntityUpdateResult
45    {
46        if (!$this->session->isConnected()) {
47            throw $request->createUnreachableFailure('Home Assistant is disconnected');
48        }
49
50        $this->warnIfExposed($request);
51
52        return new RegistryEntityUpdateResult($request->correlationId, $this->policy->dryRun ? $this->previewUpdate($request) : $this->sendUpdate($request));
53    }
54
55    private function warnIfExposed(RegistryEntityUpdateRequest $request): void
56    {
57        $exposure = $this->exposures->findAddressByEntityId($request->entityId);
58
59        if ($exposure === null) {
60            return;
61        }
62
63        $this->logger->warning('Editing the registry entry of an entity a Stewart app exposes; its handle owns the name and icon', [
64            'entity' => $request->entityId->value,
65            'app' => $exposure->appId->value,
66            'key' => $exposure->key->value,
67        ]);
68    }
69
70    /** @throws RegistryEditException */
71    private function sendUpdate(RegistryEntityUpdateRequest $request): RegisteredEntity
72    {
73        $update = $request->update->needsCurrentEntry()
74            ? $request->update->resolveAgainst($this->client->getEntityRegistryEntry($request->entityId)->toRegisteredEntity())
75            : $request->update;
76
77        return $this->client->updateEntityRegistryEntry($request->entityId, $update)->toRegisteredEntity();
78    }
79
80    /** @throws RegistryEditException */
81    private function previewUpdate(RegistryEntityUpdateRequest $request): RegisteredEntity
82    {
83        $this->logger->info('Registry entry not updated: service_calls.dry_run is on', ['entity' => $request->entityId->value]);
84
85        return $request->update->applyTo($this->client->getEntityRegistryEntry($request->entityId)->toRegisteredEntity());
86    }
87}