Skip to content

Commit 0d29e6d

Browse files
Integrate BaseClient into reporting client
Signed-off-by: Flora <[email protected]>
1 parent 0aa228b commit 0d29e6d

File tree

4 files changed

+21
-59
lines changed

4 files changed

+21
-59
lines changed

RELEASE_NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
## New Features
1212

13-
<!-- Here goes the main new features and examples or instructions on how to use them -->
13+
* Integration of the base client v0.5
1414

1515
## Bug Fixes
1616

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ dependencies = [
3333
"grpcio >= 1.54.2, < 2",
3434
"grpcio-tools >= 1.54.2, < 2",
3535
"protobuf >= 4.25.3, < 6",
36+
"frequenz-client-base[grpcio] >= 0.5.0, < 0.6.0",
3637
]
3738
dynamic = ["version"]
3839

src/frequenz/client/reporting/_client.py

Lines changed: 8 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
from collections.abc import AsyncIterator, Iterator
88
from dataclasses import dataclass
99
from datetime import datetime
10-
from typing import Any, cast
10+
from typing import cast
1111

12-
import grpc
1312
import grpc.aio as grpcaio
1413

1514
# pylint: disable=no-name-in-module
@@ -27,11 +26,10 @@
2726
)
2827
from frequenz.api.reporting.v1.reporting_pb2 import TimeFilter as PBTimeFilter
2928
from frequenz.api.reporting.v1.reporting_pb2_grpc import ReportingStub
29+
from frequenz.client.base.client import BaseApiClient
3030
from frequenz.client.common.metric import Metric
3131
from google.protobuf.timestamp_pb2 import Timestamp as PBTimestamp
3232

33-
# pylint: enable=no-name-in-module
34-
3533
MetricSample = namedtuple(
3634
"MetricSample", ["timestamp", "microgrid_id", "component_id", "metric", "value"]
3735
)
@@ -98,20 +96,18 @@ def __iter__(self) -> Iterator[MetricSample]:
9896
)
9997

10098

101-
class ReportingApiClient:
99+
class ReportingApiClient(BaseApiClient[ReportingStub, grpcaio.Channel]):
102100
"""A client for the Reporting service."""
103101

104-
def __init__(self, service_address: str, key: str | None = None) -> None:
102+
def __init__(self, server_url: str, key: str | None = None) -> None:
105103
"""Create a new Reporting client.
106104
107105
Args:
108-
service_address: The address of the Reporting service.
106+
server_url: The URL of the Reporting service.
109107
key: The API key for the authorization.
110108
"""
111-
self._grpc_channel = grpcaio.secure_channel(
112-
service_address, grpc.ssl_channel_credentials()
113-
)
114-
self._stub = ReportingStub(self._grpc_channel)
109+
super().__init__(server_url, ReportingStub, grpcaio.Channel)
110+
115111
self._metadata = (("key", key),) if key else ()
116112

117113
# pylint: disable=too-many-arguments
@@ -244,48 +240,15 @@ def dt2ts(dt: datetime) -> PBTimestamp:
244240
try:
245241
stream = cast(
246242
AsyncIterator[PBReceiveMicrogridComponentsDataStreamResponse],
247-
self._stub.ReceiveMicrogridComponentsDataStream(
243+
self.stub.ReceiveMicrogridComponentsDataStream(
248244
request, metadata=self._metadata
249245
),
250246
)
251-
# grpc.aio is missing types and mypy thinks this is not
252-
# async iterable, but it is.
253247
async for response in stream:
254248
if not response:
255249
break
256-
257250
yield ComponentsDataBatch(response)
258251

259252
except grpcaio.AioRpcError as e:
260253
print(f"RPC failed: {e}")
261254
return
262-
263-
async def close(self) -> None:
264-
"""Close the client and cancel any pending requests immediately."""
265-
await self._grpc_channel.close(grace=None)
266-
267-
async def __aenter__(self) -> "ReportingApiClient":
268-
"""Enter the async context."""
269-
return self
270-
271-
async def __aexit__(
272-
self,
273-
_exc_type: type[BaseException] | None,
274-
_exc_val: BaseException | None,
275-
_exc_tb: Any | None,
276-
) -> bool | None:
277-
"""
278-
Exit the asynchronous context manager.
279-
280-
Note that exceptions are not handled here, but are allowed to propagate.
281-
282-
Args:
283-
_exc_type: Type of exception raised in the async context.
284-
_exc_val: Exception instance raised.
285-
_exc_tb: Traceback object at the point where the exception occurred.
286-
287-
Returns:
288-
None, allowing any exceptions to propagate.
289-
"""
290-
await self.close()
291-
return None

tests/test_client_reporting.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,25 @@
22
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
33

44
"""Tests for the frequenz.client.reporting package."""
5-
from typing import Generator
6-
from unittest.mock import ANY, MagicMock, patch
5+
from unittest.mock import MagicMock, patch
76

7+
import grpc.aio as grpcaio
88
import pytest
9+
from frequenz.api.reporting.v1.reporting_pb2_grpc import ReportingStub
10+
from frequenz.client.base.client import BaseApiClient
911

1012
from frequenz.client.reporting import ReportingApiClient
1113
from frequenz.client.reporting._client import ComponentsDataBatch
1214

1315

14-
@pytest.fixture
15-
def mock_channel() -> Generator[MagicMock, None, None]:
16-
"""Fixture for grpc.aio.secure_channel."""
17-
with patch("grpc.aio.secure_channel") as mock:
18-
yield mock
19-
20-
2116
@pytest.mark.asyncio
22-
async def test_client_initialization(mock_channel: MagicMock) -> None:
23-
"""Test that the client initializes the channel."""
24-
client = ReportingApiClient("localhost:50051") # noqa: F841
25-
mock_channel.assert_called_once_with("localhost:50051", ANY)
17+
async def test_client_initialization() -> None:
18+
"""Test that the client initializes the BaseApiClient with grpcaio.Channel."""
19+
with patch.object(BaseApiClient, "__init__", return_value=None) as mock_base_init:
20+
client = ReportingApiClient("gprc://localhost:50051") # noqa: F841
21+
mock_base_init.assert_called_once_with(
22+
"gprc://localhost:50051", ReportingStub, grpcaio.Channel
23+
)
2624

2725

2826
def test_components_data_batch_is_empty_true() -> None:

0 commit comments

Comments
 (0)