lvi-api-client 0.2.6__tar.gz → 0.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/PKG-INFO +1 -1
- lvi_api_client-0.2.7/VERSION +1 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/src/logicvein/lvi_api_client.py +134 -5
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/src/lvi_api_client.egg-info/PKG-INFO +1 -1
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/src/lvi_api_client.egg-info/SOURCES.txt +1 -0
- lvi_api_client-0.2.7/tests/test_check_version_bump.py +61 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/tests/test_server_request_shapes.py +67 -0
- lvi_api_client-0.2.6/VERSION +0 -1
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/LICENSE +0 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/README.md +0 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/pyproject.toml +0 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/setup.cfg +0 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/src/logicvein/__init__.py +0 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/src/lvi_api_client.egg-info/dependency_links.txt +0 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/src/lvi_api_client.egg-info/requires.txt +0 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/src/lvi_api_client.egg-info/top_level.txt +0 -0
- {lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/tests/test_lvi_api_client.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.7
|
|
@@ -1840,7 +1840,8 @@ class LogicVeinAPIClient:
|
|
|
1840
1840
|
"summary": str - Incident summary,
|
|
1841
1841
|
"severity": str - "INFO", "WARNING", "ERROR", or "CRITICAL",
|
|
1842
1842
|
"priority": str - "LOW", "MEDIUM", "HIGH", or "CRITICAL",
|
|
1843
|
-
"status": str - "OPEN", "
|
|
1843
|
+
"status": str - "OPEN", "RESOLVED" or "DEFERRED" seen on a server; whether
|
|
1844
|
+
"WORKING" or "CLOSED" exist has not been measured,
|
|
1844
1845
|
"resolution": str - "PENDING" or "RESOLVED",
|
|
1845
1846
|
"assignee": str - Assigned username (empty string if unassigned),
|
|
1846
1847
|
"labels": list[str] - Label tags,
|
|
@@ -1875,14 +1876,15 @@ class LogicVeinAPIClient:
|
|
|
1875
1876
|
# Incidents
|
|
1876
1877
|
# =====================================================
|
|
1877
1878
|
|
|
1878
|
-
def get_incident_by_id(self, incident_id: int) -> Dict[str, Any]:
|
|
1879
|
+
def get_incident_by_id(self, incident_id: int) -> Optional[Dict[str, Any]]:
|
|
1879
1880
|
"""Get an incident by its ID from the LogicVein server.
|
|
1880
1881
|
|
|
1881
1882
|
Args:
|
|
1882
1883
|
incident_id (int): The ID of the incident.
|
|
1883
1884
|
|
|
1884
1885
|
Returns:
|
|
1885
|
-
Dict[str, Any]: The incident
|
|
1886
|
+
Dict[str, Any]: The incident, or None for an unknown ID, which the server answers with
|
|
1887
|
+
a null result. An empty dictionary only when the server gives no result at all.
|
|
1886
1888
|
"""
|
|
1887
1889
|
response = self.call("Incidents.getIncidentById", incidentId=incident_id)
|
|
1888
1890
|
|
|
@@ -2549,6 +2551,8 @@ class LogicVeinAPIClient:
|
|
|
2549
2551
|
def delete_managed_network(self, network_name: str) -> bool:
|
|
2550
2552
|
"""Delete a managed network from the LogicVein server.
|
|
2551
2553
|
|
|
2554
|
+
An alert policy whose only network this is goes with it.
|
|
2555
|
+
|
|
2552
2556
|
Args:
|
|
2553
2557
|
network_name (str): The name of the managed network to delete.
|
|
2554
2558
|
|
|
@@ -3648,6 +3652,8 @@ class LogicVeinAPIClient:
|
|
|
3648
3652
|
"""Get actions configured for an alert policy.
|
|
3649
3653
|
|
|
3650
3654
|
Calls Collection.getActionsForPolicy. Returns the result list directly (not paginated).
|
|
3655
|
+
Settings come back as stored, secrets included: an exec action's ``password`` and a
|
|
3656
|
+
trap action's ``community`` are not masked, and the web UI sends them back unchanged.
|
|
3651
3657
|
Each action has:
|
|
3652
3658
|
{
|
|
3653
3659
|
"actionId": int - Action ID,
|
|
@@ -3677,6 +3683,10 @@ class LogicVeinAPIClient:
|
|
|
3677
3683
|
Returns the saved actions with assigned actionIds (new actions use -1 as input id,
|
|
3678
3684
|
which is replaced with the assigned ID in the response).
|
|
3679
3685
|
|
|
3686
|
+
The saved actions **replace** all of the policy's actions; they are not added to them.
|
|
3687
|
+
An empty list does nothing, so it cannot clear a policy's actions. ``username`` is
|
|
3688
|
+
filled in with the caller's when left out.
|
|
3689
|
+
|
|
3680
3690
|
Each action object should have:
|
|
3681
3691
|
{
|
|
3682
3692
|
"actionId": int - Use -1 for new actions,
|
|
@@ -4288,6 +4298,33 @@ class LogicVeinAPIClient:
|
|
|
4288
4298
|
Returns:
|
|
4289
4299
|
List[Dict[str, Any]]: All matching alert policies.
|
|
4290
4300
|
"""
|
|
4301
|
+
return self._search_alert_policies(managed_networks, queries, sort_column, descending, page_size, strict=False)
|
|
4302
|
+
|
|
4303
|
+
def _search_alert_policies(
|
|
4304
|
+
self,
|
|
4305
|
+
managed_networks: List[str],
|
|
4306
|
+
queries: List[str],
|
|
4307
|
+
sort_column: str,
|
|
4308
|
+
descending: bool,
|
|
4309
|
+
page_size: int,
|
|
4310
|
+
strict: bool,
|
|
4311
|
+
) -> List[Dict[str, Any]]:
|
|
4312
|
+
"""Read every page of alert policies.
|
|
4313
|
+
|
|
4314
|
+
Args:
|
|
4315
|
+
managed_networks (List[str]): Networks to search. Empty for all.
|
|
4316
|
+
queries (List[str]): Query filters.
|
|
4317
|
+
sort_column (str): Column to sort by.
|
|
4318
|
+
descending (bool): Sort descending if True.
|
|
4319
|
+
page_size (int): Records per API call.
|
|
4320
|
+
strict (bool): Raise on an answer with no policies list, rather than stopping there.
|
|
4321
|
+
|
|
4322
|
+
Returns:
|
|
4323
|
+
List[Dict[str, Any]]: All matching alert policies.
|
|
4324
|
+
|
|
4325
|
+
Raises:
|
|
4326
|
+
ValueError: If ``strict`` and an answer has no policies list.
|
|
4327
|
+
"""
|
|
4291
4328
|
all_policies: List[Dict[str, Any]] = []
|
|
4292
4329
|
offset = 0
|
|
4293
4330
|
|
|
@@ -4306,8 +4343,11 @@ class LogicVeinAPIClient:
|
|
|
4306
4343
|
}
|
|
4307
4344
|
response = self.call("Collection.searchAlertPolicies", **params)
|
|
4308
4345
|
|
|
4309
|
-
|
|
4310
|
-
|
|
4346
|
+
result = response.get("result") if isinstance(response, dict) else None
|
|
4347
|
+
if isinstance(result, dict) and isinstance(result.get("policies"), list):
|
|
4348
|
+
batch = result["policies"]
|
|
4349
|
+
elif strict:
|
|
4350
|
+
raise ValueError("Collection.searchAlertPolicies did not answer with a list of policies")
|
|
4311
4351
|
else:
|
|
4312
4352
|
break
|
|
4313
4353
|
|
|
@@ -4325,6 +4365,12 @@ class LogicVeinAPIClient:
|
|
|
4325
4365
|
|
|
4326
4366
|
Calls Collection.saveAlertPolicy. The response mirrors the input policy object.
|
|
4327
4367
|
Use policyId=-1 (or omit) to create a new policy; provide an existing policyId to update.
|
|
4368
|
+
The result carries the new policy's ID.
|
|
4369
|
+
|
|
4370
|
+
Saving an existing policy **removes all its actions**, although ``actionTypes`` still
|
|
4371
|
+
lists them: save them again with :meth:`save_alert_actions`, as the web UI does. Saving
|
|
4372
|
+
a name that is already in use makes a second policy of that name; check with
|
|
4373
|
+
:meth:`is_alert_policy_name_unique` first.
|
|
4328
4374
|
|
|
4329
4375
|
The policy object has:
|
|
4330
4376
|
{
|
|
@@ -4347,6 +4393,89 @@ class LogicVeinAPIClient:
|
|
|
4347
4393
|
return response["result"]
|
|
4348
4394
|
return None
|
|
4349
4395
|
|
|
4396
|
+
def delete_alert_policy(self, policy_id: int, reassign_policy_id: int = -1) -> bool:
|
|
4397
|
+
"""Delete an alert policy, with its actions.
|
|
4398
|
+
|
|
4399
|
+
Calls Collection.deleteAlertPolicy, with the parameters the web UI sends. The server
|
|
4400
|
+
refuses ``policyId`` alone. The UI asks first whether the policy is referenced
|
|
4401
|
+
(:meth:`is_alert_policy_referenced`); what the server does with the references of a
|
|
4402
|
+
referenced policy deleted with ``reassign_policy_id`` -1 has not been measured.
|
|
4403
|
+
|
|
4404
|
+
The server's answer is not relied on: the policies are read back afterwards. An answer
|
|
4405
|
+
that cannot show whether the policy is gone raises rather than reading as deleted, and
|
|
4406
|
+
errors from either call (``LogicVeinAPIError``, ``requests`` errors) propagate.
|
|
4407
|
+
|
|
4408
|
+
Args:
|
|
4409
|
+
policy_id (int): The ID of the policy to delete.
|
|
4410
|
+
reassign_policy_id (int): The policy to move its references to, or -1 for none.
|
|
4411
|
+
|
|
4412
|
+
Returns:
|
|
4413
|
+
bool: True if the policy is gone afterwards.
|
|
4414
|
+
|
|
4415
|
+
Raises:
|
|
4416
|
+
ValueError: If the read-back answer has no list of policies, or a policy without an ID.
|
|
4417
|
+
"""
|
|
4418
|
+
self.call("Collection.deleteAlertPolicy", policyId=policy_id, reassignPolicyId=reassign_policy_id)
|
|
4419
|
+
remaining = self._search_alert_policies([], [], "policyName", False, 500, strict=True)
|
|
4420
|
+
ids = [policy.get("policyId") if isinstance(policy, dict) else None for policy in remaining]
|
|
4421
|
+
if not all(isinstance(found, int) for found in ids):
|
|
4422
|
+
raise ValueError("Collection.searchAlertPolicies answered with a policy that has no ID")
|
|
4423
|
+
return policy_id not in ids
|
|
4424
|
+
|
|
4425
|
+
def is_alert_policy_referenced(self, policy_id: int) -> bool:
|
|
4426
|
+
"""Check whether anything refers to an alert policy.
|
|
4427
|
+
|
|
4428
|
+
Calls Collection.isAlertPolicyReferenced, which the web UI calls before deleting one.
|
|
4429
|
+
Monitor triggers name the policy they alert through, but they are not the only
|
|
4430
|
+
references counted: on the lab, a policy no trigger names also answered true. An
|
|
4431
|
+
unknown ID answers false.
|
|
4432
|
+
|
|
4433
|
+
Args:
|
|
4434
|
+
policy_id (int): The ID of the policy.
|
|
4435
|
+
|
|
4436
|
+
Returns:
|
|
4437
|
+
bool: True if the policy is referenced.
|
|
4438
|
+
|
|
4439
|
+
Raises:
|
|
4440
|
+
ValueError: If the server's answer is not true or false.
|
|
4441
|
+
"""
|
|
4442
|
+
response = self.call("Collection.isAlertPolicyReferenced", policyId=policy_id)
|
|
4443
|
+
referenced = response.get("result") if isinstance(response, dict) else None
|
|
4444
|
+
if not isinstance(referenced, bool):
|
|
4445
|
+
raise ValueError(f"Collection.isAlertPolicyReferenced did not answer true or false for {policy_id}")
|
|
4446
|
+
return referenced
|
|
4447
|
+
|
|
4448
|
+
def is_alert_policy_name_unique(
|
|
4449
|
+
self, policy_name: str, managed_networks: List[str], ignore_policy_id: int = -1
|
|
4450
|
+
) -> bool:
|
|
4451
|
+
"""Check whether an alert policy name is free in some networks.
|
|
4452
|
+
|
|
4453
|
+
Calls Collection.isAlertPolicyNameUnique, with the parameters the web UI sends as a
|
|
4454
|
+
name is typed. :meth:`save_alert_policy` does not check: it saves a second policy of
|
|
4455
|
+
a name in use.
|
|
4456
|
+
|
|
4457
|
+
Args:
|
|
4458
|
+
policy_name (str): The name to check.
|
|
4459
|
+
managed_networks (List[str]): The networks to check it in.
|
|
4460
|
+
ignore_policy_id (int): A policy to leave out, such as the one being renamed. -1 for none.
|
|
4461
|
+
|
|
4462
|
+
Returns:
|
|
4463
|
+
bool: True if the name is free.
|
|
4464
|
+
|
|
4465
|
+
Raises:
|
|
4466
|
+
ValueError: If the server's answer is not true or false.
|
|
4467
|
+
"""
|
|
4468
|
+
response = self.call(
|
|
4469
|
+
"Collection.isAlertPolicyNameUnique",
|
|
4470
|
+
policyName=policy_name,
|
|
4471
|
+
managedNetworks=managed_networks,
|
|
4472
|
+
ignorePolicyId=ignore_policy_id,
|
|
4473
|
+
)
|
|
4474
|
+
unique = response.get("result") if isinstance(response, dict) else None
|
|
4475
|
+
if not isinstance(unique, bool):
|
|
4476
|
+
raise ValueError(f"Collection.isAlertPolicyNameUnique did not answer true or false for {policy_name}")
|
|
4477
|
+
return unique
|
|
4478
|
+
|
|
4350
4479
|
# =====================================================
|
|
4351
4480
|
# Maps / Topology
|
|
4352
4481
|
# =====================================================
|
|
@@ -9,5 +9,6 @@ src/lvi_api_client.egg-info/SOURCES.txt
|
|
|
9
9
|
src/lvi_api_client.egg-info/dependency_links.txt
|
|
10
10
|
src/lvi_api_client.egg-info/requires.txt
|
|
11
11
|
src/lvi_api_client.egg-info/top_level.txt
|
|
12
|
+
tests/test_check_version_bump.py
|
|
12
13
|
tests/test_lvi_api_client.py
|
|
13
14
|
tests/test_server_request_shapes.py
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Tests for scripts/ci/check_version_bump.py."""
|
|
2
|
+
|
|
3
|
+
import importlib.util
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
_PATH = Path(__file__).resolve().parent.parent / "scripts" / "ci" / "check_version_bump.py"
|
|
9
|
+
_SPEC = importlib.util.spec_from_file_location("check_version_bump", _PATH)
|
|
10
|
+
check = importlib.util.module_from_spec(_SPEC)
|
|
11
|
+
_SPEC.loader.exec_module(check)
|
|
12
|
+
|
|
13
|
+
LOG = "# Changelog\n\n## [0.2.7] - 2026-11-08\n\n### Fixed\n- Something.\n\n## [0.2.6] - 2026-11-07\n"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_a_heading_for_the_version_passes():
|
|
17
|
+
assert check.changelog_problem(LOG, "0.2.7") == ""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.mark.parametrize(
|
|
21
|
+
"changelog",
|
|
22
|
+
[
|
|
23
|
+
"## [Unreleased]\n\n### Fixed\n- Something.\n\n## [0.2.6] - 2026-11-07\n",
|
|
24
|
+
"Fixed in [0.2.7], see below.\n## [0.2.6]\n",
|
|
25
|
+
"## [0.2.70] - 2026-11-08\n",
|
|
26
|
+
],
|
|
27
|
+
ids=["only unreleased", "not a heading", "another version"],
|
|
28
|
+
)
|
|
29
|
+
def test_no_heading_for_the_version_fails(changelog):
|
|
30
|
+
assert "no '## [0.2.7]' heading" in check.changelog_problem(changelog, "0.2.7")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _run(monkeypatch, tmp_path, changed, version, changelog, base_version="0.2.6"):
|
|
34
|
+
(tmp_path / "VERSION").write_text(version + "\n")
|
|
35
|
+
(tmp_path / "CHANGELOG.md").write_text(changelog)
|
|
36
|
+
monkeypatch.setattr(check, "REPO_ROOT", tmp_path)
|
|
37
|
+
|
|
38
|
+
def git(*args):
|
|
39
|
+
return "\n".join(changed) if args[0] == "diff" else base_version + "\n"
|
|
40
|
+
|
|
41
|
+
monkeypatch.setattr(check, "_git", git)
|
|
42
|
+
monkeypatch.setattr("sys.argv", ["check_version_bump.py", "--base", "abc123"])
|
|
43
|
+
return check.main()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_a_bump_with_entries_left_under_unreleased_fails(monkeypatch, tmp_path, capsys):
|
|
47
|
+
changelog = "## [Unreleased]\n\n### Fixed\n- Something.\n"
|
|
48
|
+
assert _run(monkeypatch, tmp_path, ["src/x.py", "VERSION", "CHANGELOG.md"], "0.2.7", changelog) == 1
|
|
49
|
+
assert "no '## [0.2.7]' heading" in capsys.readouterr().out
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_a_bump_without_a_src_change_still_needs_the_heading(monkeypatch, tmp_path):
|
|
53
|
+
assert _run(monkeypatch, tmp_path, ["VERSION"], "0.2.7", "## [0.2.6]\n") == 1
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_a_bump_with_its_heading_passes(monkeypatch, tmp_path):
|
|
57
|
+
assert _run(monkeypatch, tmp_path, ["src/x.py", "VERSION", "CHANGELOG.md"], "0.2.7", LOG) == 0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_a_docs_change_needs_nothing(monkeypatch, tmp_path):
|
|
61
|
+
assert _run(monkeypatch, tmp_path, ["CHANGELOG.md", "README.md"], "0.2.6", "## [Unreleased]\n") == 0
|
|
@@ -620,3 +620,70 @@ class TestDeployingASet:
|
|
|
620
620
|
with pytest.raises(ValueError):
|
|
621
621
|
client.associate_monitor_set(2, queries)
|
|
622
622
|
assert server.requests == []
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
class TestAlertPolicies:
|
|
626
|
+
"""Deleting alert policies and the checks the web UI makes around saving and deleting them."""
|
|
627
|
+
|
|
628
|
+
def test_a_delete_sends_what_the_ui_sends_and_reads_the_policies_back(self, connect):
|
|
629
|
+
client, server = connect(
|
|
630
|
+
{
|
|
631
|
+
"Collection.deleteAlertPolicy": None,
|
|
632
|
+
"Collection.searchAlertPolicies": {"policies": [{"policyId": 1, "policyName": "kept"}]},
|
|
633
|
+
}
|
|
634
|
+
)
|
|
635
|
+
assert client.delete_alert_policy(65) is True
|
|
636
|
+
assert server.sent("Collection.deleteAlertPolicy") == {"policyId": 65, "reassignPolicyId": -1}
|
|
637
|
+
assert server.sent("Collection.searchAlertPolicies")["managedNetworks"] == []
|
|
638
|
+
|
|
639
|
+
def test_a_policy_still_listed_after_a_delete_is_not_deleted(self, connect):
|
|
640
|
+
client, _ = connect(
|
|
641
|
+
{
|
|
642
|
+
"Collection.deleteAlertPolicy": True,
|
|
643
|
+
"Collection.searchAlertPolicies": {"policies": [{"policyId": 65, "policyName": "p"}]},
|
|
644
|
+
}
|
|
645
|
+
)
|
|
646
|
+
assert client.delete_alert_policy(65, reassign_policy_id=1) is False
|
|
647
|
+
|
|
648
|
+
@pytest.mark.parametrize(
|
|
649
|
+
"answer", [None, {}, {"policies": None}, {"policies": [{}]}, {"policies": [{"policyId": None}, "x"]}]
|
|
650
|
+
)
|
|
651
|
+
def test_a_failed_read_back_raises_rather_than_reading_as_deleted(self, connect, answer):
|
|
652
|
+
client, _ = connect({"Collection.deleteAlertPolicy": None, "Collection.searchAlertPolicies": answer})
|
|
653
|
+
with pytest.raises(ValueError):
|
|
654
|
+
client.delete_alert_policy(65)
|
|
655
|
+
|
|
656
|
+
def test_a_failed_search_still_reads_as_no_policies(self, connect):
|
|
657
|
+
client, _ = connect({"Collection.searchAlertPolicies": None})
|
|
658
|
+
assert client.search_alert_policies() == []
|
|
659
|
+
|
|
660
|
+
@pytest.mark.parametrize("answer", [True, False])
|
|
661
|
+
def test_a_reference_check_sends_the_id_and_returns_the_answer(self, connect, answer):
|
|
662
|
+
client, server = connect({"Collection.isAlertPolicyReferenced": answer})
|
|
663
|
+
assert client.is_alert_policy_referenced(27) is answer
|
|
664
|
+
assert server.sent("Collection.isAlertPolicyReferenced") == {"policyId": 27}
|
|
665
|
+
|
|
666
|
+
def test_a_reference_check_with_no_answer_raises_rather_than_reading_as_unreferenced(self, connect):
|
|
667
|
+
client, _ = connect({"Collection.isAlertPolicyReferenced": None})
|
|
668
|
+
with pytest.raises(ValueError):
|
|
669
|
+
client.is_alert_policy_referenced(27)
|
|
670
|
+
|
|
671
|
+
@pytest.mark.parametrize("answer", [True, False])
|
|
672
|
+
def test_a_name_check_sends_what_the_ui_sends_and_returns_the_answer(self, connect, answer):
|
|
673
|
+
client, server = connect({"Collection.isAlertPolicyNameUnique": answer})
|
|
674
|
+
assert client.is_alert_policy_name_unique("p1", ["Default"], ignore_policy_id=1) is answer
|
|
675
|
+
assert server.sent("Collection.isAlertPolicyNameUnique") == {
|
|
676
|
+
"policyName": "p1",
|
|
677
|
+
"managedNetworks": ["Default"],
|
|
678
|
+
"ignorePolicyId": 1,
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
def test_a_name_check_ignores_no_policy_by_default(self, connect):
|
|
682
|
+
client, server = connect({"Collection.isAlertPolicyNameUnique": True})
|
|
683
|
+
client.is_alert_policy_name_unique("p1", ["Default"])
|
|
684
|
+
assert server.sent("Collection.isAlertPolicyNameUnique")["ignorePolicyId"] == -1
|
|
685
|
+
|
|
686
|
+
def test_a_name_check_with_no_answer_raises_rather_than_reading_as_taken(self, connect):
|
|
687
|
+
client, _ = connect({"Collection.isAlertPolicyNameUnique": None})
|
|
688
|
+
with pytest.raises(ValueError):
|
|
689
|
+
client.is_alert_policy_name_unique("p1", ["Default"])
|
lvi_api_client-0.2.6/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.2.6
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lvi_api_client-0.2.6 → lvi_api_client-0.2.7}/src/lvi_api_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|