diff --git a/netx_api/ume_client.py b/netx_api/ume_client.py index c8b2cb1..f5f1cb7 100644 --- a/netx_api/ume_client.py +++ b/netx_api/ume_client.py @@ -177,8 +177,9 @@ class UMEClient: def _client(self) -> httpx.Client: # Use explicit HTTPTransport to keep behavior consistent with onsite validation. # In this mode, requests run over HTTP/1.1 and avoid HTTP/2 negotiation issues. - transport = httpx.HTTPTransport() - return httpx.Client(verify=self.verify_tls, timeout=self.timeout_s, transport=transport) + transport = httpx.HTTPTransport(verify=self.verify_tls, http2=False) + return httpx.Client(transport=transport, timeout=self.timeout_s) + def _extract_token_and_ttl(self, payload: dict[str, Any]) -> tuple[str, int | None]: token = "" diff --git a/netx_api/ume_sync_service.py b/netx_api/ume_sync_service.py index 37f7c5a..07ae51e 100644 --- a/netx_api/ume_sync_service.py +++ b/netx_api/ume_sync_service.py @@ -282,8 +282,12 @@ def _sync_alarms_common( next_marker = str(diag.marker or "").strip() if is_end_of_reply: break - # marker paging: no marker and empty data means no next page. - if not next_marker and not rows: + # marker paging: if response header has no marker, treat as end of iteration. + # Some UME deployments omit marker when the first page already contains all rows. + if not next_marker: + if rows: + warnings.append("marker_missing_stop") + paging_note = "marker_missing_stop" break if not is_uncleared: diff --git a/tests/test_ume_sync.py b/tests/test_ume_sync.py index f538617..a2a5299 100644 --- a/tests/test_ume_sync.py +++ b/tests/test_ume_sync.py @@ -281,6 +281,32 @@ class UmeSyncServiceTests(unittest.TestCase): self.assertEqual(job.pulled_count, 1) self.assertIsNotNone(self.db.get(UmeAlarmCurrent, "AK-1")) + def test_sync_current_alarms_stop_when_marker_missing(self): + class _C: + def __init__(self): + self.calls = 0 + + def get_alarms(self, *, is_uncleared: bool, limit=None, marker=None): + self.calls += 1 + + class _D: + marker = "" + is_end_of_reply = None + + return ( + [ + {"alarmKey": "AK-1", "ne-id": "NE-1", "perceivedSeverity": "major", "isCleared": "false"}, + ], + _D(), + ) + + c = _C() + job, _ = sync_alarms_current(self.db, c, trigger_mode="manual") + self.assertEqual(job.status, "done") + self.assertEqual(job.pulled_count, 1) + self.assertEqual(c.calls, 1) + self.assertIsNotNone(self.db.get(UmeAlarmCurrent, "AK-1")) + def test_derive_ne_id_from_alarmkey_formats(self): alarm_hash = {"alarmkey": "00ceb960-1b62-478e-8303-0935ffea1d28#99010"} alarm_csv = {"alarmkey": "00ceb960-1b62-478e-8303-0935ffea1d28, 4237, 79"}