Skip to content

Commit d590d12

Browse files
committed
feat: add ACLP list entities method
Add entities envelope in AlertDefinition. Add list entities GET API method. Add tests.
1 parent c9e18a5 commit d590d12

File tree

8 files changed

+274
-30
lines changed

8 files changed

+274
-30
lines changed

linode_api4/groups/monitor.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
from typing import Any, Optional
1+
from typing import Any, Optional, Union
22

33
from linode_api4 import PaginatedList
44
from linode_api4.errors import UnexpectedResponseError
55
from linode_api4.groups import Group
66
from linode_api4.objects import (
77
AlertChannel,
88
AlertDefinition,
9+
AlertDefinitionEntity,
10+
AlertScope,
911
MonitorDashboard,
1012
MonitorMetricsDefinition,
1113
MonitorService,
@@ -221,6 +223,8 @@ def create_alert_definition(
221223
trigger_conditions: dict,
222224
entity_ids: Optional[list[str]] = None,
223225
description: Optional[str] = None,
226+
scope: Optional[Union[AlertScope, str]] = None,
227+
regions: Optional[list[str]] = None,
224228
) -> AlertDefinition:
225229
"""
226230
Create a new alert definition for a given service type.
@@ -252,6 +256,10 @@ def create_alert_definition(
252256
:type entity_ids: Optional[list[str]]
253257
:param description: (Optional) Longer description for the alert definition.
254258
:type description: Optional[str]
259+
:param scope: (Optional) Alert scope (for example: `account`, `entity`, or `region`). Defaults to `entity`.
260+
:type scope: Optional[Union[AlertScope, str]]
261+
:param regions: (Optional) Regions to monitor.
262+
:type regions: Optional[list[str]]
255263
256264
:returns: The newly created :class:`AlertDefinition`.
257265
:rtype: AlertDefinition
@@ -267,6 +275,10 @@ def create_alert_definition(
267275
"rule_criteria": rule_criteria,
268276
"trigger_conditions": trigger_conditions,
269277
}
278+
if scope is not None:
279+
params["scope"] = scope
280+
if regions is not None:
281+
params["regions"] = regions
270282
if description is not None:
271283
params["description"] = description
272284
if entity_ids is not None:
@@ -284,3 +296,38 @@ def create_alert_definition(
284296
)
285297

286298
return AlertDefinition(self.client, result["id"], service_type, result)
299+
300+
def alert_definition_entities(
301+
self,
302+
service_type: str,
303+
id: int,
304+
*filters,
305+
) -> PaginatedList:
306+
"""
307+
List entities associated with a specific alert definition.
308+
309+
This endpoint supports pagination fields (`page`, `page_size`) in the API.
310+
311+
.. note:: This endpoint is in beta and requires using the v4beta base URL.
312+
313+
API Documentation: TODO
314+
315+
:param service_type: Service type for the alert definition (e.g. `dbaas`).
316+
:type service_type: str
317+
:param id: Alert definition identifier.
318+
:type id: int
319+
:param filters: Optional filter expressions to apply to the collection.
320+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`.
321+
322+
:returns: A paginated list of entities associated with the alert definition.
323+
:rtype: PaginatedList[AlertDefinitionEntity]
324+
"""
325+
326+
endpoint = (
327+
f"/monitor/services/{service_type}/alert-definitions/{id}/entities"
328+
)
329+
return self.client._get_and_filter(
330+
AlertDefinitionEntity,
331+
*filters,
332+
endpoint=endpoint,
333+
)

linode_api4/objects/monitor.py

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77

88
__all__ = [
99
"AggregateFunction",
10-
"Alert",
1110
"AlertChannel",
1211
"AlertDefinition",
12+
"AlertDefinitionChannel",
13+
"AlertDefinitionEntity",
14+
"AlertEntities",
15+
"AlertScope",
1316
"AlertType",
14-
"Alerts",
1517
"MonitorDashboard",
1618
"MonitorMetricsDefinition",
1719
"MonitorService",
@@ -341,15 +343,15 @@ class RuleCriteria(JSONObject):
341343

342344

343345
@dataclass
344-
class Alert(JSONObject):
346+
class AlertDefinitionChannel(JSONObject):
345347
"""
346-
Represents an alert definition reference within an AlertChannel.
348+
Represents the notification channel set up for use with an alert.
347349
348350
Fields:
349-
- id: int - Unique identifier of the alert definition.
350-
- label: str - Human-readable name for the alert definition.
351-
- type: str - Type of the alert (e.g., 'alerts-definitions').
352-
- url: str - API URL for the alert definition.
351+
- id: int - Unique identifier for this notification channel.
352+
- label: str - Human-readable name for the alert channel.
353+
- type: str - Type of notification used with the channel. For a user alert definition, only `email` is supported.
354+
- url: str - URL for the channel that ends in the channel's id.
353355
"""
354356

355357
id: int = 0
@@ -358,18 +360,6 @@ class Alert(JSONObject):
358360
url: str = ""
359361

360362

361-
@dataclass
362-
class Alerts(JSONObject):
363-
"""
364-
Represents a collection of alert definitions within an AlertChannel.
365-
366-
Fields:
367-
- items: List[Alert] - List of alert definitions.
368-
"""
369-
370-
items: List[Alert] = field(default_factory=list)
371-
372-
373363
class AlertType(StrEnum):
374364
"""
375365
Enumeration of alert origin types used by alert definitions.
@@ -387,6 +377,43 @@ class AlertType(StrEnum):
387377
user = "user"
388378

389379

380+
class AlertScope(StrEnum):
381+
"""
382+
Scope values supported for alert definitions.
383+
"""
384+
385+
entity = "entity"
386+
region = "region"
387+
account = "account"
388+
389+
390+
@dataclass
391+
class AlertEntities(JSONObject):
392+
"""
393+
Represents entity metadata for an alert definition.
394+
395+
For entity scoped alerts, `entities` envelope contains the URL to list entities,
396+
a count, and a has_more_resources flag.
397+
For region/account scoped alerts, the `entities` are returned as an empty object.
398+
"""
399+
400+
url: str = ""
401+
count: int = 0
402+
has_more_resources: bool = False
403+
404+
405+
@dataclass
406+
class AlertDefinitionEntity(JSONObject):
407+
"""
408+
Represents an entity associated with an alert definition.
409+
"""
410+
411+
id: str = ""
412+
label: str = ""
413+
url: str = ""
414+
_type: str = field(default="", metadata={"json_key": "type"})
415+
416+
390417
class AlertDefinition(DerivedBase):
391418
"""
392419
Represents an alert definition for a monitor service.
@@ -406,17 +433,22 @@ class AlertDefinition(DerivedBase):
406433
"severity": Property(mutable=True),
407434
"type": Property(mutable=True),
408435
"status": Property(mutable=True),
409-
"has_more_resources": Property(mutable=True),
436+
"scope": Property(AlertScope),
437+
"regions": Property(mutable=True),
438+
"has_more_resources": Property(), # Deprecated; use entities.has_more_resources
439+
"entities": Property(json_object=AlertEntities),
410440
"rule_criteria": Property(mutable=True, json_object=RuleCriteria),
411441
"trigger_conditions": Property(
412442
mutable=True, json_object=TriggerConditions
413443
),
414-
"alert_channels": Property(mutable=True, json_object=Alerts),
444+
"alert_channels": Property(json_object=AlertDefinitionChannel),
415445
"created": Property(is_datetime=True),
416446
"updated": Property(is_datetime=True),
417447
"updated_by": Property(),
418448
"created_by": Property(),
419-
"entity_ids": Property(mutable=True),
449+
"entity_ids": Property(
450+
mutable=True
451+
), # Deprecated; use entities.url to fetch associated entities
420452
"description": Property(mutable=True),
421453
"service_class": Property(alias_of="class"),
422454
}

test/fixtures/monitor_alert-definitions.json

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,26 @@
77
"severity": 1,
88
"type": "user",
99
"description": "A test alert for dbaas service",
10+
"scope": "entity",
11+
"regions": [],
1012
"entity_ids": ["13217"],
11-
"alert_channels": [],
13+
"entities": {
14+
"url": "/monitor/services/dbaas/alert-definitions/12345/entities",
15+
"count": 1,
16+
"has_more_resources": false
17+
},
18+
"alert_channels": [
19+
{
20+
"id": 10000,
21+
"label": "Read-Write Channel",
22+
"type": "email",
23+
"url": "/monitor/alert-channels/10000"
24+
}
25+
],
1226
"has_more_resources": false,
1327
"rule_criteria": null,
1428
"trigger_conditions": null,
1529
"class": "alert",
16-
"notification_groups": [],
1730
"status": "active",
1831
"created": "2024-01-01T00:00:00",
1932
"updated": "2024-01-01T00:00:00",

test/fixtures/monitor_services_dbaas_alert-definitions.json

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,24 @@
77
"severity": 1,
88
"type": "user",
99
"description": "A test alert for dbaas service",
10+
"scope": "entity",
11+
"regions": [],
1012
"entity_ids": [
1113
"13217"
1214
],
13-
"alert_channels": [],
15+
"entities": {
16+
"url": "/monitor/services/dbaas/alert-definitions/12345/entities",
17+
"count": 1,
18+
"has_more_resources": false
19+
},
20+
"alert_channels": [
21+
{
22+
"id": 10000,
23+
"label": "Read-Write Channel",
24+
"type": "email",
25+
"url": "/monitor/alert-channels/10000"
26+
}
27+
],
1428
"has_more_resources": false,
1529
"rule_criteria": {
1630
"rules": [
@@ -39,7 +53,6 @@
3953
"trigger_occurrences": 3
4054
},
4155
"class": "alert",
42-
"notification_groups": [],
4356
"status": "active",
4457
"created": "2024-01-01T00:00:00",
4558
"updated": "2024-01-01T00:00:00",

test/fixtures/monitor_services_dbaas_alert-definitions_12345.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,24 @@
55
"severity": 1,
66
"type": "user",
77
"description": "A test alert for dbaas service",
8+
"scope": "entity",
9+
"regions": [],
810
"entity_ids": [
911
"13217"
1012
],
11-
"alert_channels": [],
13+
"entities": {
14+
"url": "/monitor/services/dbaas/alert-definitions/12345/entities",
15+
"count": 1,
16+
"has_more_resources": false
17+
},
18+
"alert_channels": [
19+
{
20+
"id": 10000,
21+
"label": "Read-Write Channel",
22+
"type": "email",
23+
"url": "/monitor/alert-channels/10000"
24+
}
25+
],
1226
"has_more_resources": false,
1327
"rule_criteria": {
1428
"rules": [
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"data": [
3+
{
4+
"id": "1",
5+
"label": "mydatabase-1",
6+
"url": "/v4/databases/mysql/instances/1",
7+
"type": "dbaas"
8+
},
9+
{
10+
"id": "2",
11+
"label": "mydatabase-2",
12+
"url": "/v4/databases/mysql/instances/2",
13+
"type": "dbaas"
14+
},
15+
{
16+
"id": "3",
17+
"label": "mydatabase-3",
18+
"url": "/v4/databases/mysql/instances/3",
19+
"type": "dbaas"
20+
}
21+
],
22+
"page": 1,
23+
"pages": 1,
24+
"results": 3
25+
}

test/integration/models/monitor/test_monitor.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77

88
import pytest
99

10-
from linode_api4 import LinodeClient
10+
from linode_api4 import LinodeClient, PaginatedList
1111
from linode_api4.objects import (
1212
AlertDefinition,
13+
AlertDefinitionEntity,
1314
ApiError,
1415
MonitorDashboard,
1516
MonitorMetricsDefinition,
@@ -256,12 +257,14 @@ def wait_for_alert_ready(alert_id, service_type: str):
256257

257258
assert created.id
258259
assert getattr(created, "label", None) == label
260+
assert getattr(created, "entities", None) is not None
259261

260262
created = wait_for_alert_ready(created.id, service_type)
261263

262264
updated = client.load(AlertDefinition, created.id, service_type)
263265
updated.label = f"{label}-updated"
264266
updated.save()
267+
assert getattr(updated, "entities", None) is not None
265268

266269
updated = wait_for_alert_ready(updated.id, service_type)
267270

@@ -275,3 +278,36 @@ def wait_for_alert_ready(alert_id, service_type: str):
275278
AlertDefinition, created.id, service_type
276279
)
277280
delete_alert.delete()
281+
282+
283+
def test_alert_definition_entities(test_linode_client):
284+
"""Test listing entities associated with an alert definition.
285+
286+
This test first retrieves alert definitions for a service type, then lists entities for the first alert definition.
287+
It asserts that the returned entities have expected fields.
288+
"""
289+
client = test_linode_client
290+
service_type = "dbaas"
291+
292+
alert_definitions = client.monitor.alert_definitions(
293+
service_type=service_type
294+
)
295+
296+
if len(alert_definitions) == 0:
297+
pytest.fail("No alert definitions available for dbaas service type")
298+
299+
assert getattr(alert_definitions[0], "entities", None) is not None
300+
301+
alert_def = alert_definitions[0]
302+
entities = client.monitor.alert_definition_entities(
303+
service_type, alert_def.id
304+
)
305+
306+
assert isinstance(entities, PaginatedList)
307+
if len(entities) > 0:
308+
entity = entities[0]
309+
assert isinstance(entity, AlertDefinitionEntity)
310+
assert entity.id
311+
assert entity.label
312+
assert entity.url
313+
assert entity._type == service_type

0 commit comments

Comments
 (0)