Skip to content

Commit 7f49c1d

Browse files
committed
refactor: enhance AssetsApiClient with default channel options and improved documentation
- Introduced default gRPC call timeout and channel options for the AssetsApiClient, ensuring consistent behavior and simplifying client configuration. - Updated the constructor documentation to clarify parameter usage and expectations, enhancing usability for developers. - Removed obsolete test file related to microgrid retrieval, streamlining the test suite. These changes improve the clarity and maintainability of the AssetsApiClient while ensuring better default configurations for users. Signed-off-by: eduardiazf <[email protected]>
1 parent ce25c60 commit 7f49c1d

File tree

3 files changed

+85
-11
lines changed

3 files changed

+85
-11
lines changed

src/frequenz/client/assets/_client.py

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
from __future__ import annotations
1111

12+
from dataclasses import replace
13+
1214
from frequenz.api.assets.v1 import assets_pb2, assets_pb2_grpc
15+
from frequenz.client.base import channel
1316
from frequenz.client.base.client import BaseApiClient, call_stub_method
1417

1518
from frequenz.client.assets.electrical_component._electrical_component import (
@@ -21,30 +24,58 @@
2124
from .electrical_component._electrical_component_proto import electrical_component_proto
2225
from .exceptions import ClientNotConnected
2326

27+
DEFAULT_GRPC_CALL_TIMEOUT = 60.0
28+
"""The default timeout for gRPC calls made by this client (in seconds)."""
29+
30+
31+
DEFAULT_CHANNEL_OPTIONS = replace(
32+
channel.ChannelOptions(), ssl=channel.SslOptions(enabled=False)
33+
)
34+
"""The default channel options for the assets API client.
35+
36+
These are the same defaults as the common default options but with SSL disabled, as the
37+
assets API does not use SSL by default.
38+
"""
39+
2440

25-
class AssetsApiClient(BaseApiClient[assets_pb2_grpc.PlatformAssetsStub]):
41+
class AssetsApiClient(
42+
BaseApiClient[assets_pb2_grpc.PlatformAssetsStub]
43+
): # pylint: disable=too-many-arguments
2644
"""A client for the Assets API."""
2745

2846
def __init__(
2947
self,
3048
server_url: str,
31-
auth_key: str | None,
32-
sign_secret: str | None,
49+
*,
50+
auth_key: str | None = None,
51+
sign_secret: str | None = None,
52+
channel_defaults: channel.ChannelOptions = DEFAULT_CHANNEL_OPTIONS,
3353
connect: bool = True,
3454
) -> None:
3555
"""
3656
Initialize the AssetsApiClient.
3757
3858
Args:
39-
server_url: The URL of the server to connect to.
40-
auth_key: The API key to use when connecting to the service.
41-
sign_secret: The secret to use when creating message HMAC.
42-
connect: Whether to connect to the server as soon as a client instance is created.
59+
server_url: The location of the microgrid API server in the form of a URL.
60+
The following format is expected:
61+
"grpc://hostname{:`port`}{?ssl=`ssl`}",
62+
where the `port` should be an int between 0 and 65535 (defaulting to
63+
9090) and `ssl` should be a boolean (defaulting to `false`).
64+
For example: `grpc://localhost:1090?ssl=true`.
65+
auth_key: The authentication key to use for the connection.
66+
sign_secret: The secret to use for signing requests.
67+
channel_defaults: The default options use to create the channel when not
68+
specified in the URL.
69+
connect: Whether to connect to the server as soon as a client instance is
70+
created. If `False`, the client will not connect to the server until
71+
[connect()][frequenz.client.base.client.BaseApiClient.connect] is
72+
called.
4373
"""
4474
super().__init__(
4575
server_url,
4676
assets_pb2_grpc.PlatformAssetsStub,
4777
connect=connect,
78+
channel_defaults=channel_defaults,
4879
auth_key=auth_key,
4980
sign_secret=sign_secret,
5081
)
@@ -85,7 +116,8 @@ async def get_microgrid_details( # noqa: DOC502 (raises ApiClientError indirect
85116
microgrid = await call_stub_method(
86117
self,
87118
lambda: self.stub.GetMicrogrid(
88-
assets_pb2.GetMicrogridRequest(microgrid_id=microgrid_id)
119+
assets_pb2.GetMicrogridRequest(microgrid_id=microgrid_id),
120+
timeout=DEFAULT_GRPC_CALL_TIMEOUT,
89121
),
90122
method_name="GetMicrogrid",
91123
)
@@ -109,7 +141,8 @@ async def get_electrical_components(
109141
lambda: self.stub.ListMicrogridElectricalComponents(
110142
assets_pb2.ListMicrogridElectricalComponentsRequest(
111143
microgrid_id=microgrid_id,
112-
)
144+
),
145+
timeout=DEFAULT_GRPC_CALL_TIMEOUT,
113146
),
114147
method_name="ListMicrogridElectricalComponents",
115148
)

tests/client_test_cases/get_microgrid/defaults_case.py renamed to tests/client_test_cases/get_microgrid_details/defaults_case.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@
1010
from frequenz.api.assets.v1 import assets_pb2
1111
from frequenz.api.common.v1alpha8.microgrid import microgrid_pb2
1212
from frequenz.client.common.microgrid import EnterpriseId, MicrogridId
13-
from google.protobuf.empty_pb2 import Empty
1413

1514
from frequenz.client.assets import Microgrid, MicrogridStatus
1615

1716

1817
def assert_stub_method_call(stub_method: AsyncMock) -> None:
1918
"""Assert that the gRPC request matches the expected request."""
20-
stub_method.assert_called_once_with(Empty(), timeout=60.0)
19+
stub_method.assert_called_once_with(
20+
assets_pb2.GetMicrogridRequest(microgrid_id=1), timeout=60.0
21+
)
2122

2223

24+
client_args = (1,)
2325
create_timestamp = datetime(2023, 1, 1, tzinfo=timezone.utc)
2426
grpc_response = assets_pb2.GetMicrogridResponse(microgrid=microgrid_pb2.Microgrid())
2527

tests/test_client.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# License: MIT
2+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Test for the Assets API client."""
5+
6+
from __future__ import annotations
7+
8+
from collections.abc import AsyncIterator
9+
from pathlib import Path
10+
11+
import pytest
12+
from frequenz.api.assets.v1 import assets_pb2_grpc
13+
14+
from frequenz.client.assets import AssetsApiClient
15+
16+
from .util import ApiClientTestCaseSpec, get_test_specs, patch_client_class
17+
18+
TESTS_DIR = Path(__file__).parent / "client_test_cases"
19+
20+
21+
@pytest.fixture
22+
async def client() -> AsyncIterator[AssetsApiClient]:
23+
"""Fixture that provides a AssetsApiClient with a mock gRPC stub and channel."""
24+
with patch_client_class(AssetsApiClient, assets_pb2_grpc.PlatformAssetsStub):
25+
client = AssetsApiClient("grpc://localhost:1234")
26+
async with client:
27+
yield client
28+
29+
30+
@pytest.mark.parametrize(
31+
"spec",
32+
get_test_specs("get_microgrid_details", tests_dir=TESTS_DIR),
33+
ids=str,
34+
)
35+
async def test_get_microgrid_details(
36+
client: AssetsApiClient, spec: ApiClientTestCaseSpec
37+
) -> None:
38+
"""Test get_microgrid_details method."""
39+
await spec.test_unary_unary_call(client, "GetMicrogrid")

0 commit comments

Comments
 (0)