basaltic-sdk-python 0.1.1__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 (47) hide show
  1. basaltic/__init__.py +26 -0
  2. basaltic/_client.py +136 -0
  3. basaltic/_common.py +284 -0
  4. basaltic/_transport.py +196 -0
  5. basaltic/_version.py +3 -0
  6. basaltic/auth.py +231 -0
  7. basaltic/client.py +144 -0
  8. basaltic/config.py +151 -0
  9. basaltic/errors.py +73 -0
  10. basaltic/models/__init__.py +17 -0
  11. basaltic/models/audit.py +85 -0
  12. basaltic/models/billing.py +284 -0
  13. basaltic/models/catalog.py +35 -0
  14. basaltic/models/certificate.py +127 -0
  15. basaltic/models/compute.py +905 -0
  16. basaltic/models/dns.py +256 -0
  17. basaltic/models/iam.py +736 -0
  18. basaltic/models/kms.py +157 -0
  19. basaltic/models/loadbalancer.py +574 -0
  20. basaltic/models/network.py +985 -0
  21. basaltic/models/quota.py +30 -0
  22. basaltic/models/secrets.py +136 -0
  23. basaltic/models/storage.py +789 -0
  24. basaltic/models/telemetry.py +393 -0
  25. basaltic/models/workspace.py +734 -0
  26. basaltic/py.typed +0 -0
  27. basaltic/response.py +152 -0
  28. basaltic/services/__init__.py +1 -0
  29. basaltic/services/audit.py +211 -0
  30. basaltic/services/billing.py +777 -0
  31. basaltic/services/catalog.py +172 -0
  32. basaltic/services/certificate.py +395 -0
  33. basaltic/services/compute.py +2395 -0
  34. basaltic/services/dns.py +1079 -0
  35. basaltic/services/iam.py +3405 -0
  36. basaltic/services/kms.py +742 -0
  37. basaltic/services/loadbalancer.py +1727 -0
  38. basaltic/services/network.py +4691 -0
  39. basaltic/services/quota.py +75 -0
  40. basaltic/services/secrets.py +550 -0
  41. basaltic/services/storage.py +3059 -0
  42. basaltic/services/telemetry.py +1316 -0
  43. basaltic/services/workspace.py +3899 -0
  44. basaltic_sdk_python-0.1.1.dist-info/METADATA +211 -0
  45. basaltic_sdk_python-0.1.1.dist-info/RECORD +47 -0
  46. basaltic_sdk_python-0.1.1.dist-info/WHEEL +4 -0
  47. basaltic_sdk_python-0.1.1.dist-info/licenses/LICENSE +202 -0
basaltic/py.typed ADDED
File without changes
basaltic/response.py ADDED
@@ -0,0 +1,152 @@
1
+ """Response envelopes, pagination and resource reference helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from collections.abc import AsyncIterator, Awaitable, Callable, Iterator, Mapping
7
+ from dataclasses import dataclass
8
+ from typing import Any, Generic, TypeVar, cast
9
+
10
+ import httpx
11
+
12
+ from .errors import AmbiguousReferenceError, ApiError, ProtocolError
13
+
14
+ T = TypeVar("T", covariant=True)
15
+ Item = TypeVar("Item", covariant=True)
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class ApiResponse(Generic[T]):
20
+ data: T
21
+ response: httpx.Response
22
+
23
+ @property
24
+ def request_id(self) -> str:
25
+ return str(self.response.headers.get("X-Request-Id", ""))
26
+
27
+ @property
28
+ def status_code(self) -> int:
29
+ return self.response.status_code
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class Page(ApiResponse[T], Generic[T, Item]):
34
+ items_key: str
35
+
36
+ @property
37
+ def items(self) -> list[Item]:
38
+ if not isinstance(self.data, dict) or not isinstance(self.data.get(self.items_key), list):
39
+ raise ProtocolError("List response is missing its items array.")
40
+ return cast(list[Item], self.data[self.items_key])
41
+
42
+ @property
43
+ def has_more(self) -> bool:
44
+ meta = self.data.get("meta") if isinstance(self.data, dict) else None
45
+ return isinstance(meta, dict) and meta.get("has_more") is True
46
+
47
+ @property
48
+ def marker(self) -> str:
49
+ meta = self.data.get("meta") if isinstance(self.data, dict) else None
50
+ value = meta.get("marker") if isinstance(meta, dict) else None
51
+ return value if isinstance(value, str) else ""
52
+
53
+
54
+ def iterate_pages(fetch: Callable[[str], Page[Any, Item]], marker: str = "") -> Iterator[Item]:
55
+ seen = {marker}
56
+ while True:
57
+ page = fetch(marker)
58
+ yield from page.items
59
+ if not page.has_more:
60
+ return
61
+ marker = next_marker(page, seen)
62
+
63
+
64
+ async def aiterate_pages(
65
+ fetch: Callable[[str], Awaitable[Page[Any, Item]]], marker: str = ""
66
+ ) -> AsyncIterator[Item]:
67
+ seen = {marker}
68
+ while True:
69
+ page = await fetch(marker)
70
+ for item in page.items:
71
+ yield item
72
+ if not page.has_more:
73
+ return
74
+ marker = next_marker(page, seen)
75
+
76
+
77
+ def next_marker(page: Page[Any, Any], seen: set[str]) -> str:
78
+ marker = page.marker
79
+ if not marker or marker in seen:
80
+ raise ProtocolError("Pagination did not advance; refusing a truncated or repeated walk.")
81
+ seen.add(marker)
82
+ return marker
83
+
84
+
85
+ def reference_scope(scope: Mapping[str, object] | None) -> dict[str, object]:
86
+ return {k: v for k, v in (scope or {}).items() if k not in ("name", "crn", "marker")}
87
+
88
+
89
+ def reference_filter(reference: str, has_name: bool) -> dict[str, str] | None:
90
+ if not reference:
91
+ raise ValueError("A nonempty resource reference is required.")
92
+ if re.fullmatch(r"[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}", reference):
93
+ return None
94
+ kind = "crn" if reference.startswith("crn:") else "name"
95
+ if kind == "crn" and len(reference.split(":")) != 5:
96
+ raise ValueError("A CRN must have five colon-separated segments.")
97
+ if kind == "name" and not has_name:
98
+ raise ValueError("This resource requires a UUID or CRN.")
99
+ return {kind: reference}
100
+
101
+
102
+ def unwrap_reference(result: ApiResponse[Any], envelope: str | None) -> ApiResponse[Any]:
103
+ data = result.data.get(envelope) if envelope and isinstance(result.data, dict) else result.data
104
+ if not isinstance(data, dict):
105
+ raise ProtocolError("Reference lookup is missing its resource envelope.")
106
+ return ApiResponse(data, result.response)
107
+
108
+
109
+ def select_reference(page: Page[Any, Any]) -> ApiResponse[Any]:
110
+ if len(page.items) > 1 or page.has_more:
111
+ raise AmbiguousReferenceError()
112
+ if not page.items:
113
+ raise ApiError(
114
+ 404,
115
+ "REFERENCE_NOT_FOUND",
116
+ "No resource matches the reference.",
117
+ page.request_id,
118
+ "resolve_reference",
119
+ )
120
+ if not isinstance(page.items[0], dict):
121
+ raise ProtocolError("Reference lookup returned a non-object resource.")
122
+ return ApiResponse(page.items[0], page.response)
123
+
124
+
125
+ def resolve_reference(
126
+ reference: str,
127
+ get: Callable[[str], ApiResponse[Any]],
128
+ listing: Callable[[dict[str, str]], Page[Any, Any]],
129
+ has_name: bool,
130
+ envelope: str | None = None,
131
+ ) -> ApiResponse[Any]:
132
+ match = reference_filter(reference, has_name)
133
+ return (
134
+ unwrap_reference(get(reference), envelope)
135
+ if match is None
136
+ else select_reference(listing(match))
137
+ )
138
+
139
+
140
+ async def aresolve_reference(
141
+ reference: str,
142
+ get: Callable[[str], Awaitable[ApiResponse[Any]]],
143
+ listing: Callable[[dict[str, str]], Awaitable[Page[Any, Any]]],
144
+ has_name: bool,
145
+ envelope: str | None = None,
146
+ ) -> ApiResponse[Any]:
147
+ match = reference_filter(reference, has_name)
148
+ return (
149
+ unwrap_reference(await get(reference), envelope)
150
+ if match is None
151
+ else select_reference(await listing(match))
152
+ )
@@ -0,0 +1 @@
1
+ """Generated synchronous and asynchronous service clients."""
@@ -0,0 +1,211 @@
1
+ """Generated typed API methods; do not edit."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import AsyncIterator, Iterator
6
+ from typing import cast
7
+
8
+ from .._common import UNSET, Operation
9
+ from .._transport import AsyncTransport, SyncTransport
10
+ from ..config import RequestOptions
11
+ from ..models import audit as m
12
+ from ..response import (
13
+ ApiResponse,
14
+ Page,
15
+ aiterate_pages,
16
+ aresolve_reference,
17
+ iterate_pages,
18
+ reference_scope,
19
+ resolve_reference,
20
+ )
21
+
22
+ _OPS = {
23
+ "getAuditLog": Operation(
24
+ id="getAuditLog",
25
+ method="GET",
26
+ path="/v1/audit-logs/{log_id}",
27
+ authenticated=True,
28
+ requiredQuery=[],
29
+ requiredHeaders=[],
30
+ queryEncoding={},
31
+ bodyRequired=False,
32
+ contentType="",
33
+ accept="application/json",
34
+ ),
35
+ "listAuditLogs": Operation(
36
+ id="listAuditLogs",
37
+ method="GET",
38
+ path="/v1/audit-logs",
39
+ authenticated=True,
40
+ requiredQuery=[],
41
+ requiredHeaders=[],
42
+ queryEncoding={
43
+ "crn": {"style": "form", "explode": True},
44
+ "actor": {"style": "form", "explode": True},
45
+ "actor_type": {"style": "form", "explode": True},
46
+ "action": {"style": "form", "explode": True},
47
+ "resource_type": {"style": "form", "explode": True},
48
+ "resource": {"style": "form", "explode": True},
49
+ "status": {"style": "form", "explode": True},
50
+ "ip_address": {"style": "form", "explode": True},
51
+ "from": {"style": "form", "explode": True},
52
+ "to": {"style": "form", "explode": True},
53
+ "limit": {"style": "form", "explode": True},
54
+ "marker": {"style": "form", "explode": True},
55
+ },
56
+ bodyRequired=False,
57
+ contentType="",
58
+ accept="application/json",
59
+ itemsKey="audit_logs",
60
+ ),
61
+ }
62
+
63
+
64
+ class AuditService:
65
+ def __init__(self, transport: SyncTransport) -> None:
66
+ self._transport = transport
67
+
68
+ def get_audit_log(
69
+ self, log_id: str, *, options: RequestOptions | None = None
70
+ ) -> ApiResponse[m.GetAuditLogResponse]:
71
+ "Get audit log entry"
72
+ return cast(
73
+ ApiResponse[m.GetAuditLogResponse],
74
+ self._transport.request(
75
+ "audit",
76
+ "https://audit.basaltic.sh",
77
+ _OPS["getAuditLog"],
78
+ "json",
79
+ {"log_id": log_id},
80
+ UNSET,
81
+ None,
82
+ options,
83
+ ),
84
+ )
85
+
86
+ def get_audit_log_by_reference(
87
+ self,
88
+ reference: str,
89
+ *,
90
+ scope: m.GetAuditLogScope | None = None,
91
+ options: RequestOptions | None = None,
92
+ ) -> ApiResponse[m.GetAuditLogResource]:
93
+ return cast(
94
+ ApiResponse[m.GetAuditLogResource],
95
+ resolve_reference(
96
+ reference,
97
+ lambda id: self.get_audit_log(id, options=options),
98
+ lambda match: self.list_audit_logs(
99
+ query=cast(
100
+ m.ListAuditLogsQuery, {**reference_scope(scope), **match, "limit": 2}
101
+ ),
102
+ options=options,
103
+ ),
104
+ False,
105
+ "audit_log",
106
+ ),
107
+ )
108
+
109
+ def list_audit_logs(
110
+ self, *, query: m.ListAuditLogsQuery | None = None, options: RequestOptions | None = None
111
+ ) -> Page[m.ListAuditLogsResponse, m.ListAuditLogsItem]:
112
+ "List audit logs"
113
+ return cast(
114
+ Page[m.ListAuditLogsResponse, m.ListAuditLogsItem],
115
+ self._transport.request(
116
+ "audit",
117
+ "https://audit.basaltic.sh",
118
+ _OPS["listAuditLogs"],
119
+ "page",
120
+ {},
121
+ UNSET,
122
+ query,
123
+ options,
124
+ ),
125
+ )
126
+
127
+ def list_audit_logs_all(
128
+ self, *, query: m.ListAuditLogsQuery | None = None, options: RequestOptions | None = None
129
+ ) -> Iterator[m.ListAuditLogsItem]:
130
+ return iterate_pages(
131
+ lambda marker: self.list_audit_logs(
132
+ query=cast(m.ListAuditLogsQuery, {**(query or {}), "marker": marker}),
133
+ options=options,
134
+ ),
135
+ (query or {}).get("marker", ""),
136
+ )
137
+
138
+
139
+ class AsyncAuditService:
140
+ def __init__(self, transport: AsyncTransport) -> None:
141
+ self._transport = transport
142
+
143
+ async def get_audit_log(
144
+ self, log_id: str, *, options: RequestOptions | None = None
145
+ ) -> ApiResponse[m.GetAuditLogResponse]:
146
+ "Get audit log entry"
147
+ return cast(
148
+ ApiResponse[m.GetAuditLogResponse],
149
+ await self._transport.request(
150
+ "audit",
151
+ "https://audit.basaltic.sh",
152
+ _OPS["getAuditLog"],
153
+ "json",
154
+ {"log_id": log_id},
155
+ UNSET,
156
+ None,
157
+ options,
158
+ ),
159
+ )
160
+
161
+ async def get_audit_log_by_reference(
162
+ self,
163
+ reference: str,
164
+ *,
165
+ scope: m.GetAuditLogScope | None = None,
166
+ options: RequestOptions | None = None,
167
+ ) -> ApiResponse[m.GetAuditLogResource]:
168
+ return cast(
169
+ ApiResponse[m.GetAuditLogResource],
170
+ await aresolve_reference(
171
+ reference,
172
+ lambda id: self.get_audit_log(id, options=options),
173
+ lambda match: self.list_audit_logs(
174
+ query=cast(
175
+ m.ListAuditLogsQuery, {**reference_scope(scope), **match, "limit": 2}
176
+ ),
177
+ options=options,
178
+ ),
179
+ False,
180
+ "audit_log",
181
+ ),
182
+ )
183
+
184
+ async def list_audit_logs(
185
+ self, *, query: m.ListAuditLogsQuery | None = None, options: RequestOptions | None = None
186
+ ) -> Page[m.ListAuditLogsResponse, m.ListAuditLogsItem]:
187
+ "List audit logs"
188
+ return cast(
189
+ Page[m.ListAuditLogsResponse, m.ListAuditLogsItem],
190
+ await self._transport.request(
191
+ "audit",
192
+ "https://audit.basaltic.sh",
193
+ _OPS["listAuditLogs"],
194
+ "page",
195
+ {},
196
+ UNSET,
197
+ query,
198
+ options,
199
+ ),
200
+ )
201
+
202
+ def list_audit_logs_all(
203
+ self, *, query: m.ListAuditLogsQuery | None = None, options: RequestOptions | None = None
204
+ ) -> AsyncIterator[m.ListAuditLogsItem]:
205
+ return aiterate_pages(
206
+ lambda marker: self.list_audit_logs(
207
+ query=cast(m.ListAuditLogsQuery, {**(query or {}), "marker": marker}),
208
+ options=options,
209
+ ),
210
+ (query or {}).get("marker", ""),
211
+ )