Lines 100.00% 23 / 23
Functions and Methods 100.00% 10 / 10
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
RuntimeAdmin 100.00% 23 / 23 100.00% 10 / 10 18 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 listHostedAppStatuses 100.00% 1 / 1 100.00% 1 / 1 1
 requireAppStatus 100.00% 2 / 2 100.00% 1 / 1 1
 pauseApp 100.00% 3 / 3 100.00% 1 / 1 2
 resumeApp 100.00% 3 / 3 100.00% 1 / 1 2
 resetApp 100.00% 2 / 2 100.00% 1 / 1 1
 restartWorker 100.00% 3 / 3 100.00% 1 / 1 1
 findChangeWarning 100.00% 1 / 1 100.00% 1 / 1 1
 describeResetOutcome 100.00% 6 / 6 100.00% 1 / 1 5
 findResetWarning 100.00% 1 / 1 100.00% 1 / 1 3
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Runtime\Admin;
6
7use Stewart\Contracts\App\AppId;
8use Stewart\Contracts\Exception\StoreException;
9use Stewart\Runtime\App\AppPauseOutcome;
10use Stewart\Runtime\App\AppPauseResetOutcome;
11use Stewart\Runtime\Broker\AppPauseService;
12use Stewart\Runtime\Broker\Collection\WorkerSlotCollection;
13use Stewart\Runtime\Broker\WorkerPool;
14use Stewart\Runtime\Broker\WorkerRestartOutcome;
15use Stewart\Runtime\Control\Assembler\AppStatusBuilder;
16use Stewart\Runtime\Control\Protocol\Frame\CommandResult;
17use Stewart\Runtime\Control\Protocol\Status\AppStatus;
18use Stewart\Runtime\Exception\AppException;
19use Stewart\Runtime\Exception\WorkerException;
20use Stewart\Runtime\Lifecycle\AppPauseOverridePersistence;
21use Stewart\Runtime\Lifecycle\AppPauseSource;
22use Stewart\Runtime\Lifecycle\AppStateAfterReset;
23
24final readonly class RuntimeAdmin
25{
26    public function __construct(
27        private AppPauseService $pauses,
28        private AppStatusBuilder $appStatuses,
29        private WorkerSlotCollection $workerSlots,
30        private WorkerPool $pool,
31    ) {}
32
33    /** @return list<AppStatus> */
34    public function listHostedAppStatuses(): array
35    {
36        return $this->appStatuses->buildHostedAppStatuses();
37    }
38
39    /** @throws AppException */
40    public function requireAppStatus(AppId $appId): AppStatus
41    {
42        $this->pauses->assertAppLoaded($appId);
43
44        return $this->appStatuses->findAppStatus($appId) ?? throw AppException::unknown($appId);
45    }
46
47    /** @throws AppException */
48    public function pauseApp(AppId $appId, AppPauseSource $source): CommandResult
49    {
50        $outcome = $this->pauses->pauseApp($appId, $source);
51        $message = \sprintf($outcome->changed ? 'App %s paused.' : 'App %s was already paused.', $appId);
52
53        return new CommandResult($outcome->changed, $message, $this->findChangeWarning($outcome));
54    }
55
56    /** @throws AppException */
57    public function resumeApp(AppId $appId, AppPauseSource $source): CommandResult
58    {
59        $outcome = $this->pauses->resumeApp($appId, $source);
60        $message = \sprintf($outcome->changed ? 'App %s resumed.' : 'App %s was not paused.', $appId);
61
62        return new CommandResult($outcome->changed, $message, $this->findChangeWarning($outcome));
63    }
64
65    /** @throws AppException|StoreException */
66    public function resetApp(AppId $appId, AppPauseSource $source): CommandResult
67    {
68        $outcome = $this->pauses->resetApp($appId, $source);
69
70        return new CommandResult($outcome->overrideRemoved, $this->describeResetOutcome($appId, $outcome), $this->findResetWarning($outcome));
71    }
72
73    /** @throws WorkerException */
74    public function restartWorker(string $workerId): CommandResult
75    {
76        $slot = $this->workerSlots->requireSlotById($workerId);
77        $outcome = $this->pool->restartWorker($slot);
78
79        return new CommandResult($outcome === WorkerRestartOutcome::Begun, $outcome->describeForWorker($slot->workerId), null);
80    }
81
82    private function findChangeWarning(AppPauseOutcome $outcome): ?string
83    {
84        return $outcome->persistence->findWarning();
85    }
86
87    private function describeResetOutcome(AppId $appId, AppPauseResetOutcome $outcome): string
88    {
89        if (!$outcome->overrideRemoved) {
90            return \sprintf('App %s had no pause override.', $appId);
91        }
92
93        return \sprintf(match ($outcome->stateAfter) {
94            AppStateAfterReset::PausedByConfig => 'App %s pause override removed; config keeps it paused.',
95            AppStateAfterReset::Running => 'App %s pause override removed; it is running.',
96            AppStateAfterReset::NotLoaded => 'App %s pause override removed.',
97        }, $appId);
98    }
99
100    private function findResetWarning(AppPauseResetOutcome $outcome): ?string
101    {
102        return $outcome->overrideRemoved || $outcome->persistence !== AppPauseOverridePersistence::NotConfigured ? $outcome->persistence->findWarning() : null;
103    }
104}