Lines 100.00% 24 / 24
Methods 100.00% 5 / 5
Classes 100.00% 1 / 1
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 routeCommand 100.00% 9 / 9 100.00% 1 / 1 4
 completeCommand 100.00% 9 / 9 100.00% 1 / 1 3
 failCommandsOf 100.00% 3 / 3 100.00% 1 / 1 2
 refuseCommand 100.00% 2 / 2 100.00% 1 / 1 1
17final readonly class ExposureCommandRouter
18{
19    public function __construct(
20        private ExposureLink $exposures,
21        private WorkerSlotRegistry $slots,
22        private AppPauseRegistry $pauses,
23        private PendingExposureCommands $pending,
24        private AppMetrics $metrics,
25    ) {}
26
27    public function routeCommand(ExposedEntityCommand $command): void
28    {
29        if ($this->pauses->isPaused($command->appId)) {
30            $this->refuseCommand($command, \sprintf('App %s is paused.', $command->appId));
31
32            return;
33        }
34
35        $handle = $command->owner === null ? null : $this->slots->findHandleForWorker($command->owner);
36
37        if ($handle === null) {
38            $this->refuseCommand($command, \sprintf('App %s is not running.', $command->appId));
39
40            return;
41        }
42
43        $this->pending->recordCommand($command);
44        $handle->send(new ExposedEntityCommanded($command->commandId, ResourceScope::forApp($command->appId), $command->key, $command->command));
45    }
46
47    public function completeCommand(WorkerHandle $handle, ExposedCommandAnswered $answer): void
48    {
49        $command = $this->pending->takeCommandAnsweredBy($answer->commandId, $handle->id);
50
51        if ($command === null) {
52            return;
53        }
54
55        if ($answer->accepted) {
56            $this->metrics->recordExposedCommand($command->appId, ExposedCommandOutcome::Accepted);
57            $this->exposures->acceptCommand($command);
58
59            return;
60        }
61
62        $this->metrics->recordExposedCommand($command->appId, ExposedCommandOutcome::Rejected);
63        $this->exposures->rejectCommand($command, $answer->rejection ?? \sprintf('App %s rejected the command.', $command->appId));
64    }
65
66    public function failCommandsOf(WorkerId $workerId): void
67    {
68        foreach ($this->pending->takeCommandsOf($workerId) as $command) {
69            $this->metrics->recordExposedCommand($command->appId, ExposedCommandOutcome::Lost);
70            $this->exposures->rejectCommand($command, \sprintf('App %s stopped while handling the command.', $command->appId));
71        }
72    }
73
74    private function refuseCommand(ExposedEntityCommand $command, string $reason): void
75    {
76        $this->metrics->recordExposedCommand($command->appId, ExposedCommandOutcome::Refused);
77        $this->exposures->rejectCommand($command, $reason);
78    }
79}