libreclient 0.1.0__py3-none-any.whl

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.
Files changed (73) hide show
  1. libreclient/__init__.py +10 -0
  2. libreclient/__init__.pyi +4 -0
  3. libreclient/_base_client.py +70 -0
  4. libreclient/_route_types.py +0 -0
  5. libreclient/client.py +325 -0
  6. libreclient/config.py +59 -0
  7. libreclient/models/__init__.py +83 -0
  8. libreclient/models/_base.py +35 -0
  9. libreclient/models/alerts.py +31 -0
  10. libreclient/models/arp.py +13 -0
  11. libreclient/models/bills.py +21 -0
  12. libreclient/models/device_groups.py +19 -0
  13. libreclient/models/devices.py +41 -0
  14. libreclient/models/index.py +15 -0
  15. libreclient/models/inventory.py +15 -0
  16. libreclient/models/locations.py +15 -0
  17. libreclient/models/logs.py +13 -0
  18. libreclient/models/poller_groups.py +15 -0
  19. libreclient/models/pollers.py +13 -0
  20. libreclient/models/port_groups.py +13 -0
  21. libreclient/models/port_security.py +15 -0
  22. libreclient/models/portgroups.py +9 -0
  23. libreclient/models/ports.py +41 -0
  24. libreclient/models/routing.py +13 -0
  25. libreclient/models/services.py +18 -0
  26. libreclient/models/switching.py +15 -0
  27. libreclient/models/system.py +13 -0
  28. libreclient/py.typed +0 -0
  29. libreclient/routes/__init__.py +79 -0
  30. libreclient/routes/__init__.pyi +79 -0
  31. libreclient/routes/_synchronicity.py +7 -0
  32. libreclient/routes/_types.py +65 -0
  33. libreclient/routes/alerts.py +215 -0
  34. libreclient/routes/alerts.pyi +220 -0
  35. libreclient/routes/arp.py +35 -0
  36. libreclient/routes/arp.pyi +30 -0
  37. libreclient/routes/bills.py +167 -0
  38. libreclient/routes/bills.pyi +166 -0
  39. libreclient/routes/device_groups.py +204 -0
  40. libreclient/routes/device_groups.pyi +168 -0
  41. libreclient/routes/devices.py +720 -0
  42. libreclient/routes/devices.pyi +692 -0
  43. libreclient/routes/index.py +25 -0
  44. libreclient/routes/index.pyi +24 -0
  45. libreclient/routes/inventory.py +58 -0
  46. libreclient/routes/inventory.pyi +46 -0
  47. libreclient/routes/locations.py +119 -0
  48. libreclient/routes/locations.pyi +113 -0
  49. libreclient/routes/logs.py +165 -0
  50. libreclient/routes/logs.pyi +137 -0
  51. libreclient/routes/poller_groups.py +34 -0
  52. libreclient/routes/poller_groups.pyi +28 -0
  53. libreclient/routes/pollers.py +40 -0
  54. libreclient/routes/pollers.pyi +38 -0
  55. libreclient/routes/port_groups.py +87 -0
  56. libreclient/routes/port_groups.pyi +87 -0
  57. libreclient/routes/port_security.py +51 -0
  58. libreclient/routes/port_security.pyi +52 -0
  59. libreclient/routes/portgroups.py +64 -0
  60. libreclient/routes/portgroups.pyi +57 -0
  61. libreclient/routes/ports.py +138 -0
  62. libreclient/routes/ports.pyi +132 -0
  63. libreclient/routes/routing.py +247 -0
  64. libreclient/routes/routing.pyi +242 -0
  65. libreclient/routes/services.py +108 -0
  66. libreclient/routes/services.pyi +102 -0
  67. libreclient/routes/switching.py +98 -0
  68. libreclient/routes/switching.pyi +112 -0
  69. libreclient/routes/system.py +36 -0
  70. libreclient/routes/system.pyi +35 -0
  71. libreclient-0.1.0.dist-info/METADATA +319 -0
  72. libreclient-0.1.0.dist-info/RECORD +73 -0
  73. libreclient-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,10 @@
1
+ from . import models
2
+ from .client import LibreClientAsync, LibreClientSync
3
+ from .config import LibreConfig
4
+
5
+ __all__ = [
6
+ "LibreClientAsync",
7
+ "LibreClientSync",
8
+ "LibreConfig",
9
+ "models",
10
+ ]
@@ -0,0 +1,4 @@
1
+ from .client import LibreClientAsync, LibreClientSync
2
+ from .config import LibreConfig
3
+
4
+ __all__ = ["LibreClientAsync", "LibreClientSync", "LibreConfig"]
@@ -0,0 +1,70 @@
1
+ """Shared base client functionality for sync/async LibreNMS clients."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from abc import ABC, abstractmethod
7
+ from typing import Any, cast
8
+
9
+ from .config import LibreConfig
10
+
11
+
12
+ class BaseLibreClient(LibreConfig, ABC):
13
+ """Common transport helpers used by both sync and async clients.
14
+
15
+ Route namespaces implemented as async code can call these methods and work
16
+ with either concrete client implementation.
17
+ """
18
+
19
+ def _api_url(self, path: str) -> str:
20
+ suffix = path if path.startswith("/") else f"/{path}"
21
+ return f"{self.base_url}{suffix}"
22
+
23
+ @abstractmethod
24
+ async def _request_raw(self, method: str, url: str, **kwargs: Any) -> Any:
25
+ """Execute one HTTP request and return the raw response object."""
26
+
27
+ async def _request(
28
+ self, method: str, path: str, **kwargs: Any
29
+ ) -> dict | list:
30
+ response = await self._request_raw(
31
+ method, self._api_url(path), **kwargs
32
+ )
33
+ return await _json_from_response(response)
34
+
35
+ async def _request_bytes(
36
+ self, method: str, path: str, **kwargs: Any
37
+ ) -> bytes:
38
+ response = await self._request_raw(
39
+ method, self._api_url(path), **kwargs
40
+ )
41
+ payload = response.content
42
+ if inspect.isawaitable(payload):
43
+ payload = await payload
44
+ return cast("bytes", payload)
45
+
46
+ async def _get(self, path: str, **kwargs: Any) -> dict | list:
47
+ return await self._request("GET", path, **kwargs)
48
+
49
+ async def _post(self, path: str, **kwargs: Any) -> dict:
50
+ return await self._request("POST", path, **kwargs)
51
+
52
+ async def _put(self, path: str, **kwargs: Any) -> dict:
53
+ return await self._request("PUT", path, **kwargs)
54
+
55
+ async def _patch(self, path: str, **kwargs: Any) -> dict:
56
+ return await self._request("PATCH", path, **kwargs)
57
+
58
+ async def _delete(self, path: str, **kwargs: Any) -> dict:
59
+ return await self._request("DELETE", path, **kwargs)
60
+
61
+ async def _get_bytes(self, path: str, **kwargs: Any) -> bytes:
62
+ return await self._request_bytes("GET", path, **kwargs)
63
+
64
+
65
+ async def _json_from_response(response: Any) -> dict | list:
66
+ """Normalize niquests response.json() for sync/async response types."""
67
+ payload = response.json()
68
+ if inspect.isawaitable(payload):
69
+ payload = await payload
70
+ return cast("dict", payload)
File without changes
libreclient/client.py ADDED
@@ -0,0 +1,325 @@
1
+ """
2
+ LibreNMS API clients — synchronous and asynchronous.
3
+ """
4
+
5
+ from typing import Any
6
+
7
+ import niquests
8
+ from pydantic import PrivateAttr
9
+
10
+ from ._base_client import BaseLibreClient
11
+ from .routes import (
12
+ AlertsAsync,
13
+ AlertsSync,
14
+ ArpAsync,
15
+ ArpSync,
16
+ BillsAsync,
17
+ BillsSync,
18
+ DeviceGroupsAsync,
19
+ DeviceGroupsSync,
20
+ DevicesAsync,
21
+ DevicesSync,
22
+ IndexAsync,
23
+ IndexSync,
24
+ InventoryAsync,
25
+ InventorySync,
26
+ LocationsAsync,
27
+ LocationsSync,
28
+ LogsAsync,
29
+ LogsSync,
30
+ PollerGroupsAsync,
31
+ PollerGroupsSync,
32
+ PollersAsync,
33
+ PollersSync,
34
+ PortGroupsAsync,
35
+ PortgroupsAsync,
36
+ PortGroupsSync,
37
+ PortgroupsSync,
38
+ PortsAsync,
39
+ PortSecurityAsync,
40
+ PortSecuritySync,
41
+ PortsSync,
42
+ RoutingAsync,
43
+ RoutingSync,
44
+ ServicesAsync,
45
+ ServicesSync,
46
+ SwitchingAsync,
47
+ SwitchingSync,
48
+ SystemAsync,
49
+ SystemSync,
50
+ )
51
+
52
+
53
+ class LibreClientSync(BaseLibreClient):
54
+ """Synchronous LibreNMS API client backed by :class:`niquests.Session`."""
55
+
56
+ _session: niquests.Session = PrivateAttr()
57
+ _alerts: AlertsSync = PrivateAttr()
58
+ _arp: ArpSync = PrivateAttr()
59
+ _bills: BillsSync = PrivateAttr()
60
+ _device_groups: DeviceGroupsSync = PrivateAttr()
61
+ _devices: DevicesSync = PrivateAttr()
62
+ _index: IndexSync = PrivateAttr()
63
+ _inventory: InventorySync = PrivateAttr()
64
+ _locations: LocationsSync = PrivateAttr()
65
+ _logs: LogsSync = PrivateAttr()
66
+ _poller_groups: PollerGroupsSync = PrivateAttr()
67
+ _pollers: PollersSync = PrivateAttr()
68
+ _port_groups: PortGroupsSync = PrivateAttr()
69
+ _port_security: PortSecuritySync = PrivateAttr()
70
+ _portgroups: PortgroupsSync = PrivateAttr()
71
+ _ports: PortsSync = PrivateAttr()
72
+ _routing: RoutingSync = PrivateAttr()
73
+ _services: ServicesSync = PrivateAttr()
74
+ _switching: SwitchingSync = PrivateAttr()
75
+ _system: SystemSync = PrivateAttr()
76
+
77
+ def model_post_init(self, __context) -> None:
78
+ self._session = niquests.Session()
79
+ self._session.headers.update({"X-Auth-Token": self.token})
80
+ self._session.verify = self.verify_ssl
81
+ self._alerts = AlertsSync(self)
82
+ self._arp = ArpSync(self)
83
+ self._bills = BillsSync(self)
84
+ self._device_groups = DeviceGroupsSync(self)
85
+ self._devices = DevicesSync(self)
86
+ self._index = IndexSync(self)
87
+ self._inventory = InventorySync(self)
88
+ self._locations = LocationsSync(self)
89
+ self._logs = LogsSync(self)
90
+ self._poller_groups = PollerGroupsSync(self)
91
+ self._pollers = PollersSync(self)
92
+ self._port_groups = PortGroupsSync(self)
93
+ self._port_security = PortSecuritySync(self)
94
+ self._portgroups = PortgroupsSync(self)
95
+ self._ports = PortsSync(self)
96
+ self._routing = RoutingSync(self)
97
+ self._services = ServicesSync(self)
98
+ self._switching = SwitchingSync(self)
99
+ self._system = SystemSync(self)
100
+
101
+ async def _request_raw(self, method: str, url: str, **kwargs) -> Any:
102
+ return self._session.request(method, url, **kwargs)
103
+
104
+ @property
105
+ def alerts(self) -> AlertsSync:
106
+ return self._alerts
107
+
108
+ @property
109
+ def arp(self) -> ArpSync:
110
+ return self._arp
111
+
112
+ @property
113
+ def bills(self) -> BillsSync:
114
+ return self._bills
115
+
116
+ @property
117
+ def device_groups(self) -> DeviceGroupsSync:
118
+ return self._device_groups
119
+
120
+ @property
121
+ def devices(self) -> DevicesSync:
122
+ return self._devices
123
+
124
+ @property
125
+ def index(self) -> IndexSync:
126
+ return self._index
127
+
128
+ @property
129
+ def inventory(self) -> InventorySync:
130
+ return self._inventory
131
+
132
+ @property
133
+ def locations(self) -> LocationsSync:
134
+ return self._locations
135
+
136
+ @property
137
+ def logs(self) -> LogsSync:
138
+ return self._logs
139
+
140
+ @property
141
+ def poller_groups(self) -> PollerGroupsSync:
142
+ return self._poller_groups
143
+
144
+ @property
145
+ def pollers(self) -> PollersSync:
146
+ return self._pollers
147
+
148
+ @property
149
+ def port_groups(self) -> PortGroupsSync:
150
+ return self._port_groups
151
+
152
+ @property
153
+ def port_security(self) -> PortSecuritySync:
154
+ return self._port_security
155
+
156
+ @property
157
+ def portgroups(self) -> PortgroupsSync:
158
+ return self._portgroups
159
+
160
+ @property
161
+ def ports(self) -> PortsSync:
162
+ return self._ports
163
+
164
+ @property
165
+ def routing(self) -> RoutingSync:
166
+ return self._routing
167
+
168
+ @property
169
+ def services(self) -> ServicesSync:
170
+ return self._services
171
+
172
+ @property
173
+ def switching(self) -> SwitchingSync:
174
+ return self._switching
175
+
176
+ @property
177
+ def system(self) -> SystemSync:
178
+ return self._system
179
+
180
+ def close(self) -> None:
181
+ self._session.close()
182
+
183
+ def __enter__(self) -> "LibreClientSync":
184
+ return self
185
+
186
+ def __exit__(self, *_) -> None:
187
+ self.close()
188
+
189
+
190
+ class LibreClientAsync(BaseLibreClient):
191
+ """Asynchronous LibreNMS API client backed by :class:`niquests.AsyncSession`."""
192
+
193
+ _session: niquests.AsyncSession = PrivateAttr()
194
+ _alerts: AlertsAsync = PrivateAttr()
195
+ _arp: ArpAsync = PrivateAttr()
196
+ _bills: BillsAsync = PrivateAttr()
197
+ _device_groups: DeviceGroupsAsync = PrivateAttr()
198
+ _devices: DevicesAsync = PrivateAttr()
199
+ _index: IndexAsync = PrivateAttr()
200
+ _inventory: InventoryAsync = PrivateAttr()
201
+ _locations: LocationsAsync = PrivateAttr()
202
+ _logs: LogsAsync = PrivateAttr()
203
+ _poller_groups: PollerGroupsAsync = PrivateAttr()
204
+ _pollers: PollersAsync = PrivateAttr()
205
+ _port_groups: PortGroupsAsync = PrivateAttr()
206
+ _port_security: PortSecurityAsync = PrivateAttr()
207
+ _portgroups: PortgroupsAsync = PrivateAttr()
208
+ _ports: PortsAsync = PrivateAttr()
209
+ _routing: RoutingAsync = PrivateAttr()
210
+ _services: ServicesAsync = PrivateAttr()
211
+ _switching: SwitchingAsync = PrivateAttr()
212
+ _system: SystemAsync = PrivateAttr()
213
+
214
+ def model_post_init(self, __context) -> None:
215
+ self._session = niquests.AsyncSession()
216
+ self._session.headers.update({"X-Auth-Token": self.token})
217
+ self._session.verify = self.verify_ssl
218
+
219
+ self._alerts = AlertsAsync(self)
220
+ self._arp = ArpAsync(self)
221
+ self._bills = BillsAsync(self)
222
+ self._device_groups = DeviceGroupsAsync(self)
223
+ self._devices = DevicesAsync(self)
224
+ self._index = IndexAsync(self)
225
+ self._inventory = InventoryAsync(self)
226
+ self._locations = LocationsAsync(self)
227
+ self._logs = LogsAsync(self)
228
+ self._poller_groups = PollerGroupsAsync(self)
229
+ self._pollers = PollersAsync(self)
230
+ self._port_groups = PortGroupsAsync(self)
231
+ self._port_security = PortSecurityAsync(self)
232
+ self._portgroups = PortgroupsAsync(self)
233
+ self._ports = PortsAsync(self)
234
+ self._routing = RoutingAsync(self)
235
+ self._services = ServicesAsync(self)
236
+ self._switching = SwitchingAsync(self)
237
+ self._system = SystemAsync(self)
238
+
239
+ async def _request_raw(self, method: str, url: str, **kwargs) -> Any:
240
+ return await self._session.request(method, url, **kwargs)
241
+
242
+ @property
243
+ def alerts(self) -> AlertsAsync:
244
+ return self._alerts
245
+
246
+ @property
247
+ def arp(self) -> ArpAsync:
248
+ return self._arp
249
+
250
+ @property
251
+ def bills(self) -> BillsAsync:
252
+ return self._bills
253
+
254
+ @property
255
+ def device_groups(self) -> DeviceGroupsAsync:
256
+ return self._device_groups
257
+
258
+ @property
259
+ def devices(self) -> DevicesAsync:
260
+ return self._devices
261
+
262
+ @property
263
+ def index(self) -> IndexAsync:
264
+ return self._index
265
+
266
+ @property
267
+ def inventory(self) -> InventoryAsync:
268
+ return self._inventory
269
+
270
+ @property
271
+ def locations(self) -> LocationsAsync:
272
+ return self._locations
273
+
274
+ @property
275
+ def logs(self) -> LogsAsync:
276
+ return self._logs
277
+
278
+ @property
279
+ def poller_groups(self) -> PollerGroupsAsync:
280
+ return self._poller_groups
281
+
282
+ @property
283
+ def pollers(self) -> PollersAsync:
284
+ return self._pollers
285
+
286
+ @property
287
+ def port_groups(self) -> PortGroupsAsync:
288
+ return self._port_groups
289
+
290
+ @property
291
+ def port_security(self) -> PortSecurityAsync:
292
+ return self._port_security
293
+
294
+ @property
295
+ def portgroups(self) -> PortgroupsAsync:
296
+ return self._portgroups
297
+
298
+ @property
299
+ def ports(self) -> PortsAsync:
300
+ return self._ports
301
+
302
+ @property
303
+ def routing(self) -> RoutingAsync:
304
+ return self._routing
305
+
306
+ @property
307
+ def services(self) -> ServicesAsync:
308
+ return self._services
309
+
310
+ @property
311
+ def switching(self) -> SwitchingAsync:
312
+ return self._switching
313
+
314
+ @property
315
+ def system(self) -> SystemAsync:
316
+ return self._system
317
+
318
+ async def close(self) -> None:
319
+ await self._session.close()
320
+
321
+ async def __aenter__(self) -> "LibreClientAsync":
322
+ return self
323
+
324
+ async def __aexit__(self, *_) -> None:
325
+ await self.close()
libreclient/config.py ADDED
@@ -0,0 +1,59 @@
1
+ """
2
+ LibreNMS client configuration via Pydantic Settings.
3
+ """
4
+
5
+ import re
6
+
7
+ from pydantic import AnyHttpUrl, Field, model_validator
8
+ from pydantic_settings import BaseSettings, SettingsConfigDict
9
+
10
+
11
+ class LibreConfig(BaseSettings):
12
+ """Configuration for the LibreNMS API client.
13
+
14
+ Values can be supplied directly or via environment variables prefixed
15
+ with ``LIBRENMS_`` (e.g. ``LIBRENMS_URL``, ``LIBRENMS_TOKEN``).
16
+ """
17
+
18
+ model_config = SettingsConfigDict(
19
+ env_prefix="LIBRENMS_",
20
+ env_file=".env",
21
+ env_file_encoding="utf-8",
22
+ extra="ignore",
23
+ )
24
+
25
+ url: AnyHttpUrl = Field(
26
+ ...,
27
+ description="Base URL of the LibreNMS instance, e.g. https://librenms.example.com",
28
+ )
29
+ token: str = Field(..., description="LibreNMS API token (X-Auth-Token)")
30
+ verify_ssl: bool = Field(True, description="Verify TLS/SSL certificates")
31
+ api_version: str = Field(
32
+ "v0", description="API version segment, e.g. 'v0'"
33
+ )
34
+
35
+ @model_validator(mode="before")
36
+ @classmethod
37
+ def normalize_url_and_api_version(cls, data):
38
+ """If `url` includes `/api/vN`, strip it from url and set `api_version`."""
39
+ if not isinstance(data, dict):
40
+ return data
41
+
42
+ raw_url = data.get("url")
43
+ if raw_url is None:
44
+ return data
45
+
46
+ url_str = str(raw_url)
47
+ # Find embedded API version path, e.g. https://host/api/v1
48
+ match = re.search(r"/api/(v\d+)(?:/|$)", url_str)
49
+ if not match:
50
+ return data
51
+
52
+ data["api_version"] = match.group(1)
53
+ data["url"] = url_str[: match.start()].rstrip("/")
54
+ return data
55
+
56
+ @property
57
+ def base_url(self) -> str:
58
+ """Full API base URL, e.g. https://librenms.example.com/api/v0."""
59
+ return f"{str(self.url).rstrip('/')}/api/{self.api_version}"
@@ -0,0 +1,83 @@
1
+ """Pydantic models for LibreNMS API responses.
2
+
3
+ Import from here for convenience, or directly from sub-modules for clarity.
4
+ """
5
+
6
+ # Base models (shared across all routes)
7
+ from ._base import ApiResponse, ApiResponseWithId, ListResponse
8
+
9
+ # Per-route response models
10
+ from .alerts import (
11
+ AlertsResponse,
12
+ AlertTemplateCreatedResponse,
13
+ AlertTemplatesResponse,
14
+ RulesResponse,
15
+ )
16
+ from .arp import ArpResponse
17
+ from .bills import BillHistoryResponse, BillsResponse
18
+ from .device_groups import DeviceGroupDevicesResponse, DeviceGroupsResponse
19
+ from .devices import (
20
+ ComponentsResponse,
21
+ DeviceFdbResponse,
22
+ DevicePortsResponse,
23
+ DeviceResponse,
24
+ DevicesResponse,
25
+ )
26
+ from .index import IndexResponse
27
+ from .inventory import InventoryResponse
28
+ from .locations import LocationsResponse
29
+ from .logs import LogsResponse
30
+ from .poller_groups import PollerGroupsResponse
31
+ from .pollers import PollersResponse
32
+ from .port_groups import PortGroupsResponse
33
+ from .port_security import PortSecurityResponse
34
+ from .portgroups import PortgroupsResponse
35
+ from .ports import (
36
+ PortDescriptionResponse,
37
+ PortIpResponse,
38
+ PortResponse,
39
+ PortsResponse,
40
+ PortTransceiverResponse,
41
+ )
42
+ from .routing import RoutingResponse
43
+ from .services import ServicesResponse
44
+ from .switching import SwitchingResponse
45
+ from .system import SystemResponse
46
+
47
+ __all__ = [
48
+ "AlertTemplateCreatedResponse",
49
+ "AlertTemplatesResponse",
50
+ "AlertsResponse",
51
+ "ApiResponse",
52
+ "ApiResponseWithId",
53
+ "ArpResponse",
54
+ "BillHistoryResponse",
55
+ "BillsResponse",
56
+ "ComponentsResponse",
57
+ "DeviceFdbResponse",
58
+ "DeviceGroupDevicesResponse",
59
+ "DeviceGroupsResponse",
60
+ "DevicePortsResponse",
61
+ "DeviceResponse",
62
+ "DevicesResponse",
63
+ "IndexResponse",
64
+ "InventoryResponse",
65
+ "ListResponse",
66
+ "LocationsResponse",
67
+ "LogsResponse",
68
+ "PollerGroupsResponse",
69
+ "PollersResponse",
70
+ "PortDescriptionResponse",
71
+ "PortGroupsResponse",
72
+ "PortIpResponse",
73
+ "PortResponse",
74
+ "PortSecurityResponse",
75
+ "PortTransceiverResponse",
76
+ "PortgroupsResponse",
77
+ "PortsResponse",
78
+ "RoutingResponse",
79
+ "RulesResponse",
80
+ "ServicesResponse",
81
+ "SwitchingResponse",
82
+ "SystemResponse",
83
+ ]
@@ -0,0 +1,35 @@
1
+ """
2
+ Base response models shared across all route modules.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+
12
+ class ApiResponse(BaseModel):
13
+ """Base response envelope — every API call returns at least these."""
14
+
15
+ model_config = ConfigDict(populate_by_name=True)
16
+
17
+ status: str = "ok"
18
+ message: str = ""
19
+
20
+
21
+ class ApiResponseWithId(ApiResponse):
22
+ """Response that includes a created/affected resource id."""
23
+
24
+ id: int
25
+
26
+
27
+ class ListResponse(ApiResponse):
28
+ """Response containing a counted list of resources.
29
+
30
+ Subclass this and override ``data`` with the appropriate
31
+ ``validation_alias`` for the API's response key.
32
+ """
33
+
34
+ count: int = 0
35
+ data: list[Any] = Field(default_factory=list)
@@ -0,0 +1,31 @@
1
+ """Response models for Alerts routes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pydantic import Field
6
+
7
+ from ._base import ApiResponseWithId, ListResponse
8
+
9
+
10
+ class AlertsResponse(ListResponse):
11
+ """Response from get_alert / list_alerts."""
12
+
13
+ data: list[dict] = Field(default_factory=list, validation_alias="alerts")
14
+
15
+
16
+ class RulesResponse(ListResponse):
17
+ """Response from get_alert_rule / list_alert_rules."""
18
+
19
+ data: list[dict] = Field(default_factory=list, validation_alias="rules")
20
+
21
+
22
+ class AlertTemplatesResponse(ListResponse):
23
+ """Response from get_alert_template / list_alert_templates."""
24
+
25
+ data: list[dict] = Field(
26
+ default_factory=list, validation_alias="alert_templates"
27
+ )
28
+
29
+
30
+ class AlertTemplateCreatedResponse(ApiResponseWithId):
31
+ """Response from add_alert_template (returns the new template id)."""
@@ -0,0 +1,13 @@
1
+ """Response models for ARP routes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pydantic import Field
6
+
7
+ from ._base import ListResponse
8
+
9
+
10
+ class ArpResponse(ListResponse):
11
+ """Response from list_arp."""
12
+
13
+ data: list[dict] = Field(default_factory=list, validation_alias="arp")
@@ -0,0 +1,21 @@
1
+ """Response models for Bills routes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pydantic import Field
6
+
7
+ from ._base import ListResponse
8
+
9
+
10
+ class BillsResponse(ListResponse):
11
+ """Response from list_bills / get_bill."""
12
+
13
+ data: list[dict] = Field(default_factory=list, validation_alias="bills")
14
+
15
+
16
+ class BillHistoryResponse(ListResponse):
17
+ """Response from get_bill_history."""
18
+
19
+ data: list[dict] = Field(
20
+ default_factory=list, validation_alias="bill_history"
21
+ )
@@ -0,0 +1,19 @@
1
+ """Response models for Device Groups routes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pydantic import Field
6
+
7
+ from ._base import ListResponse
8
+
9
+
10
+ class DeviceGroupsResponse(ListResponse):
11
+ """Response from get_devicegroups."""
12
+
13
+ data: list[dict] = Field(default_factory=list, validation_alias="groups")
14
+
15
+
16
+ class DeviceGroupDevicesResponse(ListResponse):
17
+ """Response from get_devices_by_group."""
18
+
19
+ data: list[dict] = Field(default_factory=list, validation_alias="devices")