Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

## New Features

<!-- Here goes the main new features and examples or instructions on how to use them -->
* Add HMAC generation capabilities.
* The new CLI option "key" can be used to provide the server's key.
* The client itself now has a "key" argument in the constructor.

## Bug Fixes

Expand Down
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dependencies = [
"frequenz-client-common >= 0.3.0, < 0.4",
"grpcio >=1.70.0, < 2",
"protobuf >= 5.29.3, < 7",
"frequenz-client-base >= 0.8.0, < 0.11.0",
"frequenz-client-base >= 0.11.0, < 0.12.0",
]
dynamic = ["version"]

Expand Down Expand Up @@ -156,6 +156,10 @@ disable = [

[tool.pytest.ini_options]
addopts = "-vv"
testpaths = ["tests", "src"]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
required_plugins = ["pytest-asyncio", "pytest-mock"]
filterwarnings = [
"error",
"once::DeprecationWarning",
Expand All @@ -164,10 +168,6 @@ filterwarnings = [
# chars as this is a regex
'ignore:Protobuf gencode version .*exactly one major version older.*:UserWarning',
]
testpaths = ["tests", "src"]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
required_plugins = ["pytest-asyncio", "pytest-mock"]

[tool.mypy]
explicit_package_bases = true
Expand Down
20 changes: 11 additions & 9 deletions src/frequenz/client/reporting/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,22 @@
class ReportingApiClient(BaseApiClient[ReportingStub]):
"""A client for the Reporting service."""

# pylint: disable-next=too-many-arguments
def __init__(
self,
server_url: str,
key: str | None = None,
*,
auth_key: str | None = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please update the README w.r.t. these interface changes.

sign_secret: str | None = None,
connect: bool = True,
channel_defaults: ChannelOptions = ChannelOptions(), # default options
) -> None:
"""Create a new Reporting client.

Args:
server_url: The URL of the Reporting service.
key: The API key for the authorization.
auth_key: The API key for the authorization.
sign_secret: The secret to use for HMAC signing the message
connect: Whether to connect to the server immediately.
channel_defaults: The default channel options.
"""
Expand All @@ -84,6 +88,8 @@ def __init__(
ReportingStub,
connect=connect,
channel_defaults=channel_defaults,
auth_key=auth_key,
sign_secret=sign_secret,
)

self._components_data_streams: dict[
Expand Down Expand Up @@ -129,8 +135,6 @@ def __init__(
GrpcStreamBroadcaster[PBAggregatedStreamResponse, MetricSample],
] = {}

self._metadata = (("key", key),) if key else ()

@property
def stub(self) -> ReportingStub:
"""The gRPC stub for the API."""
Expand Down Expand Up @@ -309,7 +313,7 @@ def stream_method() -> (
AsyncIterable[PBReceiveMicrogridComponentsDataStreamResponse]
):
call_iterator = self.stub.ReceiveMicrogridComponentsDataStream(
request, metadata=self._metadata
request,
)
return cast(
AsyncIterable[PBReceiveMicrogridComponentsDataStreamResponse],
Expand Down Expand Up @@ -493,9 +497,7 @@ def transform_response(
def stream_method() -> (
AsyncIterable[PBReceiveMicrogridSensorsDataStreamResponse]
):
call_iterator = self.stub.ReceiveMicrogridSensorsDataStream(
request, metadata=self._metadata
)
call_iterator = self.stub.ReceiveMicrogridSensorsDataStream(request)
return cast(
AsyncIterable[PBReceiveMicrogridSensorsDataStreamResponse],
call_iterator,
Expand Down Expand Up @@ -588,7 +590,7 @@ def transform_response(
def stream_method() -> AsyncIterable[PBAggregatedStreamResponse]:
call_iterator = (
self.stub.ReceiveAggregatedMicrogridComponentsDataStream(
request, metadata=self._metadata
request,
)
)

Expand Down
21 changes: 16 additions & 5 deletions src/frequenz/client/reporting/cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,17 @@ def main() -> None:
"--format", choices=["iter", "csv", "dict"], help="Output format", default="csv"
)
parser.add_argument(
"--key",
"--auth_key",
type=str,
help="API key",
default=None,
)
parser.add_argument(
"--sign_secret",
type=str,
help="The secret to use for generating HMAC signatures",
default=None,
)
args = parser.parse_args()
asyncio.run(
run(
Expand All @@ -96,8 +102,9 @@ def main() -> None:
states=args.states,
bounds=args.bounds,
service_address=args.url,
key=args.key,
auth_key=args.key,
fmt=args.format,
sign_secret=args.sign_secret,
)
)

Expand All @@ -114,8 +121,9 @@ async def run( # noqa: DOC502
states: bool,
bounds: bool,
service_address: str,
key: str,
auth_key: str,
fmt: str,
sign_secret: str | None,
) -> None:
"""Test the ReportingApiClient.

Expand All @@ -129,13 +137,16 @@ async def run( # noqa: DOC502
states: include states in the output
bounds: include bounds in the output
service_address: service address
key: API key
auth_key: API key
fmt: output format
sign_secret: secret used for creating HMAC signatures

Raises:
ValueError: if output format is invalid
"""
client = ReportingApiClient(service_address, key)
client = ReportingApiClient(
service_address, auth_key=auth_key, sign_secret=sign_secret
)

metrics = [Metric[mn] for mn in metric_names]

Expand Down
13 changes: 9 additions & 4 deletions tests/test_client_reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,27 @@ async def test_client_initialization() -> None:
# Parameters for the ReportingApiClient initialization
server_url = "gprc://localhost:50051"
key = "some-api-key"
sign_secret = "hunter2"
connect = True
channel_defaults = ChannelOptions()

with patch.object(BaseApiClient, "__init__", return_value=None) as mock_base_init:
client = ReportingApiClient(
server_url, key=key, connect=connect, channel_defaults=channel_defaults
ReportingApiClient(
server_url,
auth_key=key,
connect=connect,
channel_defaults=channel_defaults,
sign_secret=sign_secret,
) # noqa: F841
mock_base_init.assert_called_once_with(
server_url,
ReportingStub,
connect=connect,
channel_defaults=channel_defaults,
auth_key=key,
sign_secret=sign_secret,
)

assert client._metadata == (("key", key),) # pylint: disable=W0212


def test_components_data_batch_is_empty_true() -> None:
"""Test that the is_empty method returns True when the page is empty."""
Expand Down
Loading