vaultkit 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.
- vaultkit/__init__.py +85 -0
- vaultkit/client.py +441 -0
- vaultkit/core/__init__.py +1 -0
- vaultkit/core/http.py +190 -0
- vaultkit/core/polling.py +140 -0
- vaultkit/errors/__init__.py +36 -0
- vaultkit/errors/base.py +30 -0
- vaultkit/errors/exceptions.py +211 -0
- vaultkit/models/__init__.py +11 -0
- vaultkit/models/dataset_info.py +25 -0
- vaultkit/models/dataset_schema.py +75 -0
- vaultkit/models/fetch_result.py +53 -0
- vaultkit/models/query_result.py +73 -0
- vaultkit/tools/__init__.py +5 -0
- vaultkit/tools/adapters/__init__.py +12 -0
- vaultkit/tools/adapters/anthropic.py +17 -0
- vaultkit/tools/adapters/openai.py +23 -0
- vaultkit/tools/builder.py +128 -0
- vaultkit/tools/definitions.py +177 -0
- vaultkit/tools/executor.py +199 -0
- vaultkit/tools/schemas.py +39 -0
- vaultkit/utils/__init__.py +1 -0
- vaultkit/utils/retry.py +81 -0
- vaultkit/utils/validation.py +87 -0
- vaultkit-0.1.0.dist-info/METADATA +207 -0
- vaultkit-0.1.0.dist-info/RECORD +28 -0
- vaultkit-0.1.0.dist-info/WHEEL +5 -0
- vaultkit-0.1.0.dist-info/top_level.txt +1 -0
vaultkit/__init__.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VaultKit Python SDK
|
|
3
|
+
|
|
4
|
+
Quick start:
|
|
5
|
+
|
|
6
|
+
from vaultkit import VaultKitClient
|
|
7
|
+
|
|
8
|
+
with VaultKitClient(
|
|
9
|
+
base_url="https://vaultkit.yourorg.com",
|
|
10
|
+
token="your-jwt-token",
|
|
11
|
+
org="your-org-id",
|
|
12
|
+
) as client:
|
|
13
|
+
|
|
14
|
+
# High-level: full lifecycle, grants invisible
|
|
15
|
+
result = client.execute(
|
|
16
|
+
dataset="customers",
|
|
17
|
+
fields=["id", "email", "revenue"],
|
|
18
|
+
filters=[{"field": "revenue", "operator": "gt", "value": 10000}],
|
|
19
|
+
purpose="Q4 revenue analysis",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# For AI agents — scoped tool schemas from the live registry:
|
|
23
|
+
from vaultkit.tools import ToolBuilder, ToolExecutor
|
|
24
|
+
|
|
25
|
+
tools = ToolBuilder(client).build()
|
|
26
|
+
executor = ToolExecutor(client)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from .client import ClientConfig, VaultKitClient
|
|
30
|
+
|
|
31
|
+
from .errors import (
|
|
32
|
+
ApprovalRequiredError,
|
|
33
|
+
DeniedError,
|
|
34
|
+
GrantExpiredError,
|
|
35
|
+
GrantRevokedError,
|
|
36
|
+
PolicyBundleRevokedError,
|
|
37
|
+
PollTimeoutError,
|
|
38
|
+
QueuedError,
|
|
39
|
+
ValidationError,
|
|
40
|
+
VaultKitError,
|
|
41
|
+
TransportError,
|
|
42
|
+
ServerError,
|
|
43
|
+
RateLimitError,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
from .models import (
|
|
47
|
+
DatasetInfo,
|
|
48
|
+
DatasetSchema,
|
|
49
|
+
FetchResult,
|
|
50
|
+
QueryResult,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Optional (nice DX improvement)
|
|
54
|
+
from .tools import ToolBuilder, ToolExecutor
|
|
55
|
+
|
|
56
|
+
__version__ = "0.1.0"
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"VaultKitClient",
|
|
60
|
+
"ClientConfig",
|
|
61
|
+
|
|
62
|
+
# Models
|
|
63
|
+
"QueryResult",
|
|
64
|
+
"FetchResult",
|
|
65
|
+
"DatasetInfo",
|
|
66
|
+
"DatasetSchema",
|
|
67
|
+
|
|
68
|
+
# Errors
|
|
69
|
+
"VaultKitError",
|
|
70
|
+
"DeniedError",
|
|
71
|
+
"ApprovalRequiredError",
|
|
72
|
+
"QueuedError",
|
|
73
|
+
"GrantExpiredError",
|
|
74
|
+
"GrantRevokedError",
|
|
75
|
+
"PolicyBundleRevokedError",
|
|
76
|
+
"ValidationError",
|
|
77
|
+
"PollTimeoutError",
|
|
78
|
+
"TransportError",
|
|
79
|
+
"ServerError",
|
|
80
|
+
"RateLimitError",
|
|
81
|
+
|
|
82
|
+
# Tools
|
|
83
|
+
"ToolBuilder",
|
|
84
|
+
"ToolExecutor",
|
|
85
|
+
]
|
vaultkit/client.py
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from vaultkit.core.http import HttpClient, HttpConfig
|
|
8
|
+
from vaultkit.core.polling import PollConfig, poll_until_done
|
|
9
|
+
from vaultkit.errors.exceptions import (
|
|
10
|
+
ApprovalRequiredError,
|
|
11
|
+
DeniedError,
|
|
12
|
+
QueuedError,
|
|
13
|
+
ValidationError,
|
|
14
|
+
)
|
|
15
|
+
from vaultkit.models.dataset_info import DatasetInfo
|
|
16
|
+
from vaultkit.models.dataset_schema import DatasetSchema
|
|
17
|
+
from vaultkit.models.fetch_result import FetchResult
|
|
18
|
+
from vaultkit.models.query_result import QueryResult
|
|
19
|
+
from vaultkit.utils.retry import RetryConfig, with_retries
|
|
20
|
+
from vaultkit.utils.validation import (
|
|
21
|
+
require_str,
|
|
22
|
+
validate_filters,
|
|
23
|
+
validate_limit,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class ClientConfig:
|
|
29
|
+
# Use field(default_factory=...) to avoid Python mutable-default error
|
|
30
|
+
http: HttpConfig = field(default_factory=HttpConfig)
|
|
31
|
+
retries: RetryConfig = field(default_factory=RetryConfig)
|
|
32
|
+
polling: PollConfig = field(default_factory=PollConfig)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class VaultKitClient:
|
|
36
|
+
"""
|
|
37
|
+
VaultKit Python SDK.
|
|
38
|
+
|
|
39
|
+
Two levels of abstraction:
|
|
40
|
+
|
|
41
|
+
High-level (recommended for agents):
|
|
42
|
+
execute() Full lifecycle: intent → poll → fetch → data.
|
|
43
|
+
Grants are invisible. Agent gets data or a typed exception.
|
|
44
|
+
|
|
45
|
+
Low-level (for custom orchestration):
|
|
46
|
+
query() Submit intent, get QueryResult (granted/queued/denied).
|
|
47
|
+
fetch() Redeem a grant_ref for data.
|
|
48
|
+
poll() Block until queued QueryResult reaches terminal state.
|
|
49
|
+
poll_request() Poll by request_id (used by check_approval flows).
|
|
50
|
+
|
|
51
|
+
Discovery:
|
|
52
|
+
datasets() List authorized datasets from the registry.
|
|
53
|
+
schema() Get field-level schema for a dataset.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
*,
|
|
59
|
+
base_url: str,
|
|
60
|
+
token: str,
|
|
61
|
+
org: str,
|
|
62
|
+
config: Optional[ClientConfig] = None,
|
|
63
|
+
logger: Optional[logging.Logger] = None,
|
|
64
|
+
) -> None:
|
|
65
|
+
self.base_url = require_str("base_url", base_url)
|
|
66
|
+
self.token = require_str("token", token)
|
|
67
|
+
self.org = require_str("org", org)
|
|
68
|
+
self.config = config or ClientConfig()
|
|
69
|
+
self._logger = logger
|
|
70
|
+
|
|
71
|
+
self._http = HttpClient(
|
|
72
|
+
base_url=self.base_url,
|
|
73
|
+
token=self.token,
|
|
74
|
+
config=self.config.http,
|
|
75
|
+
logger=self._logger,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def close(self) -> None:
|
|
79
|
+
self._http.close()
|
|
80
|
+
|
|
81
|
+
def __enter__(self) -> "VaultKitClient":
|
|
82
|
+
return self
|
|
83
|
+
|
|
84
|
+
def __exit__(self, *args: Any) -> None:
|
|
85
|
+
self.close()
|
|
86
|
+
|
|
87
|
+
# Internal helpers
|
|
88
|
+
|
|
89
|
+
def _path(self, suffix: str) -> str:
|
|
90
|
+
return f"/api/v1/orgs/{self.org}{suffix}"
|
|
91
|
+
|
|
92
|
+
# High-level
|
|
93
|
+
|
|
94
|
+
def execute(
|
|
95
|
+
self,
|
|
96
|
+
*,
|
|
97
|
+
dataset: str,
|
|
98
|
+
fields: Optional[List[str]] = None,
|
|
99
|
+
filters: Optional[List[Dict[str, Any]]] = None,
|
|
100
|
+
limit: Optional[int] = None,
|
|
101
|
+
purpose: Optional[str] = None,
|
|
102
|
+
requester_region: Optional[str] = None,
|
|
103
|
+
requester_clearance: Optional[str] = None,
|
|
104
|
+
poll_config: Optional[PollConfig] = None,
|
|
105
|
+
) -> FetchResult:
|
|
106
|
+
"""
|
|
107
|
+
Submit an intent request and return data — full grant lifecycle internal.
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
DeniedError Policy rejected. Do not retry.
|
|
111
|
+
ApprovalRequiredError Needs human approval (request_id attached).
|
|
112
|
+
PollTimeoutError Approval polling exceeded configured timeout.
|
|
113
|
+
GrantExpiredError / GrantRevokedError Transient; re-submit.
|
|
114
|
+
PolicyBundleRevokedError Unrecoverable; escalate.
|
|
115
|
+
"""
|
|
116
|
+
dataset = require_str("dataset", dataset)
|
|
117
|
+
|
|
118
|
+
if self._logger:
|
|
119
|
+
self._logger.info(
|
|
120
|
+
"[VaultKit] Execute",
|
|
121
|
+
extra={"dataset": dataset},
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
result = self.query(
|
|
125
|
+
dataset=dataset,
|
|
126
|
+
fields=fields,
|
|
127
|
+
filters=filters,
|
|
128
|
+
limit=limit,
|
|
129
|
+
purpose=purpose,
|
|
130
|
+
requester_region=requester_region,
|
|
131
|
+
requester_clearance=requester_clearance,
|
|
132
|
+
poll=True,
|
|
133
|
+
poll_config=poll_config,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
if result.needs_approval:
|
|
137
|
+
if self._logger:
|
|
138
|
+
self._logger.info(
|
|
139
|
+
"[VaultKit] Approval required",
|
|
140
|
+
extra={
|
|
141
|
+
"dataset": dataset,
|
|
142
|
+
"request_id": result.request_id,
|
|
143
|
+
"approver_role": result.approver_role,
|
|
144
|
+
},
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
raise ApprovalRequiredError(
|
|
148
|
+
f"Dataset '{dataset}' requires human approval. "
|
|
149
|
+
f"Approver role: {result.approver_role or 'unknown'}. "
|
|
150
|
+
f"Use poll_request('{result.request_id}') to check status.",
|
|
151
|
+
request_id=result.request_id,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
if result.is_denied:
|
|
155
|
+
if self._logger:
|
|
156
|
+
self._logger.info(
|
|
157
|
+
"[VaultKit] Request denied",
|
|
158
|
+
extra={
|
|
159
|
+
"dataset": dataset,
|
|
160
|
+
"request_id": result.request_id,
|
|
161
|
+
"request_id": result.request_id or "none",
|
|
162
|
+
"policy_id": result.policy_id,
|
|
163
|
+
},
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
raise DeniedError(
|
|
167
|
+
result.reason or "Request denied by policy",
|
|
168
|
+
policy_id=result.policy_id,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Case 1: data already returned (reuse path)
|
|
172
|
+
if result.has_data and not result.grant_ref:
|
|
173
|
+
if self._logger:
|
|
174
|
+
self._logger.debug(
|
|
175
|
+
"[VaultKit] Reused request — returning data directly",
|
|
176
|
+
extra={
|
|
177
|
+
"dataset": dataset,
|
|
178
|
+
"request_id": result.request_id,
|
|
179
|
+
},
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return FetchResult(
|
|
183
|
+
rows=result.rows,
|
|
184
|
+
meta=result.meta,
|
|
185
|
+
correlation_id=result.correlation_id,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# Case 2: normal grant flow
|
|
189
|
+
if result.grant_ref:
|
|
190
|
+
return self.fetch(grant_ref=result.grant_ref)
|
|
191
|
+
|
|
192
|
+
raise ValidationError(
|
|
193
|
+
f"Unexpected state after polling: status={result.status}, no grant_ref or data."
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if self._logger:
|
|
197
|
+
self._logger.debug(
|
|
198
|
+
"[VaultKit] Fetch after grant",
|
|
199
|
+
extra={
|
|
200
|
+
"dataset": dataset,
|
|
201
|
+
"grant_ref": result.grant_ref,
|
|
202
|
+
"request_id": result.request_id,
|
|
203
|
+
},
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return self.fetch(grant_ref=result.grant_ref)
|
|
207
|
+
|
|
208
|
+
# Low-level primitives
|
|
209
|
+
|
|
210
|
+
def query(
|
|
211
|
+
self,
|
|
212
|
+
*,
|
|
213
|
+
dataset: str,
|
|
214
|
+
fields: Optional[List[str]] = None,
|
|
215
|
+
filters: Optional[List[Dict[str, Any]]] = None,
|
|
216
|
+
limit: Optional[int] = None,
|
|
217
|
+
purpose: Optional[str] = None,
|
|
218
|
+
requester_region: Optional[str] = None,
|
|
219
|
+
requester_clearance: Optional[str] = None,
|
|
220
|
+
poll: bool = False,
|
|
221
|
+
poll_config: Optional[PollConfig] = None,
|
|
222
|
+
) -> QueryResult:
|
|
223
|
+
dataset = require_str("dataset", dataset)
|
|
224
|
+
limit = validate_limit(limit)
|
|
225
|
+
filters = validate_filters(filters)
|
|
226
|
+
|
|
227
|
+
req: Dict[str, Any] = {"dataset": dataset}
|
|
228
|
+
body: Dict[str, Any] = {"request": req}
|
|
229
|
+
|
|
230
|
+
if fields is not None:
|
|
231
|
+
req["fields"] = list(fields)
|
|
232
|
+
if limit is not None:
|
|
233
|
+
req["limit"] = limit
|
|
234
|
+
if purpose is not None:
|
|
235
|
+
req["purpose"] = str(purpose)
|
|
236
|
+
if filters is not None:
|
|
237
|
+
req["filters"] = filters
|
|
238
|
+
if requester_region is not None:
|
|
239
|
+
body["requester_region"] = requester_region
|
|
240
|
+
if requester_clearance is not None:
|
|
241
|
+
body["requester_clearance"] = requester_clearance
|
|
242
|
+
|
|
243
|
+
if self._logger:
|
|
244
|
+
self._logger.debug(
|
|
245
|
+
"[VaultKit] Submit intent",
|
|
246
|
+
extra={
|
|
247
|
+
"dataset": dataset,
|
|
248
|
+
"poll": poll,
|
|
249
|
+
"has_fields": fields is not None,
|
|
250
|
+
"has_filters": filters is not None,
|
|
251
|
+
"limit": limit,
|
|
252
|
+
},
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
def _do() -> QueryResult:
|
|
256
|
+
data = self._http.post(
|
|
257
|
+
self._path("/intent/requests"),
|
|
258
|
+
json_body=body,
|
|
259
|
+
)
|
|
260
|
+
result = QueryResult.from_dict(data)
|
|
261
|
+
|
|
262
|
+
if self._logger:
|
|
263
|
+
self._logger.debug(
|
|
264
|
+
"[VaultKit] Intent response",
|
|
265
|
+
extra={
|
|
266
|
+
"dataset": dataset,
|
|
267
|
+
"status": result.status,
|
|
268
|
+
"request_id": result.request_id,
|
|
269
|
+
"policy_id": result.policy_id,
|
|
270
|
+
},
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
if result.is_denied:
|
|
274
|
+
raise DeniedError(
|
|
275
|
+
result.reason or "Request denied",
|
|
276
|
+
policy_id=result.policy_id,
|
|
277
|
+
)
|
|
278
|
+
if result.is_pending and not poll:
|
|
279
|
+
raise QueuedError(
|
|
280
|
+
"Request is queued. Set poll=True or call client.poll(result).",
|
|
281
|
+
request_id=result.request_id,
|
|
282
|
+
)
|
|
283
|
+
return result
|
|
284
|
+
|
|
285
|
+
result = with_retries(_do, config=self.config.retries)
|
|
286
|
+
|
|
287
|
+
if poll and result.is_pending:
|
|
288
|
+
result = self.poll(result, config=poll_config)
|
|
289
|
+
|
|
290
|
+
return result
|
|
291
|
+
|
|
292
|
+
def fetch(self, *, grant_ref: str) -> FetchResult:
|
|
293
|
+
grant_ref = require_str("grant_ref", grant_ref)
|
|
294
|
+
|
|
295
|
+
if self._logger:
|
|
296
|
+
self._logger.debug(
|
|
297
|
+
"[VaultKit] Fetch",
|
|
298
|
+
extra={"grant_ref": grant_ref},
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
def _do() -> FetchResult:
|
|
302
|
+
data = self._http.post(
|
|
303
|
+
self._path(f"/grants/{grant_ref}/fetch"),
|
|
304
|
+
json_body={},
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
result = FetchResult.from_dict(data)
|
|
308
|
+
|
|
309
|
+
if self._logger:
|
|
310
|
+
self._logger.info(
|
|
311
|
+
"[VaultKit] Fetch complete",
|
|
312
|
+
extra={"grant_ref": grant_ref, "row_count": result.row_count},
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
return result
|
|
316
|
+
|
|
317
|
+
return with_retries(_do, config=self.config.retries)
|
|
318
|
+
|
|
319
|
+
def poll(
|
|
320
|
+
self,
|
|
321
|
+
result: QueryResult,
|
|
322
|
+
*,
|
|
323
|
+
config: Optional[PollConfig] = None,
|
|
324
|
+
) -> QueryResult:
|
|
325
|
+
if not result.is_pending:
|
|
326
|
+
return result
|
|
327
|
+
|
|
328
|
+
cfg = config or self.config.polling
|
|
329
|
+
|
|
330
|
+
if self._logger:
|
|
331
|
+
self._logger.debug(
|
|
332
|
+
"[VaultKit] Poll start",
|
|
333
|
+
extra={
|
|
334
|
+
"request_id": result.request_id,
|
|
335
|
+
"status": result.status,
|
|
336
|
+
"timeout_s": cfg.timeout_s,
|
|
337
|
+
},
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
def poll_fn(request_id: str) -> QueryResult:
|
|
341
|
+
def _do() -> QueryResult:
|
|
342
|
+
data = self._http.get(self._path(f"/requests/{request_id}"))
|
|
343
|
+
return QueryResult.from_dict(data)
|
|
344
|
+
|
|
345
|
+
return with_retries(_do, config=self.config.retries)
|
|
346
|
+
|
|
347
|
+
return poll_until_done(
|
|
348
|
+
initial=result,
|
|
349
|
+
poll_fn=poll_fn,
|
|
350
|
+
config=cfg,
|
|
351
|
+
logger=self._logger,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
def poll_request(self, *, request_id: str) -> QueryResult:
|
|
355
|
+
"""Poll by request_id directly — used by the check_approval tool flow."""
|
|
356
|
+
request_id = require_str("request_id", request_id)
|
|
357
|
+
|
|
358
|
+
if self._logger:
|
|
359
|
+
self._logger.debug(
|
|
360
|
+
"[VaultKit] Poll request",
|
|
361
|
+
extra={"request_id": request_id},
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
def _do() -> QueryResult:
|
|
365
|
+
data = self._http.get(self._path(f"/requests/{request_id}"))
|
|
366
|
+
return QueryResult.from_dict(data)
|
|
367
|
+
|
|
368
|
+
return with_retries(_do, config=self.config.retries)
|
|
369
|
+
|
|
370
|
+
# Discovery
|
|
371
|
+
|
|
372
|
+
def datasets(
|
|
373
|
+
self,
|
|
374
|
+
*,
|
|
375
|
+
environment: str = "production",
|
|
376
|
+
requester_region: Optional[str] = None,
|
|
377
|
+
dataset_region: Optional[str] = None,
|
|
378
|
+
) -> List[DatasetInfo]:
|
|
379
|
+
environment = require_str("environment", environment)
|
|
380
|
+
params: Dict[str, Any] = {"environment": environment}
|
|
381
|
+
if requester_region is not None:
|
|
382
|
+
params["requester_region"] = requester_region
|
|
383
|
+
if dataset_region is not None:
|
|
384
|
+
params["dataset_region"] = dataset_region
|
|
385
|
+
|
|
386
|
+
if self._logger:
|
|
387
|
+
self._logger.debug(
|
|
388
|
+
"[VaultKit] List datasets",
|
|
389
|
+
extra={
|
|
390
|
+
"environment": environment,
|
|
391
|
+
"requester_region": requester_region,
|
|
392
|
+
"dataset_region": dataset_region,
|
|
393
|
+
},
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
def _do() -> List[DatasetInfo]:
|
|
397
|
+
data = self._http.get(self._path("/aql/datasets"), params=params)
|
|
398
|
+
raw = data.get("datasets")
|
|
399
|
+
if not isinstance(raw, list):
|
|
400
|
+
raise ValidationError("Invalid datasets response: expected list")
|
|
401
|
+
return [DatasetInfo.from_dict(d) for d in raw]
|
|
402
|
+
|
|
403
|
+
return with_retries(_do, config=self.config.retries)
|
|
404
|
+
|
|
405
|
+
def schema(
|
|
406
|
+
self,
|
|
407
|
+
dataset: str,
|
|
408
|
+
*,
|
|
409
|
+
environment: str = "production",
|
|
410
|
+
requester_region: Optional[str] = None,
|
|
411
|
+
dataset_region: Optional[str] = None,
|
|
412
|
+
) -> DatasetSchema:
|
|
413
|
+
dataset = require_str("dataset", dataset)
|
|
414
|
+
environment = require_str("environment", environment)
|
|
415
|
+
params: Dict[str, Any] = {"environment": environment}
|
|
416
|
+
if requester_region is not None:
|
|
417
|
+
params["requester_region"] = requester_region
|
|
418
|
+
if dataset_region is not None:
|
|
419
|
+
params["dataset_region"] = dataset_region
|
|
420
|
+
|
|
421
|
+
if self._logger:
|
|
422
|
+
self._logger.debug(
|
|
423
|
+
"[VaultKit] Get schema",
|
|
424
|
+
extra={
|
|
425
|
+
"dataset": dataset,
|
|
426
|
+
"environment": environment,
|
|
427
|
+
"requester_region": requester_region,
|
|
428
|
+
"dataset_region": dataset_region,
|
|
429
|
+
},
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
def _do() -> DatasetSchema:
|
|
433
|
+
data = self._http.get(
|
|
434
|
+
self._path(f"/aql/datasets/{dataset}/schema"),
|
|
435
|
+
params=params,
|
|
436
|
+
)
|
|
437
|
+
if not isinstance(data, dict):
|
|
438
|
+
raise ValidationError("Invalid schema response")
|
|
439
|
+
return DatasetSchema.from_dict(data)
|
|
440
|
+
|
|
441
|
+
return with_retries(_do, config=self.config.retries)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__all__ = []
|