telemt-sdk 1.0.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.
telemt/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """Asynchronous Python SDK for Telemt."""
2
+
3
+ from importlib.metadata import version
4
+
5
+ from . import models
6
+ from .client import TelemtClient
7
+ from .exceptions import (
8
+ APIError,
9
+ AuthenticationError,
10
+ ConflictError,
11
+ InvalidResponseError,
12
+ NotFoundError,
13
+ PermissionDeniedError,
14
+ RequestTimeoutError,
15
+ TelemtError,
16
+ TransportError,
17
+ )
18
+
19
+ __version__ = version("telemt-sdk")
20
+
21
+ __all__ = [
22
+ "APIError",
23
+ "AuthenticationError",
24
+ "ConflictError",
25
+ "InvalidResponseError",
26
+ "NotFoundError",
27
+ "PermissionDeniedError",
28
+ "RequestTimeoutError",
29
+ "TelemtClient",
30
+ "TelemtError",
31
+ "TransportError",
32
+ "__version__",
33
+ "models",
34
+ ]
telemt/client.py ADDED
@@ -0,0 +1,177 @@
1
+ """An asynchronous, connection-pooling Telemt Control API client."""
2
+
3
+ from collections.abc import Mapping
4
+ from types import TracebackType
5
+ from typing import Self
6
+
7
+ import httpx
8
+ from pydantic import ValidationError
9
+
10
+ from .exceptions import (
11
+ APIError,
12
+ AuthenticationError,
13
+ ConflictError,
14
+ InvalidResponseError,
15
+ NotFoundError,
16
+ PermissionDeniedError,
17
+ RequestTimeoutError,
18
+ TransportError,
19
+ )
20
+ from .models.base import APIResponse, ErrorEnvelope, RequestModel
21
+ from .models.requests import PatchConfigRequest, PatchUserRequest
22
+ from .resources import (
23
+ ConfigResource,
24
+ HealthResource,
25
+ LimitsResource,
26
+ RuntimeResource,
27
+ SecurityResource,
28
+ StatsResource,
29
+ SystemResource,
30
+ UsersResource,
31
+ )
32
+
33
+
34
+ class TelemtClient:
35
+ """Use as an async context manager, or call aclose() explicitly.
36
+
37
+ auth_header is the exact Authorization value configured by Telemt.
38
+ An injected HTTPX client remains owned by the caller.
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ base_url: str,
44
+ *,
45
+ auth_header: str | None = None,
46
+ timeout: float | httpx.Timeout = 30.0,
47
+ http_client: httpx.AsyncClient | None = None,
48
+ ) -> None:
49
+ url = httpx.URL(base_url)
50
+ if url.scheme not in {"http", "https"} or not url.host:
51
+ raise ValueError("base_url must be an absolute HTTP(S) URL")
52
+ if url.query or url.fragment or url.userinfo:
53
+ raise ValueError("base_url must not contain credentials, a query, or a fragment")
54
+ path = url.path.rstrip("/")
55
+ if not path.endswith("/v1"):
56
+ path += "/v1"
57
+ self._base_url = url.copy_with(path=path + "/")
58
+ self._headers = {"Accept": "application/json"}
59
+ if auth_header is not None:
60
+ self._headers["Authorization"] = auth_header
61
+ self._timeout = timeout
62
+ self._owns_http_client = http_client is None
63
+ self._http = http_client if http_client is not None else httpx.AsyncClient()
64
+ self._closed = False
65
+ self.health = HealthResource(self)
66
+ self.system = SystemResource(self)
67
+ self.users = UsersResource(self)
68
+ self.config = ConfigResource(self)
69
+ self.stats = StatsResource(self)
70
+ self.runtime = RuntimeResource(self)
71
+ self.security = SecurityResource(self)
72
+ self.limits = LimitsResource(self)
73
+
74
+ async def __aenter__(self) -> Self:
75
+ if self._closed:
76
+ raise RuntimeError("TelemtClient is closed")
77
+ return self
78
+
79
+ async def __aexit__(
80
+ self,
81
+ exc_type: type[BaseException] | None,
82
+ exc: BaseException | None,
83
+ traceback: TracebackType | None,
84
+ ) -> None:
85
+ await self.aclose()
86
+
87
+ async def aclose(self) -> None:
88
+ """Close owned connections; safe to call more than once."""
89
+ if not self._closed:
90
+ if self._owns_http_client:
91
+ await self._http.aclose()
92
+ self._closed = True
93
+
94
+ async def _request[T](
95
+ self,
96
+ method: str,
97
+ path: str,
98
+ response_type: type[APIResponse[T]],
99
+ *,
100
+ body: RequestModel | None = None,
101
+ params: Mapping[str, str | int] | None = None,
102
+ revision: str | None = None,
103
+ accepted_statuses: tuple[int, ...] = (200, 201, 202),
104
+ ) -> APIResponse[T]:
105
+ if self._closed:
106
+ raise RuntimeError("TelemtClient is closed")
107
+ headers = self._headers.copy()
108
+ if revision is not None:
109
+ headers["If-Match"] = revision
110
+ payload = None
111
+ if body is not None:
112
+ body = type(body).model_validate(body)
113
+ headers["Content-Type"] = "application/json"
114
+ is_patch = isinstance(body, (PatchUserRequest, PatchConfigRequest))
115
+ payload = body.model_dump(
116
+ mode="json", exclude_unset=is_patch, exclude_none=not is_patch, by_alias=True
117
+ )
118
+ try:
119
+ url = self._base_url.join(path).copy_with(params=params)
120
+ request = self._http.build_request(
121
+ method,
122
+ url,
123
+ headers=headers,
124
+ json=payload,
125
+ timeout=self._timeout,
126
+ )
127
+ # Client defaults must not add query fields or replace exact Telemt authorization.
128
+ request.url = url
129
+ response = await self._http.send(request, auth=None, follow_redirects=False)
130
+ except httpx.TimeoutException as exc:
131
+ raise RequestTimeoutError("Telemt request timed out") from exc
132
+ except httpx.RequestError as exc:
133
+ raise TransportError("Telemt request failed") from exc
134
+
135
+ try:
136
+ envelope = response.json()
137
+ except ValueError as exc:
138
+ raise InvalidResponseError(
139
+ "Telemt returned invalid JSON", status_code=response.status_code
140
+ ) from exc
141
+ if not isinstance(envelope, dict) or type(envelope.get("ok")) is not bool:
142
+ raise InvalidResponseError(
143
+ "Telemt returned an invalid envelope", status_code=response.status_code
144
+ )
145
+ if envelope["ok"] is False:
146
+ try:
147
+ error = ErrorEnvelope.model_validate(envelope)
148
+ except ValidationError as exc:
149
+ raise InvalidResponseError(
150
+ "Telemt returned an invalid error envelope", status_code=response.status_code
151
+ ) from exc
152
+ error_type = {
153
+ 401: AuthenticationError,
154
+ 403: PermissionDeniedError,
155
+ 404: NotFoundError,
156
+ 409: ConflictError,
157
+ }.get(response.status_code, APIError)
158
+ raise error_type(
159
+ status_code=response.status_code,
160
+ code=error.error.code,
161
+ message=error.error.message,
162
+ request_id=error.request_id,
163
+ )
164
+ if response.status_code not in accepted_statuses:
165
+ raise APIError(
166
+ status_code=response.status_code,
167
+ code="unexpected_http_status",
168
+ message="Success envelope received with an unexpected HTTP status",
169
+ )
170
+ try:
171
+ parsed = response_type.model_validate_json(response.content)
172
+ parsed.status_code = response.status_code
173
+ return parsed
174
+ except ValidationError as exc:
175
+ raise InvalidResponseError(
176
+ "Telemt response does not match the API contract", status_code=response.status_code
177
+ ) from exc
telemt/exceptions.py ADDED
@@ -0,0 +1,50 @@
1
+ """Errors exposed by the SDK; cancellation is deliberately never intercepted."""
2
+
3
+
4
+ class TelemtError(Exception):
5
+ """Base class for SDK failures."""
6
+
7
+
8
+ class TransportError(TelemtError):
9
+ """The request could not complete; the original HTTPX error is the cause."""
10
+
11
+
12
+ class RequestTimeoutError(TransportError):
13
+ """The request exceeded its configured HTTP timeout."""
14
+
15
+
16
+ class InvalidResponseError(TelemtError):
17
+ """The server returned an invalid or incompatible API envelope."""
18
+
19
+ def __init__(self, message: str, *, status_code: int) -> None:
20
+ self.status_code = status_code
21
+ super().__init__(message)
22
+
23
+
24
+ class APIError(TelemtError):
25
+ """A rejected API request, retaining its machine code and audit request ID."""
26
+
27
+ def __init__(
28
+ self, *, status_code: int, code: str, message: str, request_id: int | None = None
29
+ ) -> None:
30
+ self.status_code = status_code
31
+ self.code = code
32
+ self.message = message
33
+ self.request_id = request_id
34
+ super().__init__(f"HTTP {status_code} [{code}]: {message}")
35
+
36
+
37
+ class AuthenticationError(APIError):
38
+ """Missing or invalid Authorization header (401)."""
39
+
40
+
41
+ class PermissionDeniedError(APIError):
42
+ """The whitelist or read-only policy rejected the request (403)."""
43
+
44
+
45
+ class NotFoundError(APIError):
46
+ """The requested resource does not exist (404)."""
47
+
48
+
49
+ class ConflictError(APIError):
50
+ """A revision, runtime-instance, or operation conflict (409)."""
@@ -0,0 +1,345 @@
1
+ """Public request and response models for the Telemt Control API."""
2
+
3
+ from .base import (
4
+ APIResponse,
5
+ RequestModel,
6
+ ResponseModel,
7
+ )
8
+ from .operations import (
9
+ ReloadAccepted,
10
+ ReloadStatus,
11
+ UserQuota,
12
+ UserQuotaData,
13
+ )
14
+ from .requests import (
15
+ AllSelector,
16
+ Carrier,
17
+ CloseRequest,
18
+ CreateUserRequest,
19
+ DrainRequest,
20
+ DrainTimeout,
21
+ FilterSelector,
22
+ Hex32,
23
+ OperationRef,
24
+ PatchConfigRequest,
25
+ PatchUserRequest,
26
+ QuotaPeriod,
27
+ RefsSelector,
28
+ ReloadRequest,
29
+ RotateSecretRequest,
30
+ RuntimeInstance,
31
+ RuntimeInstanceRequest,
32
+ SessionFilter,
33
+ SessionQuery,
34
+ SessionRef,
35
+ SessionState,
36
+ UInt64,
37
+ Username,
38
+ )
39
+ from .responses import (
40
+ ApiEventRecord,
41
+ ClassCount,
42
+ ConfigData,
43
+ CreateUserResponse,
44
+ DcEndpointWriters,
45
+ DcStatus,
46
+ DcStatusData,
47
+ DeleteUserResponse,
48
+ EffectiveLimitsData,
49
+ EffectiveMiddleProxyLimits,
50
+ EffectiveTimeoutLimits,
51
+ EffectiveUpstreamLimits,
52
+ EffectiveUserIpPolicyLimits,
53
+ EffectiveUserTcpPolicyLimits,
54
+ HealthData,
55
+ HealthReadyData,
56
+ MeWritersData,
57
+ MeWritersSummary,
58
+ MeWriterStatus,
59
+ MinimalAllData,
60
+ MinimalAllPayload,
61
+ MinimalDcPathData,
62
+ MinimalMeRuntimeData,
63
+ MinimalQuarantineData,
64
+ PatchConfigResponse,
65
+ ResetUserQuotaResponse,
66
+ RuntimeEdgeConnectionCacheData,
67
+ RuntimeEdgeConnectionsSummaryData,
68
+ RuntimeEdgeConnectionsSummaryPayload,
69
+ RuntimeEdgeConnectionTelemetryData,
70
+ RuntimeEdgeConnectionTopData,
71
+ RuntimeEdgeConnectionTotalsData,
72
+ RuntimeEdgeConnectionUserData,
73
+ RuntimeEdgeEventsData,
74
+ RuntimeEdgeEventsPayload,
75
+ RuntimeEdgeTlsFingerprintRow,
76
+ RuntimeEdgeTlsFingerprintsData,
77
+ RuntimeEdgeTlsFingerprintsPayload,
78
+ RuntimeGatesData,
79
+ RuntimeInitializationComponentData,
80
+ RuntimeInitializationData,
81
+ RuntimeInitializationMeData,
82
+ RuntimeMePoolStateData,
83
+ RuntimeMePoolStateGenerationData,
84
+ RuntimeMePoolStateHardswapData,
85
+ RuntimeMePoolStatePayload,
86
+ RuntimeMePoolStateRefillData,
87
+ RuntimeMePoolStateRefillDcData,
88
+ RuntimeMePoolStateWriterContourData,
89
+ RuntimeMePoolStateWriterData,
90
+ RuntimeMePoolStateWriterHealthData,
91
+ RuntimeMeQualityCountersData,
92
+ RuntimeMeQualityData,
93
+ RuntimeMeQualityDcRttData,
94
+ RuntimeMeQualityDrainGateData,
95
+ RuntimeMeQualityFamilyStateData,
96
+ RuntimeMeQualityPayload,
97
+ RuntimeMeQualityRouteDropData,
98
+ RuntimeMeSelftestBndData,
99
+ RuntimeMeSelftestData,
100
+ RuntimeMeSelftestIpData,
101
+ RuntimeMeSelftestIpFamilyData,
102
+ RuntimeMeSelftestKdfData,
103
+ RuntimeMeSelftestPayload,
104
+ RuntimeMeSelftestPidData,
105
+ RuntimeMeSelftestTimeskewData,
106
+ RuntimeMeSelftestUpstreamData,
107
+ RuntimeNatStunData,
108
+ RuntimeNatStunFlagsData,
109
+ RuntimeNatStunPayload,
110
+ RuntimeNatStunReflectionBlockData,
111
+ RuntimeNatStunReflectionData,
112
+ RuntimeNatStunServersData,
113
+ RuntimeUpstreamQualityCountersData,
114
+ RuntimeUpstreamQualityData,
115
+ RuntimeUpstreamQualityDcData,
116
+ RuntimeUpstreamQualityPolicyData,
117
+ RuntimeUpstreamQualitySummaryData,
118
+ RuntimeUpstreamQualityUpstreamData,
119
+ SecurityPostureData,
120
+ SecurityWhitelistData,
121
+ SummaryData,
122
+ SystemInfoData,
123
+ TlsDomainLink,
124
+ UpstreamDcStatus,
125
+ UpstreamsData,
126
+ UpstreamStatus,
127
+ UpstreamSummaryData,
128
+ UserActiveIps,
129
+ UserInfo,
130
+ UserLinks,
131
+ ZeroAllData,
132
+ ZeroCodeCount,
133
+ ZeroCoreData,
134
+ ZeroDesyncData,
135
+ ZeroMiddleProxyData,
136
+ ZeroPoolData,
137
+ ZeroUpstreamData,
138
+ )
139
+ from .web import (
140
+ ControlOperationStatus,
141
+ DebugClearData,
142
+ GoneSessionData,
143
+ LearningResetData,
144
+ OperatorDrainStatus,
145
+ OperatorLifecycleStatus,
146
+ SessionPage,
147
+ SessionRow,
148
+ WebBridgeRecoveryCounter,
149
+ WebBudgetStatus,
150
+ WebCapacityResourceStatus,
151
+ WebCapacityStatus,
152
+ WebCarrierFailureCounter,
153
+ WebCarrierLearningCounter,
154
+ WebCarrierNegotiationStatus,
155
+ WebCarrierSelectionCounter,
156
+ WebDebugConfig,
157
+ WebDebugStatus,
158
+ WebDecoyFastTrackCounter,
159
+ WebDecoyFastTrackStatus,
160
+ WebDecoyUpstreamStatus,
161
+ WebIngressStatus,
162
+ WebLearningStatus,
163
+ WebLifecycleCountersStatus,
164
+ WebLimitsConfig,
165
+ WebManagerStatus,
166
+ WebOutcomeCounter,
167
+ WebPermitStatus,
168
+ WebRejectionCounter,
169
+ WebRuntimeStatus,
170
+ WebSessionCloseCounter,
171
+ WebSessionLifecycleObservationCounter,
172
+ WebSessionStatus,
173
+ WebSocketStatus,
174
+ WebStatusData,
175
+ WebStreamStatus,
176
+ WebWildcardLimitsConfig,
177
+ )
178
+
179
+ __all__ = [
180
+ "APIResponse",
181
+ "AllSelector",
182
+ "ApiEventRecord",
183
+ "Carrier",
184
+ "ClassCount",
185
+ "CloseRequest",
186
+ "ConfigData",
187
+ "ControlOperationStatus",
188
+ "CreateUserRequest",
189
+ "CreateUserResponse",
190
+ "DcEndpointWriters",
191
+ "DcStatus",
192
+ "DcStatusData",
193
+ "DebugClearData",
194
+ "DeleteUserResponse",
195
+ "DrainRequest",
196
+ "DrainTimeout",
197
+ "EffectiveLimitsData",
198
+ "EffectiveMiddleProxyLimits",
199
+ "EffectiveTimeoutLimits",
200
+ "EffectiveUpstreamLimits",
201
+ "EffectiveUserIpPolicyLimits",
202
+ "EffectiveUserTcpPolicyLimits",
203
+ "FilterSelector",
204
+ "GoneSessionData",
205
+ "HealthData",
206
+ "HealthReadyData",
207
+ "Hex32",
208
+ "LearningResetData",
209
+ "MeWriterStatus",
210
+ "MeWritersData",
211
+ "MeWritersSummary",
212
+ "MinimalAllData",
213
+ "MinimalAllPayload",
214
+ "MinimalDcPathData",
215
+ "MinimalMeRuntimeData",
216
+ "MinimalQuarantineData",
217
+ "OperatorDrainStatus",
218
+ "OperatorLifecycleStatus",
219
+ "OperationRef",
220
+ "PatchConfigRequest",
221
+ "PatchConfigResponse",
222
+ "PatchUserRequest",
223
+ "QuotaPeriod",
224
+ "RefsSelector",
225
+ "ReloadAccepted",
226
+ "ReloadRequest",
227
+ "ReloadStatus",
228
+ "RequestModel",
229
+ "ResetUserQuotaResponse",
230
+ "ResponseModel",
231
+ "RotateSecretRequest",
232
+ "RuntimeEdgeConnectionCacheData",
233
+ "RuntimeEdgeConnectionTelemetryData",
234
+ "RuntimeEdgeConnectionTopData",
235
+ "RuntimeEdgeConnectionTotalsData",
236
+ "RuntimeEdgeConnectionUserData",
237
+ "RuntimeEdgeConnectionsSummaryData",
238
+ "RuntimeEdgeConnectionsSummaryPayload",
239
+ "RuntimeEdgeEventsData",
240
+ "RuntimeEdgeEventsPayload",
241
+ "RuntimeEdgeTlsFingerprintRow",
242
+ "RuntimeEdgeTlsFingerprintsData",
243
+ "RuntimeEdgeTlsFingerprintsPayload",
244
+ "RuntimeGatesData",
245
+ "RuntimeInitializationComponentData",
246
+ "RuntimeInitializationData",
247
+ "RuntimeInitializationMeData",
248
+ "RuntimeInstance",
249
+ "RuntimeInstanceRequest",
250
+ "RuntimeMePoolStateData",
251
+ "RuntimeMePoolStateGenerationData",
252
+ "RuntimeMePoolStateHardswapData",
253
+ "RuntimeMePoolStatePayload",
254
+ "RuntimeMePoolStateRefillData",
255
+ "RuntimeMePoolStateRefillDcData",
256
+ "RuntimeMePoolStateWriterContourData",
257
+ "RuntimeMePoolStateWriterData",
258
+ "RuntimeMePoolStateWriterHealthData",
259
+ "RuntimeMeQualityCountersData",
260
+ "RuntimeMeQualityData",
261
+ "RuntimeMeQualityDcRttData",
262
+ "RuntimeMeQualityDrainGateData",
263
+ "RuntimeMeQualityFamilyStateData",
264
+ "RuntimeMeQualityPayload",
265
+ "RuntimeMeQualityRouteDropData",
266
+ "RuntimeMeSelftestBndData",
267
+ "RuntimeMeSelftestData",
268
+ "RuntimeMeSelftestIpData",
269
+ "RuntimeMeSelftestIpFamilyData",
270
+ "RuntimeMeSelftestKdfData",
271
+ "RuntimeMeSelftestPayload",
272
+ "RuntimeMeSelftestPidData",
273
+ "RuntimeMeSelftestTimeskewData",
274
+ "RuntimeMeSelftestUpstreamData",
275
+ "RuntimeNatStunData",
276
+ "RuntimeNatStunFlagsData",
277
+ "RuntimeNatStunPayload",
278
+ "RuntimeNatStunReflectionBlockData",
279
+ "RuntimeNatStunReflectionData",
280
+ "RuntimeNatStunServersData",
281
+ "RuntimeUpstreamQualityCountersData",
282
+ "RuntimeUpstreamQualityData",
283
+ "RuntimeUpstreamQualityDcData",
284
+ "RuntimeUpstreamQualityPolicyData",
285
+ "RuntimeUpstreamQualitySummaryData",
286
+ "RuntimeUpstreamQualityUpstreamData",
287
+ "SecurityPostureData",
288
+ "SecurityWhitelistData",
289
+ "SessionFilter",
290
+ "SessionPage",
291
+ "SessionQuery",
292
+ "SessionRef",
293
+ "SessionRow",
294
+ "SessionState",
295
+ "SummaryData",
296
+ "SystemInfoData",
297
+ "TlsDomainLink",
298
+ "UInt64",
299
+ "UpstreamDcStatus",
300
+ "UpstreamStatus",
301
+ "UpstreamSummaryData",
302
+ "UpstreamsData",
303
+ "UserActiveIps",
304
+ "UserInfo",
305
+ "UserLinks",
306
+ "UserQuota",
307
+ "UserQuotaData",
308
+ "Username",
309
+ "WebBridgeRecoveryCounter",
310
+ "WebBudgetStatus",
311
+ "WebCapacityResourceStatus",
312
+ "WebCapacityStatus",
313
+ "WebCarrierFailureCounter",
314
+ "WebCarrierLearningCounter",
315
+ "WebCarrierNegotiationStatus",
316
+ "WebCarrierSelectionCounter",
317
+ "WebDebugConfig",
318
+ "WebDebugStatus",
319
+ "WebDecoyFastTrackCounter",
320
+ "WebDecoyFastTrackStatus",
321
+ "WebDecoyUpstreamStatus",
322
+ "WebIngressStatus",
323
+ "WebLearningStatus",
324
+ "WebLifecycleCountersStatus",
325
+ "WebLimitsConfig",
326
+ "WebManagerStatus",
327
+ "WebOutcomeCounter",
328
+ "WebPermitStatus",
329
+ "WebRejectionCounter",
330
+ "WebRuntimeStatus",
331
+ "WebSessionCloseCounter",
332
+ "WebSessionLifecycleObservationCounter",
333
+ "WebSessionStatus",
334
+ "WebSocketStatus",
335
+ "WebStatusData",
336
+ "WebStreamStatus",
337
+ "WebWildcardLimitsConfig",
338
+ "ZeroAllData",
339
+ "ZeroCodeCount",
340
+ "ZeroCoreData",
341
+ "ZeroDesyncData",
342
+ "ZeroMiddleProxyData",
343
+ "ZeroPoolData",
344
+ "ZeroUpstreamData",
345
+ ]
telemt/models/base.py ADDED
@@ -0,0 +1,39 @@
1
+ """Shared model configuration and HTTP response metadata."""
2
+
3
+ from typing import Literal
4
+
5
+ from pydantic import BaseModel, ConfigDict
6
+
7
+
8
+ class ResponseModel(BaseModel):
9
+ """Typed API data; preserve new server fields for forward compatibility."""
10
+
11
+ model_config = ConfigDict(extra="allow", strict=True, hide_input_in_errors=True)
12
+
13
+
14
+ class APIResponse[T](ResponseModel):
15
+ """One response and its own revision, safe to use with concurrent requests."""
16
+
17
+ ok: Literal[True]
18
+ data: T
19
+ revision: str
20
+ status_code: int = 200
21
+
22
+
23
+ class RequestModel(BaseModel):
24
+ """Reject unknown inputs and keep omitted fields distinct from explicit null."""
25
+
26
+ model_config = ConfigDict(
27
+ extra="forbid", strict=True, revalidate_instances="always", hide_input_in_errors=True
28
+ )
29
+
30
+
31
+ class ErrorDetail(ResponseModel):
32
+ code: str
33
+ message: str
34
+
35
+
36
+ class ErrorEnvelope(ResponseModel):
37
+ ok: Literal[False]
38
+ error: ErrorDetail
39
+ request_id: int
@@ -0,0 +1,39 @@
1
+ """Reload and quota contracts."""
2
+
3
+ from pydantic import Field
4
+
5
+ from .base import ResponseModel
6
+
7
+
8
+ class ReloadAccepted(ResponseModel):
9
+ reload_id: int
10
+ target_generation: int
11
+ config_revision: str
12
+ state: str
13
+ mode: str
14
+ failure_policy: str
15
+
16
+
17
+ class ReloadStatus(ReloadAccepted):
18
+ requested_at_epoch_secs: int
19
+ started_at_epoch_secs: int | None = None
20
+ finished_at_epoch_secs: int | None = None
21
+ deferred_process_fields: list[str] = Field(default_factory=list)
22
+ warnings: list[str] = Field(default_factory=list)
23
+ error: str | None = None
24
+
25
+
26
+ class UserQuota(ResponseModel):
27
+ username: str
28
+ data_quota_bytes: int
29
+ used_bytes: int
30
+ last_reset_epoch_secs: int
31
+ quota_period: str | None = None
32
+ active_quota_period: str | None = None
33
+ quota_period_start_epoch_secs: int | None = None
34
+ quota_next_reset_epoch_secs: int | None = None
35
+ quota_schedule_state: str
36
+
37
+
38
+ class UserQuotaData(ResponseModel):
39
+ users: list[UserQuota]