pangea-sdk 6.1.1__py3-none-any.whl → 6.2.0b2__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.
- pangea/__init__.py +9 -1
- pangea/asyncio/__init__.py +1 -0
- pangea/asyncio/file_uploader.py +4 -2
- pangea/asyncio/request.py +199 -35
- pangea/asyncio/services/__init__.py +3 -0
- pangea/asyncio/services/ai_guard.py +91 -2
- pangea/asyncio/services/audit.py +307 -2
- pangea/asyncio/services/authn.py +12 -2
- pangea/asyncio/services/base.py +4 -0
- pangea/asyncio/services/file_scan.py +7 -1
- pangea/asyncio/services/intel.py +6 -2
- pangea/asyncio/services/management.py +576 -0
- pangea/asyncio/services/prompt_guard.py +112 -2
- pangea/asyncio/services/redact.py +269 -4
- pangea/asyncio/services/sanitize.py +5 -1
- pangea/asyncio/services/share.py +5 -1
- pangea/asyncio/services/vault.py +4 -0
- pangea/audit_logger.py +3 -1
- pangea/deep_verify.py +13 -13
- pangea/deprecated.py +1 -1
- pangea/dump_audit.py +2 -3
- pangea/exceptions.py +8 -5
- pangea/file_uploader.py +4 -0
- pangea/request.py +205 -52
- pangea/response.py +15 -12
- pangea/services/__init__.py +3 -0
- pangea/services/ai_guard.py +497 -16
- pangea/services/audit/audit.py +310 -8
- pangea/services/audit/models.py +279 -0
- pangea/services/audit/signing.py +1 -1
- pangea/services/audit/util.py +10 -10
- pangea/services/authn/authn.py +12 -2
- pangea/services/authn/models.py +3 -0
- pangea/services/authz.py +4 -0
- pangea/services/base.py +5 -1
- pangea/services/embargo.py +6 -0
- pangea/services/file_scan.py +7 -1
- pangea/services/intel.py +4 -0
- pangea/services/management.py +720 -0
- pangea/services/prompt_guard.py +193 -2
- pangea/services/redact.py +477 -7
- pangea/services/sanitize.py +5 -1
- pangea/services/share/share.py +13 -7
- pangea/services/vault/models/asymmetric.py +4 -0
- pangea/services/vault/models/common.py +4 -0
- pangea/services/vault/models/symmetric.py +4 -0
- pangea/services/vault/vault.py +2 -4
- pangea/tools.py +13 -9
- pangea/utils.py +3 -5
- pangea/verify_audit.py +23 -27
- {pangea_sdk-6.1.1.dist-info → pangea_sdk-6.2.0b2.dist-info}/METADATA +4 -4
- pangea_sdk-6.2.0b2.dist-info/RECORD +62 -0
- pangea_sdk-6.1.1.dist-info/RECORD +0 -60
- {pangea_sdk-6.1.1.dist-info → pangea_sdk-6.2.0b2.dist-info}/WHEEL +0 -0
pangea/__init__.py
CHANGED
@@ -1,7 +1,15 @@
|
|
1
|
-
__version__ = "6.
|
1
|
+
__version__ = "6.2.0beta2"
|
2
2
|
|
3
3
|
from pangea.asyncio.request import PangeaRequestAsync
|
4
4
|
from pangea.config import PangeaConfig
|
5
5
|
from pangea.file_uploader import FileUploader
|
6
6
|
from pangea.request import PangeaRequest
|
7
7
|
from pangea.response import PangeaResponse
|
8
|
+
|
9
|
+
__all__ = (
|
10
|
+
"FileUploader",
|
11
|
+
"PangeaConfig",
|
12
|
+
"PangeaRequest",
|
13
|
+
"PangeaRequestAsync",
|
14
|
+
"PangeaResponse",
|
15
|
+
)
|
pangea/asyncio/__init__.py
CHANGED
pangea/asyncio/file_uploader.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
|
3
6
|
import io
|
4
7
|
import logging
|
5
|
-
from typing import Dict, Optional
|
6
8
|
|
7
9
|
from pangea.asyncio.request import PangeaRequestAsync
|
8
10
|
from pangea.request import PangeaConfig
|
@@ -24,7 +26,7 @@ class FileUploaderAsync:
|
|
24
26
|
url: str,
|
25
27
|
file: io.BufferedReader,
|
26
28
|
transfer_method: TransferMethod = TransferMethod.PUT_URL,
|
27
|
-
file_details:
|
29
|
+
file_details: dict | None = None,
|
28
30
|
) -> None:
|
29
31
|
if transfer_method == TransferMethod.PUT_URL:
|
30
32
|
files = [("file", ("filename", file, "application/octet-stream"))]
|
pangea/asyncio/request.py
CHANGED
@@ -1,23 +1,39 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
3
7
|
from __future__ import annotations
|
4
8
|
|
5
9
|
import asyncio
|
6
10
|
import json
|
7
11
|
import time
|
8
|
-
from
|
12
|
+
from collections.abc import Iterable, Mapping
|
13
|
+
from typing import Dict, List, Optional, Sequence, Tuple, Type, Union, cast, overload
|
9
14
|
|
10
15
|
import aiohttp
|
11
16
|
from aiohttp import FormData
|
12
|
-
from pydantic import BaseModel
|
17
|
+
from pydantic import BaseModel, TypeAdapter
|
13
18
|
from pydantic_core import to_jsonable_python
|
14
|
-
from typing_extensions import Any, TypeVar
|
19
|
+
from typing_extensions import Any, Literal, TypeAlias, TypeVar, override
|
15
20
|
|
16
21
|
import pangea.exceptions as pe
|
17
22
|
from pangea.request import MultipartResponse, PangeaRequestBase
|
18
23
|
from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult, ResponseStatus, TransferMethod
|
19
24
|
from pangea.utils import default_encoder
|
20
25
|
|
26
|
+
_FileName: TypeAlias = Union[str, None]
|
27
|
+
_FileContent: TypeAlias = Union[str, bytes]
|
28
|
+
_FileContentType: TypeAlias = str
|
29
|
+
_FileCustomHeaders: TypeAlias = Mapping[str, str]
|
30
|
+
_FileSpecTuple2: TypeAlias = tuple[_FileName, _FileContent]
|
31
|
+
_FileSpecTuple3: TypeAlias = tuple[_FileName, _FileContent, _FileContentType]
|
32
|
+
_FileSpecTuple4: TypeAlias = tuple[_FileName, _FileContent, _FileContentType, _FileCustomHeaders]
|
33
|
+
_FileSpec: TypeAlias = Union[_FileContent, _FileSpecTuple2, _FileSpecTuple3, _FileSpecTuple4]
|
34
|
+
_Files: TypeAlias = Union[Mapping[str, _FileSpec], Iterable[tuple[str, _FileSpec]]]
|
35
|
+
|
36
|
+
|
21
37
|
TResult = TypeVar("TResult", bound=PangeaResponseResult)
|
22
38
|
|
23
39
|
|
@@ -30,26 +46,86 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
30
46
|
be set in PangeaConfig.
|
31
47
|
"""
|
32
48
|
|
49
|
+
async def delete(self, endpoint: str) -> None:
|
50
|
+
"""
|
51
|
+
Makes a DELETE call to a Pangea endpoint.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
endpoint: The Pangea API endpoint.
|
55
|
+
"""
|
56
|
+
|
57
|
+
url = self._url(endpoint)
|
58
|
+
|
59
|
+
self.logger.debug(
|
60
|
+
json.dumps({"service": self.service, "action": "delete", "url": url}, default=default_encoder)
|
61
|
+
)
|
62
|
+
|
63
|
+
requests_response = await self._http_delete(url, headers=self._headers())
|
64
|
+
await self._check_http_errors(requests_response)
|
65
|
+
|
66
|
+
@overload
|
33
67
|
async def post(
|
34
68
|
self,
|
35
69
|
endpoint: str,
|
36
70
|
result_class: Type[TResult],
|
37
|
-
data: str | BaseModel |
|
71
|
+
data: str | BaseModel | Mapping[str, Any] | None = None,
|
38
72
|
files: Optional[List[Tuple]] = None,
|
39
73
|
poll_result: bool = True,
|
40
74
|
url: Optional[str] = None,
|
75
|
+
*,
|
76
|
+
pangea_response: Literal[True] = True,
|
41
77
|
) -> PangeaResponse[TResult]:
|
42
|
-
"""
|
78
|
+
"""
|
79
|
+
Makes a POST call to a Pangea Service endpoint.
|
43
80
|
|
44
81
|
Args:
|
45
|
-
endpoint
|
46
|
-
data
|
82
|
+
endpoint: The Pangea Service API endpoint.
|
83
|
+
data: The POST body payload object
|
47
84
|
|
48
85
|
Returns:
|
49
86
|
PangeaResponse which contains the response in its entirety and
|
50
87
|
various properties to retrieve individual fields
|
51
88
|
"""
|
52
89
|
|
90
|
+
@overload
|
91
|
+
async def post(
|
92
|
+
self,
|
93
|
+
endpoint: str,
|
94
|
+
result_class: Type[TResult],
|
95
|
+
data: str | BaseModel | Mapping[str, Any] | None = None,
|
96
|
+
files: Optional[List[Tuple]] = None,
|
97
|
+
poll_result: bool = True,
|
98
|
+
url: Optional[str] = None,
|
99
|
+
*,
|
100
|
+
pangea_response: Literal[False],
|
101
|
+
) -> TResult:
|
102
|
+
"""
|
103
|
+
Makes a POST call to a Pangea Service endpoint.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
endpoint: The Pangea Service API endpoint.
|
107
|
+
data: The POST body payload object
|
108
|
+
"""
|
109
|
+
|
110
|
+
async def post(
|
111
|
+
self,
|
112
|
+
endpoint: str,
|
113
|
+
result_class: Type[TResult],
|
114
|
+
data: str | BaseModel | Mapping[str, Any] | None = None,
|
115
|
+
files: Optional[List[Tuple]] = None,
|
116
|
+
poll_result: bool = True,
|
117
|
+
url: Optional[str] = None,
|
118
|
+
*,
|
119
|
+
pangea_response: bool = True,
|
120
|
+
) -> PangeaResponse[TResult] | TResult:
|
121
|
+
"""
|
122
|
+
Makes a POST call to a Pangea Service endpoint.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
endpoint: The Pangea Service API endpoint.
|
126
|
+
data: The POST body payload object
|
127
|
+
"""
|
128
|
+
|
53
129
|
if isinstance(data, BaseModel):
|
54
130
|
data = data.model_dump(exclude_none=True)
|
55
131
|
|
@@ -80,15 +156,22 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
80
156
|
endpoint, result_class=result_class, data=data, files=files
|
81
157
|
)
|
82
158
|
else:
|
159
|
+
headers = self._headers()
|
160
|
+
if transfer_method == TransferMethod.MULTIPART.value:
|
161
|
+
del headers["Content-Type"]
|
83
162
|
requests_response = await self._http_post(
|
84
|
-
url, headers=
|
163
|
+
url, headers=headers, data=data, files=files, presigned_url_post=False
|
85
164
|
)
|
86
165
|
|
87
166
|
await self._check_http_errors(requests_response)
|
88
167
|
|
168
|
+
if not pangea_response:
|
169
|
+
type_adapter = TypeAdapter(result_class)
|
170
|
+
return type_adapter.validate_python(await requests_response.json())
|
171
|
+
|
89
172
|
if "multipart/form-data" in requests_response.headers.get("content-type", ""):
|
90
173
|
multipart_response = await self._process_multipart_response(requests_response)
|
91
|
-
|
174
|
+
pangea_response_obj: PangeaResponse = PangeaResponse(
|
92
175
|
requests_response,
|
93
176
|
result_class=result_class,
|
94
177
|
json=multipart_response.pangea_json,
|
@@ -101,47 +184,110 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
101
184
|
json.dumps({"service": self.service, "action": "post", "url": url, "response": json_resp})
|
102
185
|
)
|
103
186
|
|
104
|
-
|
187
|
+
pangea_response_obj = PangeaResponse(requests_response, result_class=result_class, json=json_resp)
|
105
188
|
except aiohttp.ContentTypeError as e:
|
106
|
-
raise pe.PangeaException(
|
189
|
+
raise pe.PangeaException(
|
190
|
+
f"Failed to decode json response. {e}. Body: {await requests_response.text()}"
|
191
|
+
) from e
|
107
192
|
|
108
193
|
if poll_result:
|
109
|
-
|
194
|
+
pangea_response_obj = await self._handle_queued_result(pangea_response_obj)
|
110
195
|
|
111
|
-
return self._check_response(
|
196
|
+
return self._check_response(pangea_response_obj)
|
112
197
|
|
113
|
-
|
114
|
-
|
198
|
+
@overload
|
199
|
+
async def get(
|
200
|
+
self,
|
201
|
+
path: str,
|
202
|
+
result_class: Type[TResult],
|
203
|
+
check_response: bool = True,
|
204
|
+
*,
|
205
|
+
params: (
|
206
|
+
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
207
|
+
| None
|
208
|
+
) = None,
|
209
|
+
pangea_response: Literal[True] = True,
|
210
|
+
) -> PangeaResponse[TResult]:
|
211
|
+
"""
|
212
|
+
Makes the GET call to a Pangea Service endpoint.
|
115
213
|
|
116
214
|
Args:
|
117
|
-
|
118
|
-
|
215
|
+
path: Additional URL path
|
216
|
+
params: Dictionary of querystring data to attach to the request
|
119
217
|
|
120
218
|
Returns:
|
121
219
|
PangeaResponse which contains the response in its entirety and
|
122
220
|
various properties to retrieve individual fields
|
123
221
|
"""
|
124
222
|
|
223
|
+
@overload
|
224
|
+
async def get(
|
225
|
+
self,
|
226
|
+
path: str,
|
227
|
+
result_class: Type[TResult],
|
228
|
+
check_response: bool = True,
|
229
|
+
*,
|
230
|
+
params: (
|
231
|
+
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
232
|
+
| None
|
233
|
+
) = None,
|
234
|
+
pangea_response: Literal[False] = False,
|
235
|
+
) -> TResult:
|
236
|
+
"""
|
237
|
+
Makes the GET call to a Pangea Service endpoint.
|
238
|
+
|
239
|
+
Args:
|
240
|
+
path: Additional URL path
|
241
|
+
params: Dictionary of querystring data to attach to the request
|
242
|
+
"""
|
243
|
+
|
244
|
+
async def get(
|
245
|
+
self,
|
246
|
+
path: str,
|
247
|
+
result_class: Type[TResult],
|
248
|
+
check_response: bool = True,
|
249
|
+
*,
|
250
|
+
params: (
|
251
|
+
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
252
|
+
| None
|
253
|
+
) = None,
|
254
|
+
pangea_response: bool = True,
|
255
|
+
) -> PangeaResponse[TResult] | TResult:
|
256
|
+
"""
|
257
|
+
Makes the GET call to a Pangea Service endpoint.
|
258
|
+
|
259
|
+
Args:
|
260
|
+
path: Additional URL path
|
261
|
+
params: Dictionary of querystring data to attach to the request
|
262
|
+
pangea_response: Whether or not the response body follows Pangea's
|
263
|
+
standard response schema
|
264
|
+
"""
|
265
|
+
|
125
266
|
url = self._url(path)
|
126
267
|
self.logger.debug(json.dumps({"service": self.service, "action": "get", "url": url}))
|
127
268
|
|
128
|
-
async with self.session.get(url, headers=self._headers()) as requests_response:
|
269
|
+
async with self.session.get(url, params=params, headers=self._headers()) as requests_response:
|
129
270
|
await self._check_http_errors(requests_response)
|
130
|
-
|
271
|
+
|
272
|
+
if not pangea_response:
|
273
|
+
type_adapter = TypeAdapter(result_class)
|
274
|
+
return type_adapter.validate_python(await requests_response.json())
|
275
|
+
|
276
|
+
pangea_response_obj = PangeaResponse(
|
131
277
|
requests_response, result_class=result_class, json=await requests_response.json()
|
132
278
|
)
|
133
279
|
|
134
280
|
self.logger.debug(
|
135
281
|
json.dumps(
|
136
|
-
{"service": self.service, "action": "get", "url": url, "response":
|
282
|
+
{"service": self.service, "action": "get", "url": url, "response": pangea_response_obj.json},
|
137
283
|
default=default_encoder,
|
138
284
|
)
|
139
285
|
)
|
140
286
|
|
141
287
|
if check_response is False:
|
142
|
-
return
|
288
|
+
return pangea_response_obj
|
143
289
|
|
144
|
-
return self._check_response(
|
290
|
+
return self._check_response(pangea_response_obj)
|
145
291
|
|
146
292
|
async def _check_http_errors(self, resp: aiohttp.ClientResponse):
|
147
293
|
if resp.status == 503:
|
@@ -164,7 +310,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
164
310
|
|
165
311
|
return await self.poll_result_by_id(request_id, response.result_class, check_response=check_response)
|
166
312
|
|
167
|
-
async def post_presigned_url(self, url: str, data:
|
313
|
+
async def post_presigned_url(self, url: str, data: dict[Any, Any], files: Sequence[Tuple]):
|
168
314
|
# Send form request with file and upload_details as body
|
169
315
|
resp = await self._http_post(url=url, data=data, files=files, presigned_url_post=True)
|
170
316
|
self.logger.debug(
|
@@ -275,38 +421,55 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
275
421
|
attached_files = await self._get_attached_files(multipart_reader)
|
276
422
|
return MultipartResponse(pangea_json, attached_files) # type: ignore[arg-type]
|
277
423
|
|
424
|
+
async def _http_delete(
|
425
|
+
self,
|
426
|
+
url: str,
|
427
|
+
*,
|
428
|
+
headers: Mapping[str, str | bytes | None] = {},
|
429
|
+
) -> aiohttp.ClientResponse:
|
430
|
+
return await self.session.delete(url, headers=headers)
|
431
|
+
|
278
432
|
async def _http_post(
|
279
433
|
self,
|
280
434
|
url: str,
|
281
|
-
headers:
|
282
|
-
data:
|
283
|
-
files:
|
435
|
+
headers: Mapping[str, str] = {},
|
436
|
+
data: str | dict[str, Any] | None = None,
|
437
|
+
files: _Files | None = None,
|
284
438
|
presigned_url_post: bool = False,
|
285
439
|
) -> aiohttp.ClientResponse:
|
440
|
+
if data is None:
|
441
|
+
data = {}
|
442
|
+
|
286
443
|
if files:
|
287
444
|
form = FormData()
|
288
445
|
if presigned_url_post:
|
289
|
-
|
446
|
+
assert isinstance(data, dict)
|
447
|
+
assert isinstance(files, list)
|
448
|
+
for k, v in data.items():
|
290
449
|
form.add_field(k, v)
|
291
|
-
for
|
450
|
+
for _name, value in files:
|
292
451
|
form.add_field("file", value[1], filename=value[0], content_type=value[2])
|
293
452
|
else:
|
294
|
-
|
453
|
+
assert isinstance(files, list)
|
454
|
+
data_send: str | FormData = (
|
455
|
+
json.dumps(data, default=default_encoder) if isinstance(data, dict) else data
|
456
|
+
)
|
295
457
|
form.add_field("request", data_send, content_type="application/json")
|
296
458
|
for name, value in files:
|
297
459
|
form.add_field(name, value[1], filename=value[0], content_type=value[2])
|
298
460
|
|
299
|
-
data_send = form
|
461
|
+
data_send = form
|
300
462
|
else:
|
301
463
|
data_send = json.dumps(data, default=default_encoder) if isinstance(data, dict) else data
|
302
464
|
|
465
|
+
assert isinstance(self.session, aiohttp.ClientSession)
|
303
466
|
return await self.session.post(url, headers=headers, data=data_send)
|
304
467
|
|
305
468
|
async def _http_put(
|
306
469
|
self,
|
307
470
|
url: str,
|
308
471
|
files: Sequence[Tuple],
|
309
|
-
headers:
|
472
|
+
headers: Mapping[str, str] = {},
|
310
473
|
) -> aiohttp.ClientResponse:
|
311
474
|
self.logger.debug(
|
312
475
|
json.dumps({"service": self.service, "action": "http_put", "url": url}, default=default_encoder)
|
@@ -318,8 +481,8 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
318
481
|
self,
|
319
482
|
endpoint: str,
|
320
483
|
result_class: Type[PangeaResponseResult],
|
321
|
-
data: Union[str,
|
322
|
-
files:
|
484
|
+
data: Union[str, Mapping[str, Any]] = {},
|
485
|
+
files: Sequence[Tuple] = [],
|
323
486
|
):
|
324
487
|
if len(files) == 0:
|
325
488
|
raise AttributeError("files attribute should have at least 1 file")
|
@@ -343,7 +506,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
343
506
|
self,
|
344
507
|
endpoint: str,
|
345
508
|
result_class: Type[PangeaResponseResult],
|
346
|
-
data: Union[str,
|
509
|
+
data: Union[str, Mapping[str, Any]] = {},
|
347
510
|
) -> PangeaResponse:
|
348
511
|
# Send request
|
349
512
|
try:
|
@@ -394,7 +557,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
394
557
|
{"service": self.service, "action": "poll_presigned_url", "step": "exit", "cause": {str(e)}}
|
395
558
|
)
|
396
559
|
)
|
397
|
-
raise pe.PresignedURLException("Failed to pull Presigned URL", loop_exc.response, e)
|
560
|
+
raise pe.PresignedURLException("Failed to pull Presigned URL", loop_exc.response, e) from e
|
398
561
|
|
399
562
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_presigned_url", "step": "exit"}))
|
400
563
|
|
@@ -426,6 +589,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
426
589
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_result_retry", "step": "exit"}))
|
427
590
|
return self._check_response(response)
|
428
591
|
|
592
|
+
@override
|
429
593
|
def _init_session(self) -> aiohttp.ClientSession:
|
430
594
|
# retry_config = Retry(
|
431
595
|
# total=self.config.request_retries,
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# ruff: noqa: F401
|
2
|
+
|
1
3
|
from .ai_guard import AIGuardAsync
|
2
4
|
from .audit import AuditAsync
|
3
5
|
from .authn import AuthNAsync
|
@@ -5,6 +7,7 @@ from .authz import AuthZAsync
|
|
5
7
|
from .embargo import EmbargoAsync
|
6
8
|
from .file_scan import FileScanAsync
|
7
9
|
from .intel import DomainIntelAsync, FileIntelAsync, IpIntelAsync, UrlIntelAsync, UserIntelAsync
|
10
|
+
from .management import ManagementAsync
|
8
11
|
from .prompt_guard import PromptGuardAsync
|
9
12
|
from .redact import RedactAsync
|
10
13
|
from .sanitize import SanitizeAsync
|
@@ -1,13 +1,24 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
from collections.abc import Mapping
|
3
4
|
from typing import overload
|
4
5
|
|
5
|
-
from typing_extensions import TypeVar
|
6
|
+
from typing_extensions import Literal, TypeVar
|
6
7
|
|
7
8
|
from pangea.asyncio.services.base import ServiceBaseAsync
|
8
9
|
from pangea.config import PangeaConfig
|
9
10
|
from pangea.response import PangeaResponse
|
10
|
-
from pangea.services.ai_guard import
|
11
|
+
from pangea.services.ai_guard import (
|
12
|
+
AuditDataActivityConfig,
|
13
|
+
ConnectionsConfig,
|
14
|
+
LogFields,
|
15
|
+
Overrides,
|
16
|
+
RecipeConfig,
|
17
|
+
ServiceConfig,
|
18
|
+
ServiceConfigFilter,
|
19
|
+
ServiceConfigsPage,
|
20
|
+
TextGuardResult,
|
21
|
+
)
|
11
22
|
|
12
23
|
_T = TypeVar("_T")
|
13
24
|
|
@@ -170,3 +181,81 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
170
181
|
"log_fields": log_fields,
|
171
182
|
},
|
172
183
|
)
|
184
|
+
|
185
|
+
async def get_service_config(self, id: str) -> PangeaResponse[ServiceConfig]:
|
186
|
+
"""
|
187
|
+
OperationId: ai_guard_post_v1beta_config
|
188
|
+
"""
|
189
|
+
return await self.request.post("v1beta/config", data={"id": id}, result_class=ServiceConfig)
|
190
|
+
|
191
|
+
async def create_service_config(
|
192
|
+
self,
|
193
|
+
name: str,
|
194
|
+
*,
|
195
|
+
id: str | None = None,
|
196
|
+
audit_data_activity: AuditDataActivityConfig | None = None,
|
197
|
+
connections: ConnectionsConfig | None = None,
|
198
|
+
recipes: Mapping[str, RecipeConfig] | None = None,
|
199
|
+
) -> PangeaResponse[ServiceConfig]:
|
200
|
+
"""
|
201
|
+
OperationId: ai_guard_post_v1beta_config_create
|
202
|
+
"""
|
203
|
+
return await self.request.post(
|
204
|
+
"v1beta/config/create",
|
205
|
+
data={
|
206
|
+
"name": name,
|
207
|
+
"id": id,
|
208
|
+
"audit_data_activity": audit_data_activity,
|
209
|
+
"connections": connections,
|
210
|
+
"recipes": recipes,
|
211
|
+
},
|
212
|
+
result_class=ServiceConfig,
|
213
|
+
)
|
214
|
+
|
215
|
+
async def update_service_config(
|
216
|
+
self,
|
217
|
+
id: str,
|
218
|
+
name: str,
|
219
|
+
*,
|
220
|
+
audit_data_activity: AuditDataActivityConfig | None = None,
|
221
|
+
connections: ConnectionsConfig | None = None,
|
222
|
+
recipes: Mapping[str, RecipeConfig] | None = None,
|
223
|
+
) -> PangeaResponse[ServiceConfig]:
|
224
|
+
"""
|
225
|
+
OperationId: ai_guard_post_v1beta_config_update
|
226
|
+
"""
|
227
|
+
return await self.request.post(
|
228
|
+
"v1beta/config/update",
|
229
|
+
data={
|
230
|
+
"id": id,
|
231
|
+
"name": name,
|
232
|
+
"audit_data_activity": audit_data_activity,
|
233
|
+
"connections": connections,
|
234
|
+
"recipes": recipes,
|
235
|
+
},
|
236
|
+
result_class=ServiceConfig,
|
237
|
+
)
|
238
|
+
|
239
|
+
async def delete_service_config(self, id: str) -> PangeaResponse[ServiceConfig]:
|
240
|
+
"""
|
241
|
+
OperationId: ai_guard_post_v1beta_config_delete
|
242
|
+
"""
|
243
|
+
return await self.request.post("v1beta/config/delete", data={"id": id}, result_class=ServiceConfig)
|
244
|
+
|
245
|
+
async def list_service_configs(
|
246
|
+
self,
|
247
|
+
*,
|
248
|
+
filter: ServiceConfigFilter | None = None,
|
249
|
+
last: str | None = None,
|
250
|
+
order: Literal["asc", "desc"] | None = None,
|
251
|
+
order_by: Literal["id", "created_at", "updated_at"] | None = None,
|
252
|
+
size: int | None = None,
|
253
|
+
) -> PangeaResponse[ServiceConfigsPage]:
|
254
|
+
"""
|
255
|
+
OperationId: ai_guard_post_v1beta_config_list
|
256
|
+
"""
|
257
|
+
return await self.request.post(
|
258
|
+
"v1beta/config/list",
|
259
|
+
data={"filter": filter, "last": last, "order": order, "order_by": order_by, "size": size},
|
260
|
+
result_class=ServiceConfigsPage,
|
261
|
+
)
|