From a128cd12b93627946b5017060fcaf6d6f1a5ef62 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 18:54:10 +0000 Subject: [PATCH 1/4] Initial plan From 0bdca571defcf6b5102fd1ab26b68e26617bd2d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 18:59:25 +0000 Subject: [PATCH 2/4] Fix Route dataclass and notification policy serialization to allow optional fields Agent-Logs-Url: https://github.com/ZPascal/grafana_api_sdk/sessions/3292ed0c-ac7b-4aa9-ad97-aadf647d2f14 Co-authored-by: ZPascal <13447634+ZPascal@users.noreply.github.com> --- grafana_api/alerting_provisioning.py | 40 +++++++++++-------- grafana_api/model.py | 12 +++--- tests/unittests/test_alerting_provisioning.py | 19 ++++++++- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/grafana_api/alerting_provisioning.py b/grafana_api/alerting_provisioning.py index a8257ec8..b33135b8 100644 --- a/grafana_api/alerting_provisioning.py +++ b/grafana_api/alerting_provisioning.py @@ -755,22 +755,30 @@ def _create_alert_route_dictionary(self, route: Route) -> dict: result (dict): Returns the alert route dictionary """ - return dict( - { - "continue": route.continue_parameter, - "group_by": route.group_by_str, - "mute_time_intervals": route.mute_time_intervals, - "receiver": route.receiver, - "routes": self._create_alert_routes_list(route.routes), - "group_interval": route.group_interval, - "group_wait": route.group_wait, - "object_matchers": self._create_object_matcher_list( - route.object_matchers - ), - "provenance": route.provenance, - "repeat_interval": route.repeat_interval, - } - ) + route_dict: dict = {"receiver": route.receiver} + + if route.continue_parameter is not None: + route_dict["continue"] = route.continue_parameter + if route.group_by_str is not None: + route_dict["group_by"] = route.group_by_str + if route.mute_time_intervals is not None: + route_dict["mute_time_intervals"] = route.mute_time_intervals + if route.routes is not None: + route_dict["routes"] = self._create_alert_routes_list(route.routes) + if route.group_interval is not None: + route_dict["group_interval"] = route.group_interval + if route.group_wait is not None: + route_dict["group_wait"] = route.group_wait + if route.object_matchers is not None: + route_dict["object_matchers"] = self._create_object_matcher_list( + route.object_matchers + ) + if route.provenance is not None: + route_dict["provenance"] = route.provenance + if route.repeat_interval is not None: + route_dict["repeat_interval"] = route.repeat_interval + + return route_dict def _create_alert_routes_list(self, routes: List[Route]) -> (list, None): """The method includes a functionality to create the alert route list diff --git a/grafana_api/model.py b/grafana_api/model.py index cb34f2c2..884b7b53 100644 --- a/grafana_api/model.py +++ b/grafana_api/model.py @@ -345,10 +345,10 @@ class Route: """The class includes all necessary variables to generate an alert rule route that is necessary to communicate with the Grafana alert provisioning endpoint Args: - continue_parameter (bool): Specify the continue parameter - group_by_str (List[str]): Specify the list of group by strings receiver (str): Specify the receiver - provenance (str): Specify the provenance + continue_parameter (bool): Specify the continue parameter (default None) + group_by_str (List[str]): Specify the list of group by strings (default None) + provenance (str): Specify the provenance (default None) object_matchers (List[Matcher]): Specify the list of object matchers (default None) group_interval (str): Specify the group time interval (default None) group_wait (str): Specify the group wait time (default None) @@ -357,10 +357,10 @@ class Route: mute_time_intervals (List[str]): Specify the mute time interval as list (default None) """ - continue_parameter: bool - group_by_str: List[str] receiver: str - provenance: str + continue_parameter: bool = None + group_by_str: List[str] = None + provenance: str = None object_matchers: List[Matcher] = None group_interval: str = None group_wait: str = None diff --git a/tests/unittests/test_alerting_provisioning.py b/tests/unittests/test_alerting_provisioning.py index 65acf7fe..93ba0d06 100644 --- a/tests/unittests/test_alerting_provisioning.py +++ b/tests/unittests/test_alerting_provisioning.py @@ -25,12 +25,12 @@ def setUp(self): "test", "test", {"test": "test"} ) matcher: Matcher = Matcher("test", MatchType.MatchEqual, "test") - route_2: Route = Route(False, ["test"], "test", "test") + route_2: Route = Route("test", False, ["test"], "test") self.route: Route = Route( + "test", False, ["test"], "test", - "test", object_matchers=[matcher], routes=[route_2], ) @@ -386,6 +386,21 @@ def test_add_notification_policies(self, call_the_api_mock): None, alerting_provisioning.add_notification_policies(self.route) ) + @patch("grafana_api.api.Api.call_the_api") + def test_add_notification_policies_receiver_only(self, call_the_api_mock): + model: APIModel = APIModel(host=MagicMock(), token=MagicMock()) + alerting_provisioning: AlertingProvisioning = AlertingProvisioning( + grafana_api_model=model + ) + + call_the_api_mock.return_value = dict({"status": 201}) + + minimal_route: Route = Route("grafana-default-email") + self.assertEqual( + None, + alerting_provisioning.add_notification_policies(minimal_route), + ) + def test_add_notification_policies_no_rule(self): model: APIModel = APIModel(host=MagicMock(), token=MagicMock()) alerting_provisioning: AlertingProvisioning = AlertingProvisioning( From 270904b0786c4ae9f74c5bef574264394597029c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Apr 2026 03:38:14 +0000 Subject: [PATCH 3/4] Use dict comprehension to filter None values in _create_alert_route_dictionary; fix integration tests Agent-Logs-Url: https://github.com/ZPascal/grafana_api_sdk/sessions/afddd9f0-284d-47a0-8ea8-4fa7e3c6f247 Co-authored-by: ZPascal <13447634+ZPascal@users.noreply.github.com> --- grafana_api/alerting_provisioning.py | 42 ++++++++----------- .../test_alerting_provisioning.py | 4 +- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/grafana_api/alerting_provisioning.py b/grafana_api/alerting_provisioning.py index b33135b8..a2875ce1 100644 --- a/grafana_api/alerting_provisioning.py +++ b/grafana_api/alerting_provisioning.py @@ -755,30 +755,24 @@ def _create_alert_route_dictionary(self, route: Route) -> dict: result (dict): Returns the alert route dictionary """ - route_dict: dict = {"receiver": route.receiver} - - if route.continue_parameter is not None: - route_dict["continue"] = route.continue_parameter - if route.group_by_str is not None: - route_dict["group_by"] = route.group_by_str - if route.mute_time_intervals is not None: - route_dict["mute_time_intervals"] = route.mute_time_intervals - if route.routes is not None: - route_dict["routes"] = self._create_alert_routes_list(route.routes) - if route.group_interval is not None: - route_dict["group_interval"] = route.group_interval - if route.group_wait is not None: - route_dict["group_wait"] = route.group_wait - if route.object_matchers is not None: - route_dict["object_matchers"] = self._create_object_matcher_list( - route.object_matchers - ) - if route.provenance is not None: - route_dict["provenance"] = route.provenance - if route.repeat_interval is not None: - route_dict["repeat_interval"] = route.repeat_interval - - return route_dict + return { + k: v + for k, v in { + "continue": route.continue_parameter, + "group_by": route.group_by_str, + "mute_time_intervals": route.mute_time_intervals, + "receiver": route.receiver, + "routes": self._create_alert_routes_list(route.routes), + "group_interval": route.group_interval, + "group_wait": route.group_wait, + "object_matchers": self._create_object_matcher_list( + route.object_matchers + ), + "provenance": route.provenance, + "repeat_interval": route.repeat_interval, + }.items() + if v is not None + } def _create_alert_routes_list(self, routes: List[Route]) -> (list, None): """The method includes a functionality to create the alert route list diff --git a/tests/integrationtest/test_alerting_provisioning.py b/tests/integrationtest/test_alerting_provisioning.py index 1094ff69..f4e124ff 100644 --- a/tests/integrationtest/test_alerting_provisioning.py +++ b/tests/integrationtest/test_alerting_provisioning.py @@ -127,9 +127,9 @@ def test_j_get_notification_policies(self): def test_k_add_notification_policies(self): route: Route = Route( + "test1", False, ["grafana_folder", "alertname"], - "test1", "test", group_wait="5m", group_interval="30s", @@ -229,9 +229,9 @@ def test_u_delete_message_template(self): def doCleanups(self): route: Route = Route( + "grafana-default-email", False, ["grafana_folder", "alertname"], - "grafana-default-email", "test", group_wait="5m", group_interval="30s", From 572423995dade823cb03d54b80240b8737d16142 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 24 Apr 2026 03:43:39 +0000 Subject: [PATCH 4/4] docs: Add the documentation --- docs/content/grafana_api/model.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/grafana_api/model.md b/docs/content/grafana_api/model.md index 6a2d7257..af6e06ca 100644 --- a/docs/content/grafana_api/model.md +++ b/docs/content/grafana_api/model.md @@ -327,10 +327,10 @@ The class includes all necessary variables to generate an alert rule route that **Arguments**: -- `continue_parameter` _bool_ - Specify the continue parameter -- `group_by_str` _List[str]_ - Specify the list of group by strings - `receiver` _str_ - Specify the receiver -- `provenance` _str_ - Specify the provenance +- `continue_parameter` _bool_ - Specify the continue parameter (default None) +- `group_by_str` _List[str]_ - Specify the list of group by strings (default None) +- `provenance` _str_ - Specify the provenance (default None) - `object_matchers` _List[Matcher]_ - Specify the list of object matchers (default None) - `group_interval` _str_ - Specify the group time interval (default None) - `group_wait` _str_ - Specify the group wait time (default None)