Skip to content

Commit b7d0367

Browse files
committed
adding sdk support for get channel endpoints
1 parent 21ef5c1 commit b7d0367

9 files changed

Lines changed: 283 additions & 4 deletions

‎linode_api4/groups/monitor.py‎

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
MonitorService,
1414
MonitorServiceToken,
1515
)
16+
from linode_api4.objects.filtering import and_
1617
from linode_api4.objects.monitor import ChannelDetails
1718

1819
__all__ = [
@@ -378,3 +379,86 @@ def channel_create(
378379
)
379380

380381
return AlertChannel(self.client, result["id"], result)
382+
383+
def alert_channel(self, channel_id: int) -> AlertChannel:
384+
"""
385+
Retrieve a specific notification channel definition details by its channel ID.
386+
387+
Returns an :class:`AlertChannel` object for the specified channel ID.
388+
The channel object contains all configuration details for the notification
389+
destination (e.g., email lists, webhooks, etc.).
390+
391+
.. note:: This endpoint is in beta and requires using the v4beta base URL.
392+
393+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-notification-channel
394+
395+
:param channel_id: The ID of the alert channel to retrieve.
396+
:type channel_id: int
397+
398+
:returns: The requested :class:`AlertChannel` object.
399+
:rtype: AlertChannel
400+
:raises ApiError: if the requested channel could not be loaded.
401+
"""
402+
return self.client.load(AlertChannel, channel_id)
403+
404+
def alert_channel_alerts(self, channel_id: int, *filters) -> PaginatedList:
405+
"""
406+
Retrieve all alerts associated with a specific alert channel.
407+
408+
Returns a paginated collection of alert definitions associated with the
409+
specified alert channel. This allows you to see which alert definitions
410+
are configured to notify this specific channel.
411+
412+
.. note:: This endpoint is in beta and requires using the v4beta base URL.
413+
414+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-notification-channel-alerts
415+
416+
:param channel_id: The ID of the alert channel to retrieve alerts for.
417+
:type channel_id: int
418+
:param filters: Optional filter expressions to apply to the collection.
419+
See :doc:`Filtering Collections</linode_api4/objects/filtering>` for details.
420+
421+
:returns: A paginated list of alert definitions associated with this channel.
422+
:rtype: PaginatedList[AlertDefinition]
423+
"""
424+
endpoint = f"/monitor/alert-channels/{channel_id}/alerts"
425+
parsed_filters = None
426+
if filters:
427+
if len(filters) > 1:
428+
parsed_filters = and_(
429+
*filters
430+
).dct # pylint: disable=no-value-for-parameter
431+
else:
432+
parsed_filters = filters[0].dct
433+
434+
response_json = self.client.get(endpoint, filters=parsed_filters)
435+
436+
if "data" not in response_json:
437+
raise UnexpectedResponseError(
438+
"Unexpected response when retrieving alert channel alerts!",
439+
json=response_json,
440+
)
441+
442+
# For each alert definition in the response, extract the service_type
443+
# and use it as the parent_id when creating AlertDefinition objects
444+
result = []
445+
for obj in response_json.get("data", []):
446+
if "id" in obj and "service_type" in obj:
447+
alert = AlertDefinition.make_instance(
448+
obj["id"],
449+
self.client,
450+
parent_id=obj["service_type"],
451+
json=obj,
452+
)
453+
result.append(alert)
454+
455+
# Return paginated list with pagination metadata from response
456+
return PaginatedList(
457+
self.client,
458+
endpoint[1:],
459+
page=result,
460+
max_pages=response_json.get("pages", 1),
461+
total_items=response_json.get("results", len(result)),
462+
parent_id=None,
463+
filters=parsed_filters,
464+
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"id": 123,
3+
"label": "alert notification channel",
4+
"type": "user",
5+
"channel_type": "email",
6+
"details": {
7+
"email": {
8+
"usernames": [
9+
"admin-user1",
10+
"admin-user2"
11+
],
12+
"recipient_type": "user"
13+
}
14+
},
15+
"alerts": {
16+
"url": "/monitor/alert-channels/123/alerts",
17+
"type": "alerts-definitions",
18+
"alert_count": 2
19+
},
20+
"created": "2024-01-01T00:00:00",
21+
"updated": "2024-01-01T00:00:00",
22+
"created_by": "tester",
23+
"updated_by": "tester"
24+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"data": [
3+
{
4+
"id": 12345,
5+
"label": "DBAAS Alert 1",
6+
"service_type": "dbaas",
7+
"type": "alerts-definitions",
8+
"url": "/monitor/services/dbaas/alerts-definitions/12345"
9+
},
10+
{
11+
"id": 12346,
12+
"label": "DBAAS Alert 2",
13+
"service_type": "dbaas",
14+
"type": "alerts-definitions",
15+
"url": "/monitor/services/dbaas/alerts-definitions/12346"
16+
}
17+
],
18+
"page": 1,
19+
"pages": 1,
20+
"results": 2
21+
}

‎test/fixtures/monitor_services_dbaas_alert-definitions.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"metric": "cpu_usage",
3939
"operator": "gt",
4040
"threshold": 90,
41-
"unit": "percent"
41+
"unit": "%"
4242
}
4343
]
4444
},

‎test/fixtures/monitor_services_dbaas_alert-definitions_12345.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"metric": "cpu_usage",
3737
"operator": "gt",
3838
"threshold": 90,
39-
"unit": "percent"
39+
"unit": "%"
4040
}
4141
]
4242
},

‎test/fixtures/monitor_services_dbaas_metric-definitions.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"metric": "cpu_usage",
2323
"metric_type": "gauge",
2424
"scrape_interval": "60s",
25-
"unit": "percent"
25+
"unit": "%"
2626
},
2727
{
2828
"available_aggregate_functions": [

‎test/integration/models/monitor/test_monitor.py‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,57 @@ def test_integration_create_get_update_delete_alert_channel(test_linode_client):
400400
print(
401401
f"Warning: Failed to delete channel {created_channel.id}: {e}"
402402
)
403+
404+
405+
def test_integration_alert_channel(test_linode_client):
406+
"""Test retrieving a single alert channel by ID.
407+
408+
This test fetches an existing alert channel and verifies that all
409+
expected properties are populated correctly.
410+
"""
411+
client = test_linode_client
412+
413+
# Get an existing alert channel to test with
414+
channels = list(client.monitor.alert_channels())
415+
if len(channels) == 0:
416+
pytest.skip("No alert channels available on account for testing")
417+
418+
channel_id = channels[0].id
419+
420+
# Test the alert_channel() method
421+
fetched_channel = client.monitor.alert_channel(channel_id)
422+
423+
assert isinstance(fetched_channel, AlertChannel)
424+
assert fetched_channel.id == channel_id
425+
assert fetched_channel.label is not None
426+
assert fetched_channel.channel_type is not None
427+
assert fetched_channel.details is not None
428+
429+
430+
def test_integration_alert_channel_alerts(test_linode_client):
431+
"""Test retrieving alerts associated with a specific alert channel.
432+
433+
This test fetches alerts for an existing alert channel and verifies
434+
the paginated list of alert definitions is returned correctly.
435+
"""
436+
client = test_linode_client
437+
438+
# Get an existing alert channel to test with
439+
channels = list(client.monitor.alert_channels())
440+
if len(channels) == 0:
441+
pytest.skip("No alert channels available on account for testing")
442+
443+
channel_id = channels[0].id
444+
445+
# Test the alert_channel_alerts() method
446+
alerts = client.monitor.alert_channel_alerts(channel_id)
447+
448+
assert isinstance(alerts, PaginatedList)
449+
450+
# If there are alerts, verify their structure
451+
if len(alerts) > 0:
452+
alert = alerts[0]
453+
assert isinstance(alert, AlertDefinition)
454+
assert alert.id is not None
455+
assert alert.label is not None
456+
assert alert.service_type is not None

‎test/unit/groups/monitor_api_test.py‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,99 @@ def test_create_update_delete_alert_channel(self):
251251

252252
assert mock_delete.call_url == channel_url
253253
assert result is True
254+
255+
def test_alert_channel(self):
256+
"""
257+
Test retrieval of a specific alert channel by ID.
258+
Verifies the alert_channel method returns a single AlertChannel object.
259+
"""
260+
channel_id = 123
261+
channel_url = f"/monitor/alert-channels/{channel_id}"
262+
263+
channel_response = {
264+
"id": channel_id,
265+
"label": "alert notification channel",
266+
"type": "user",
267+
"channel_type": "email",
268+
"details": {
269+
"email": {
270+
"usernames": ["admin-user1", "admin-user2"],
271+
"recipient_type": "user",
272+
}
273+
},
274+
"alerts": {
275+
"url": f"{channel_url}/alerts",
276+
"type": "alerts-definitions",
277+
"alert_count": 2,
278+
},
279+
"created": "2024-01-01T00:00:00",
280+
"updated": "2024-01-01T00:00:00",
281+
"created_by": "tester",
282+
"updated_by": "tester",
283+
}
284+
285+
with self.mock_get(channel_response) as mock_get:
286+
channel = self.client.monitor.alert_channel(channel_id=channel_id)
287+
288+
assert mock_get.call_url == channel_url
289+
assert isinstance(channel, AlertChannel)
290+
assert channel.id == channel_id
291+
assert channel.label == "alert notification channel"
292+
assert channel.channel_type == "email"
293+
assert channel.details.email.usernames == [
294+
"admin-user1",
295+
"admin-user2",
296+
]
297+
assert channel.alerts.alert_count == 2
298+
299+
def test_alert_channel_alerts(self):
300+
"""
301+
Test retrieval of alerts associated with a specific alert channel.
302+
Verifies the alert_channel_alerts method returns a paginated list
303+
of AlertDefinition objects associated with the channel.
304+
"""
305+
channel_id = 123
306+
alerts_url = f"/monitor/alert-channels/{channel_id}/alerts"
307+
308+
alerts_response = {
309+
"data": [
310+
{
311+
"id": 12345,
312+
"label": "DBAAS Alert 1",
313+
"service_type": "dbaas",
314+
"type": "alerts-definitions",
315+
"url": "/monitor/services/dbaas/alerts-definitions/12345",
316+
},
317+
{
318+
"id": 12346,
319+
"label": "DBAAS Alert 2",
320+
"service_type": "dbaas",
321+
"type": "alerts-definitions",
322+
"url": "/monitor/services/dbaas/alerts-definitions/12346",
323+
},
324+
],
325+
"page": 1,
326+
"pages": 1,
327+
"results": 2,
328+
}
329+
330+
with self.mock_get(alerts_response) as mock_get:
331+
alerts = self.client.monitor.alert_channel_alerts(
332+
channel_id=channel_id
333+
)
334+
335+
assert mock_get.call_url == alerts_url
336+
assert isinstance(alerts, PaginatedList)
337+
assert len(alerts) == 2
338+
339+
# Verify first alert
340+
assert isinstance(alerts[0], AlertDefinition)
341+
assert alerts[0].id == 12345
342+
assert alerts[0].label == "DBAAS Alert 1"
343+
assert alerts[0].service_type == "dbaas"
344+
345+
# Verify second alert
346+
assert isinstance(alerts[1], AlertDefinition)
347+
assert alerts[1].id == 12346
348+
assert alerts[1].label == "DBAAS Alert 2"
349+
assert alerts[1].service_type == "dbaas"

‎test/unit/objects/monitor_test.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def test_metric_definitions(self):
127127
self.assertEqual(metrics[0].metric, "cpu_usage")
128128
self.assertEqual(metrics[0].metric_type, "gauge")
129129
self.assertEqual(metrics[0].scrape_interval, "60s")
130-
self.assertEqual(metrics[0].unit, "percent")
130+
self.assertEqual(metrics[0].unit, "%")
131131
self.assertEqual(metrics[0].dimensions[0].dimension_label, "node_type")
132132
self.assertEqual(metrics[0].dimensions[0].label, "Node Type")
133133
self.assertEqual(

0 commit comments

Comments
 (0)