Lines 0.00% 0 / 30
Functions and Methods 0.00% 0 / 3
Classes and Traits 0.00% 0 / 1
Name Lines Functions and Methods CRAP Classes and Traits
BootstrapMessageFactory 0.00% 0 / 30 0.00% 0 / 3 30 0.00% 0 / 1
 __construct 0.00% 0 / 11 0.00% 0 / 1 6
 createBootstrapMessage 0.00% 0 / 12 0.00% 0 / 1 2
 buildStoreSettings 0.00% 0 / 7 0.00% 0 / 1 6
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Runtime\Broker;
6
7use Stewart\Contracts\Time\Duration;
8use Stewart\Runtime\App\AppCatalog;
9use Stewart\Runtime\Config\PersistenceConfig;
10use Stewart\Runtime\Config\StewartConfig;
11use Stewart\Runtime\Exception\ConfigurationException;
12use Stewart\Runtime\Ipc\Message\Bootstrap;
13use Stewart\Runtime\Ipc\StoreSettings;
14use Stewart\Runtime\Ipc\Wire\AppIdsFragment;
15use Stewart\Runtime\Ipc\Wire\IpcCodec;
16use Stewart\Runtime\Ipc\Wire\WorkerAppsFragment;
17use Stewart\Runtime\Ipc\WorkerSettings;
18
19final readonly class BootstrapMessageFactory
20{
21    // Headroom so a worker times out a call only after Home Assistant has.
22    private const int CALL_TIMEOUT_GRACE_SECONDS = 5;
23
24    private WorkerSettings $workerSettings;
25
26    private ?StoreSettings $storeSettings;
27
28    /** @throws ConfigurationException */
29    public function __construct(
30        StewartConfig $config,
31        private AppCatalog $apps,
32        private AppPauseRegistry $pausedApps,
33        string $userServicesFile,
34    ) {
35        $this->workerSettings = new WorkerSettings(
36            logLevel: $config->logLevel,
37            callTimeout: $config->requireHomeAssistant()->commandTimeout->plus(Duration::seconds(self::CALL_TIMEOUT_GRACE_SECONDS)),
38            shutdownGrace: $config->shutdownGrace,
39            subscriptionBuffer: $config->subscriptionBuffer,
40            initializeTimeout: $config->supervision->initializeTimeout->findDuration(),
41            servicesFile: is_file($userServicesFile) ? $userServicesFile : null,
42            generatedNamespace: $config->codegen->namespace->value,
43            mqttEnabled: $config->mqtt !== null,
44        );
45        $this->storeSettings = $this->buildStoreSettings($config->persistence);
46    }
47
48    public function createBootstrapMessage(WorkerHandle $handle, HaSession $session): Bootstrap
49    {
50        return new Bootstrap(
51            protocol: IpcCodec::PROTOCOL_VERSION,
52            workerId: $handle->id,
53            timeZone: $session->getTimeZone()->getName(),
54            location: $session->getLocation(),
55            apps: WorkerAppsFragment::fromCollection($handle->slot->listWorkerApps()),
56            settings: $this->workerSettings,
57            store: $this->storeSettings,
58            knownAppIds: AppIdsFragment::fromCollection($this->apps->knownIds),
59            pausedAppIds: AppIdsFragment::fromCollection($this->pausedApps->listPausedAppIds()),
60            haUserId: $session->getHaUserId(),
61        );
62    }
63
64    private function buildStoreSettings(PersistenceConfig $persistence): ?StoreSettings
65    {
66        $url = $persistence->url;
67
68        return $url === null ? null : new StoreSettings(
69            dsn: $url->reveal(),
70            prefix: $persistence->prefix->value,
71            timeout: $persistence->timeout,
72            recoveryInterval: $persistence->recoveryInterval,
73        );
74    }
75}