Skip to content

Commit 7529642

Browse files
committed
fix:PRComments
1 parent b3144f7 commit 7529642

File tree

3 files changed

+100
-5
lines changed

3 files changed

+100
-5
lines changed

examples/alarm/alarm.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
SetAlarmTransition,
1212
TransitionInclusionOption,
1313
)
14-
from nisystemlink.clients.core import HttpConfiguration
14+
from nisystemlink.clients.core import ApiException, HttpConfiguration
1515

1616
# Setup the server configuration to point to your instance of SystemLink Enterprise
1717
server_configuration = HttpConfiguration(
@@ -75,15 +75,35 @@
7575
# Acknowledge the alarm
7676
client.acknowledge_alarms(ids=[id])
7777

78-
# Clear the alarm
78+
# Clear the alarm with 409 conflict handling - Method 1: Manual exception handling
79+
# A 409 Conflict response indicates that the requested transition would not change the alarm's state.
80+
# This allows stateless applications to simply attempt state transitions without first checking
81+
# the current state. For example, a monitoring system can repeatedly try to CLEAR an alarm
82+
# when conditions return to normal, and the API will return 409 if already cleared.
7983
clear_request = CreateOrUpdateAlarmRequest(
8084
alarm_id=alarm_id,
8185
transition=ClearAlarmTransition(
8286
occurred_at=datetime.now(),
8387
condition="Temperature returned to normal",
8488
),
8589
)
86-
client.create_or_update_alarm(clear_request)
90+
try:
91+
client.create_or_update_alarm(clear_request)
92+
print("Alarm cleared successfully")
93+
except ApiException as e:
94+
if e.http_status_code == 409:
95+
print("Alarm is already in the requested state (409 Conflict)")
96+
else:
97+
raise
98+
99+
# Clear the alarm with 409 conflict handling - Method 2: Using ignore_conflict parameter
100+
# This approach is cleaner for stateless applications that don't care about 409 errors.
101+
# Returns None if the alarm is already in the requested state.
102+
result = client.create_or_update_alarm(clear_request, ignore_conflict=True)
103+
if result is None:
104+
print("No state change needed (alarm already in requested state)")
105+
else:
106+
print(f"Alarm cleared successfully: {result}")
87107

88108
# Delete the alarm by its instance ID
89109
client.delete_alarm(id)

nisystemlink/clients/alarm/_alarm_client.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ def acknowledge_alarms(
5959
"""
6060
...
6161

62-
@post("instances", return_key="instanceId")
63-
def create_or_update_alarm(self, request: models.CreateOrUpdateAlarmRequest) -> str:
62+
def create_or_update_alarm(
63+
self, request: models.CreateOrUpdateAlarmRequest, *, ignore_conflict: bool = False
64+
) -> str | None:
6465
"""Creates or updates an instance, or occurrence, of an alarm.
6566
6667
Creates or updates an alarm based on the requested transition and the state
@@ -70,17 +71,38 @@ def create_or_update_alarm(self, request: models.CreateOrUpdateAlarmRequest) ->
7071
Args:
7172
request: The request containing alarm_id (user-defined identifier),
7273
transition details, and other alarm properties.
74+
ignore_conflict: If True, 409 Conflict errors will be ignored and None will be returned.
75+
If False (default), 409 errors will raise an ApiException.
76+
Setting this to True is useful for stateless applications that want to
77+
attempt state transitions without checking the current alarm state first.
7378
7479
Returns:
7580
The instance_id (unique occurrence identifier) of the created or modified alarm.
7681
Use this ID for operations like get_alarm(), delete_alarm(), or acknowledge.
82+
Returns None if ignore_conflict is True and a 409 Conflict occurs.
7783
7884
Raises:
7985
ApiException: if unable to communicate with the `/nialarm` Service or provided invalid arguments.
8086
A 409 Conflict error occurs when the request does not represent a valid transition
8187
for an existing alarm, such as attempting to clear an alarm which is already clear,
8288
or attempting to set an alarm which is already set at the given severity level.
89+
This error can be suppressed by setting ignore_conflict=True.
8390
"""
91+
if ignore_conflict:
92+
try:
93+
return self._create_or_update_alarm(request)
94+
except core.ApiException as e:
95+
if e.http_status_code == 409:
96+
return None
97+
raise
98+
else:
99+
return self._create_or_update_alarm(request)
100+
101+
@post("instances", return_key="instanceId")
102+
def _create_or_update_alarm(
103+
self, request: models.CreateOrUpdateAlarmRequest
104+
) -> str:
105+
"""Internal implementation of create_or_update_alarm."""
84106
...
85107

86108
@get("instances/{instance_id}", args=[Path("instance_id")])

tests/integration/alarm/test_alarm_client.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,3 +454,56 @@ def test__create_alarm_with_invalid_severity__raises_ApiException_BadRequest(
454454
)
455455
with pytest.raises(ApiException, match="Bad Request"):
456456
client.create_or_update_alarm(request)
457+
458+
def test__create_or_update_alarm_with_conflict_and_ignore_conflict_true__returns_none(
459+
self,
460+
client: AlarmClient,
461+
create_alarms: Callable[[str, int, str], str],
462+
unique_identifier: Callable[[], str],
463+
):
464+
"""Test that ignore_conflict=True returns None on 409 Conflict."""
465+
alarm_id = unique_identifier()
466+
467+
# Create an alarm
468+
create_alarms(alarm_id, AlarmSeverityLevel.HIGH, "Initial Condition")
469+
470+
# Try to create another alarm with the same alarm_id and same severity (would cause 409)
471+
duplicate_request = CreateOrUpdateAlarmRequest(
472+
alarm_id=alarm_id,
473+
transition=SetAlarmTransition(
474+
occurred_at=datetime.now(timezone.utc),
475+
severity_level=AlarmSeverityLevel.HIGH,
476+
condition="Initial Condition",
477+
),
478+
)
479+
480+
# With ignore_conflict=True, should return None instead of raising exception
481+
result = client.create_or_update_alarm(duplicate_request, ignore_conflict=True)
482+
assert result is None
483+
484+
def test__create_or_update_alarm_with_conflict_and_ignore_conflict_false__raises_exception(
485+
self,
486+
client: AlarmClient,
487+
create_alarms: Callable[[str, int, str], str],
488+
unique_identifier: Callable[[], str],
489+
):
490+
"""Test that ignore_conflict=False (default) raises ApiException on 409 Conflict."""
491+
alarm_id = unique_identifier()
492+
493+
# Create an alarm
494+
create_alarms(alarm_id, AlarmSeverityLevel.HIGH, "Initial Condition")
495+
496+
# Try to create another alarm with the same alarm_id and same severity (would cause 409)
497+
duplicate_request = CreateOrUpdateAlarmRequest(
498+
alarm_id=alarm_id,
499+
transition=SetAlarmTransition(
500+
occurred_at=datetime.now(timezone.utc),
501+
severity_level=AlarmSeverityLevel.HIGH,
502+
condition="Initial Condition",
503+
),
504+
)
505+
506+
# With ignore_conflict=False (default), should raise ApiException
507+
with pytest.raises(ApiException) as exc_info:
508+
client.create_or_update_alarm(duplicate_request, ignore_conflict=False)
509+
assert exc_info.value.http_status_code == 409

0 commit comments

Comments
 (0)