Lines
0.00%
0 / 32
Methods
0.00%
0 / 5
Classes
0.00%
0 / 1
| Name | Lines | Methods | CRAP | ||||
|---|---|---|---|---|---|---|---|
| __construct | 0.00% | 0 / 1 | 0.00% | 0 / 1 | 2 | ||
| callService | 0.00% | 0 / 1 | 0.00% | 0 / 1 | 2 | ||
| callServiceForResponse | 0.00% | 0 / 1 | 0.00% | 0 / 1 | 2 | ||
| sendRequestAndAwait | 0.00% | 0 / 24 | 0.00% | 0 / 1 | 12 | ||
| sendRequest | 0.00% | 0 / 5 | 0.00% | 0 / 1 | 20 | ||
| 24 | final readonly class ServiceCaller | |
| 25 | { | |
| 26 | public function __construct( | |
| 27 | private Transport $transport, | |
| 28 | private PendingRequests $pending, | |
| 29 | private ConnectionStatus $connection, | |
| 30 | private Deadlines $deadlines, | |
| 31 | private WorkerSettings $workerSettings, | |
| 32 | ) {} | |
| 33 | ||
| 34 | /** @throws ServiceCallException */ | |
| 35 | public function callService(ResourceScope $scope, string $domain, string $service, ServiceFields $data, ?ServiceTarget $target): EventContext | |
| 36 | { | |
| 37 | return $this->sendRequestAndAwait($scope, $domain, $service, $data, $target, returnResponse: false)->context ?? EventContext::unknown(); | |
| 38 | } | |
| 39 | ||
| 40 | /** @throws ServiceCallException */ | |
| 41 | public function callServiceForResponse(ResourceScope $scope, string $domain, string $service, ServiceFields $data, ?ServiceTarget $target): ServiceResponse | |
| 42 | { | |
| 43 | return $this->sendRequestAndAwait($scope, $domain, $service, $data, $target, returnResponse: true); | |
| 44 | } | |
| 45 | ||
| 46 | /** @throws ServiceCallException */ | |
| 47 | private function sendRequestAndAwait( | |
| 48 | ResourceScope $scope, | |
| 49 | string $domain, | |
| 50 | string $service, | |
| 51 | ServiceFields $data, | |
| 52 | ?ServiceTarget $target, | |
| 53 | bool $returnResponse, | |
| 54 | ): ServiceResponse { | |
| 55 | if (!$this->connection->connected) { | |
| 56 | throw ServiceCallException::unreachable($domain, $service, 'Home Assistant is disconnected'); | |
| 57 | } | |
| 58 | ||
| 59 | $call = $this->pending->open( | |
| 60 | ServiceResponse::class, | |
| 61 | static fn(string $detail): ServiceCallException => ServiceCallException::unreachable($domain, $service, $detail), | |
| 62 | ); | |
| 63 | ||
| 64 | try { | |
| 65 | $this->sendRequest(new ServiceCallRequest( | |
| 66 | correlationId: $call->correlationId, | |
| 67 | scope: $scope, | |
| 68 | domain: $domain, | |
| 69 | service: $service, | |
| 70 | data: $data->fields, | |
| 71 | target: $target, | |
| 72 | returnResponse: $returnResponse, | |
| 73 | )); | |
| 74 | ||
| 75 | return $call->getFuture()->await($this->deadlines->timeout($this->workerSettings->callTimeout)); | |
| 76 | } catch (CancelledException $e) { | |
| 77 | throw ServiceCallException::timedOut( | |
| 78 | $domain, | |
| 79 | $service, | |
| 80 | \sprintf('no answer from the broker within %s', $this->workerSettings->callTimeout), | |
| 81 | $e, | |
| 82 | ); | |
| 83 | } finally { | |
| 84 | $this->pending->forget($call->correlationId); | |
| 85 | } | |
| 86 | } | |
| 87 | ||
| 88 | /** @throws ServiceCallException */ | |
| 89 | private function sendRequest(ServiceCallRequest $request): void | |
| 90 | { | |
| 91 | try { | |
| 92 | $this->transport->send($request); | |
| 93 | } catch (Throwable $e) { | |
| 94 | throw $e instanceof TransportException && $e->reason === TransportError::Unencodable | |
| 95 | ? ServiceCallException::rejected($request->domain, $request->service, $e->getMessage(), null, $e) | |
| 96 | : ServiceCallException::unreachable($request->domain, $request->service, 'the broker is gone: ' . $e->getMessage(), $e); | |
| 97 | } | |
| 98 | } | |
| 99 | } |