pangea-sdk 6.3.0__py3-none-any.whl → 6.5.0b1__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 +1 -1
- pangea/asyncio/request.py +152 -19
- pangea/asyncio/services/__init__.py +1 -0
- pangea/asyncio/services/ai_guard.py +205 -32
- pangea/asyncio/services/audit.py +301 -1
- pangea/asyncio/services/management.py +576 -0
- pangea/asyncio/services/prompt_guard.py +112 -2
- pangea/asyncio/services/redact.py +265 -4
- pangea/request.py +154 -19
- pangea/services/__init__.py +1 -0
- pangea/services/ai_guard.py +771 -59
- pangea/services/audit/audit.py +301 -1
- pangea/services/audit/models.py +273 -1
- pangea/services/management.py +720 -0
- pangea/services/prompt_guard.py +193 -2
- pangea/services/redact.py +473 -7
- {pangea_sdk-6.3.0.dist-info → pangea_sdk-6.5.0b1.dist-info}/METADATA +1 -1
- {pangea_sdk-6.3.0.dist-info → pangea_sdk-6.5.0b1.dist-info}/RECORD +19 -17
- {pangea_sdk-6.3.0.dist-info → pangea_sdk-6.5.0b1.dist-info}/WHEEL +0 -0
@@ -6,12 +6,15 @@
|
|
6
6
|
|
7
7
|
from __future__ import annotations
|
8
8
|
|
9
|
-
from
|
9
|
+
from collections.abc import Mapping, Sequence
|
10
|
+
from typing import Dict, List, Literal, Optional, Union, cast, overload
|
11
|
+
|
12
|
+
from pydantic import TypeAdapter
|
10
13
|
|
11
14
|
import pangea.services.redact as m
|
12
15
|
from pangea.asyncio.services.base import ServiceBaseAsync
|
13
16
|
from pangea.config import PangeaConfig
|
14
|
-
from pangea.response import PangeaResponse
|
17
|
+
from pangea.response import PangeaResponse, PangeaResponseResult
|
15
18
|
|
16
19
|
|
17
20
|
class RedactAsync(ServiceBaseAsync):
|
@@ -68,7 +71,7 @@ class RedactAsync(ServiceBaseAsync):
|
|
68
71
|
rules: Optional[List[str]] = None,
|
69
72
|
rulesets: Optional[List[str]] = None,
|
70
73
|
return_result: Optional[bool] = None,
|
71
|
-
redaction_method_overrides:
|
74
|
+
redaction_method_overrides: Mapping[str, m.RedactionMethodOverrides] | None = None,
|
72
75
|
llm_request: Optional[bool] = None,
|
73
76
|
vault_parameters: Optional[m.VaultParameters] = None,
|
74
77
|
) -> PangeaResponse[m.RedactResult]:
|
@@ -123,7 +126,7 @@ class RedactAsync(ServiceBaseAsync):
|
|
123
126
|
rules: Optional[List[str]] = None,
|
124
127
|
rulesets: Optional[List[str]] = None,
|
125
128
|
return_result: Optional[bool] = None,
|
126
|
-
redaction_method_overrides:
|
129
|
+
redaction_method_overrides: Mapping[str, m.RedactionMethodOverrides] | None = None,
|
127
130
|
llm_request: bool | None = None,
|
128
131
|
vault_parameters: m.VaultParameters | None = None,
|
129
132
|
) -> PangeaResponse[m.StructuredResult]:
|
@@ -204,3 +207,261 @@ class RedactAsync(ServiceBaseAsync):
|
|
204
207
|
"""
|
205
208
|
input = m.UnredactRequest(redacted_data=redacted_data, fpe_context=fpe_context)
|
206
209
|
return await self.request.post("v1/unredact", m.UnredactResult, data=input.model_dump(exclude_none=True))
|
210
|
+
|
211
|
+
async def get_service_config(self, config_id: str) -> PangeaResponse[m.ServiceConfigResult]:
|
212
|
+
"""
|
213
|
+
Get a service config.
|
214
|
+
|
215
|
+
|
216
|
+
OperationId: redact_post_v1beta_config
|
217
|
+
"""
|
218
|
+
response = await self.request.post("v1beta/config", PangeaResponseResult, data={"id": config_id})
|
219
|
+
response.result = TypeAdapter(m.ServiceConfigResult).validate_python(response.json["result"])
|
220
|
+
return cast(PangeaResponse[m.ServiceConfigResult], response)
|
221
|
+
|
222
|
+
@overload
|
223
|
+
async def create_service_config(
|
224
|
+
self,
|
225
|
+
name: str,
|
226
|
+
*,
|
227
|
+
version: Literal["1.0.0"],
|
228
|
+
enabled_rules: Sequence[str] | None = None,
|
229
|
+
redactions: Mapping[str, m.Redaction] | None = None,
|
230
|
+
vault_service_config_id: str | None = None,
|
231
|
+
salt_vault_secret_id: str | None = None,
|
232
|
+
rules: Mapping[str, m.RuleV1] | None = None,
|
233
|
+
rulesets: Mapping[str, m.RulesetV1] | None = None,
|
234
|
+
supported_languages: Sequence[Literal["en"]] | None = None,
|
235
|
+
) -> PangeaResponse[m.ServiceConfigResult]:
|
236
|
+
"""
|
237
|
+
Create a v1.0.0 service config.
|
238
|
+
|
239
|
+
OperationId: redact_post_v1beta_config_create
|
240
|
+
|
241
|
+
Args:
|
242
|
+
vault_service_config_id: Service config used to create the secret
|
243
|
+
salt_vault_secret_id: Pangea only allows hashing to be done using a salt value to prevent brute-force attacks.
|
244
|
+
"""
|
245
|
+
|
246
|
+
@overload
|
247
|
+
async def create_service_config(
|
248
|
+
self,
|
249
|
+
name: str,
|
250
|
+
*,
|
251
|
+
version: Literal["2.0.0"] | None = None,
|
252
|
+
enabled_rules: Sequence[str] | None = None,
|
253
|
+
enforce_enabled_rules: bool | None = None,
|
254
|
+
redactions: Mapping[str, m.Redaction] | None = None,
|
255
|
+
vault_service_config_id: str | None = None,
|
256
|
+
salt_vault_secret_id: str | None = None,
|
257
|
+
fpe_vault_secret_id: str | None = None,
|
258
|
+
rules: Mapping[str, m.RuleV2] | None = None,
|
259
|
+
rulesets: Mapping[str, m.RulesetV2] | None = None,
|
260
|
+
supported_languages: Sequence[Literal["en"]] | None = None,
|
261
|
+
) -> PangeaResponse[m.ServiceConfigResult]:
|
262
|
+
"""
|
263
|
+
Create a v2.0.0 service config.
|
264
|
+
|
265
|
+
OperationId: redact_post_v1beta_config_create
|
266
|
+
|
267
|
+
Args:
|
268
|
+
enforce_enabled_rules: Always run service config enabled rules across all redact calls regardless of flags?
|
269
|
+
vault_service_config_id: Service config used to create the secret
|
270
|
+
salt_vault_secret_id: Pangea only allows hashing to be done using a salt value to prevent brute-force attacks.
|
271
|
+
fpe_vault_secret_id: The ID of the key used by FF3 Encryption algorithms for FPE.
|
272
|
+
"""
|
273
|
+
|
274
|
+
async def create_service_config(
|
275
|
+
self,
|
276
|
+
name: str,
|
277
|
+
*,
|
278
|
+
version: Literal["1.0.0", "2.0.0"] | None = None,
|
279
|
+
enabled_rules: Sequence[str] | None = None,
|
280
|
+
enforce_enabled_rules: bool | None = None,
|
281
|
+
fpe_vault_secret_id: str | None = None,
|
282
|
+
redactions: Mapping[str, m.Redaction] | None = None,
|
283
|
+
rules: Mapping[str, m.RuleV1 | m.RuleV2] | None = None,
|
284
|
+
rulesets: Mapping[str, m.RulesetV1 | m.RulesetV2] | None = None,
|
285
|
+
salt_vault_secret_id: str | None = None,
|
286
|
+
supported_languages: Sequence[Literal["en"]] | None = None,
|
287
|
+
vault_service_config_id: str | None = None,
|
288
|
+
) -> PangeaResponse[m.ServiceConfigResult]:
|
289
|
+
"""
|
290
|
+
Create a service config.
|
291
|
+
|
292
|
+
OperationId: redact_post_v1beta_config_create
|
293
|
+
|
294
|
+
Args:
|
295
|
+
enforce_enabled_rules: Always run service config enabled rules across all redact calls regardless of flags?
|
296
|
+
fpe_vault_secret_id: The ID of the key used by FF3 Encryption algorithms for FPE.
|
297
|
+
salt_vault_secret_id: Pangea only allows hashing to be done using a salt value to prevent brute-force attacks.
|
298
|
+
vault_service_config_id: Service config used to create the secret
|
299
|
+
"""
|
300
|
+
|
301
|
+
response = await self.request.post(
|
302
|
+
"v1beta/config/create",
|
303
|
+
PangeaResponseResult,
|
304
|
+
data={
|
305
|
+
"name": name,
|
306
|
+
"version": version,
|
307
|
+
"enabled_rules": enabled_rules,
|
308
|
+
"enforce_enabled_rules": enforce_enabled_rules,
|
309
|
+
"fpe_vault_secret_id": fpe_vault_secret_id,
|
310
|
+
"redactions": redactions,
|
311
|
+
"rules": rules,
|
312
|
+
"rulesets": rulesets,
|
313
|
+
"salt_vault_secret_id": salt_vault_secret_id,
|
314
|
+
"supported_languages": supported_languages,
|
315
|
+
"vault_service_config_id": vault_service_config_id,
|
316
|
+
},
|
317
|
+
)
|
318
|
+
response.result = TypeAdapter(m.ServiceConfigResult).validate_python(response.json["result"])
|
319
|
+
return cast(PangeaResponse[m.ServiceConfigResult], response)
|
320
|
+
|
321
|
+
@overload
|
322
|
+
async def update_service_config(
|
323
|
+
self,
|
324
|
+
config_id: str,
|
325
|
+
*,
|
326
|
+
version: Literal["1.0.0"],
|
327
|
+
name: str,
|
328
|
+
updated_at: str,
|
329
|
+
enabled_rules: Sequence[str] | None = None,
|
330
|
+
redactions: Mapping[str, m.Redaction] | None = None,
|
331
|
+
vault_service_config_id: str | None = None,
|
332
|
+
salt_vault_secret_id: str | None = None,
|
333
|
+
rules: Mapping[str, m.RuleV1] | None = None,
|
334
|
+
rulesets: Mapping[str, m.RulesetV1] | None = None,
|
335
|
+
supported_languages: Sequence[Literal["en"]] | None = None,
|
336
|
+
) -> PangeaResponse[m.ServiceConfigResult]:
|
337
|
+
"""
|
338
|
+
Update a v1.0.0 service config.
|
339
|
+
|
340
|
+
OperationId: redact_post_v1beta_config_update
|
341
|
+
|
342
|
+
Args:
|
343
|
+
vault_service_config_id: Service config used to create the secret
|
344
|
+
salt_vault_secret_id: Pangea only allows hashing to be done using a salt value to prevent brute-force attacks.
|
345
|
+
"""
|
346
|
+
|
347
|
+
@overload
|
348
|
+
async def update_service_config(
|
349
|
+
self,
|
350
|
+
config_id: str,
|
351
|
+
*,
|
352
|
+
version: Literal["2.0.0"] | None = None,
|
353
|
+
name: str,
|
354
|
+
updated_at: str,
|
355
|
+
enabled_rules: Sequence[str] | None = None,
|
356
|
+
enforce_enabled_rules: bool | None = None,
|
357
|
+
redactions: Mapping[str, m.Redaction] | None = None,
|
358
|
+
vault_service_config_id: str | None = None,
|
359
|
+
salt_vault_secret_id: str | None = None,
|
360
|
+
fpe_vault_secret_id: str | None = None,
|
361
|
+
rules: Mapping[str, m.RuleV2] | None = None,
|
362
|
+
rulesets: Mapping[str, m.RulesetV2] | None = None,
|
363
|
+
supported_languages: Sequence[Literal["en"]] | None = None,
|
364
|
+
) -> PangeaResponse[m.ServiceConfigResult]:
|
365
|
+
"""
|
366
|
+
Update a v2.0.0 service config.
|
367
|
+
|
368
|
+
OperationId: redact_post_v1beta_config_update
|
369
|
+
|
370
|
+
Args:
|
371
|
+
enforce_enabled_rules: Always run service config enabled rules across all redact calls regardless of flags?
|
372
|
+
vault_service_config_id: Service config used to create the secret
|
373
|
+
salt_vault_secret_id: Pangea only allows hashing to be done using a salt value to prevent brute-force attacks.
|
374
|
+
fpe_vault_secret_id: The ID of the key used by FF3 Encryption algorithms for FPE.
|
375
|
+
"""
|
376
|
+
|
377
|
+
async def update_service_config(
|
378
|
+
self,
|
379
|
+
config_id: str,
|
380
|
+
*,
|
381
|
+
version: Literal["1.0.0", "2.0.0"] | None = None,
|
382
|
+
name: str,
|
383
|
+
updated_at: str,
|
384
|
+
enabled_rules: Sequence[str] | None = None,
|
385
|
+
enforce_enabled_rules: bool | None = None,
|
386
|
+
fpe_vault_secret_id: str | None = None,
|
387
|
+
redactions: Mapping[str, m.Redaction] | None = None,
|
388
|
+
rules: Mapping[str, m.RuleV1 | m.RuleV2] | None = None,
|
389
|
+
rulesets: Mapping[str, m.RulesetV1 | m.RulesetV2] | None = None,
|
390
|
+
salt_vault_secret_id: str | None = None,
|
391
|
+
supported_languages: Sequence[Literal["en"]] | None = None,
|
392
|
+
vault_service_config_id: str | None = None,
|
393
|
+
) -> PangeaResponse[m.ServiceConfigResult]:
|
394
|
+
"""
|
395
|
+
Update a service config.
|
396
|
+
|
397
|
+
OperationId: redact_post_v1beta_config_update
|
398
|
+
|
399
|
+
Args:
|
400
|
+
enforce_enabled_rules: Always run service config enabled rules across all redact calls regardless of flags?
|
401
|
+
fpe_vault_secret_id: The ID of the key used by FF3 Encryption algorithms for FPE.
|
402
|
+
salt_vault_secret_id: Pangea only allows hashing to be done using a salt value to prevent brute-force attacks.
|
403
|
+
vault_service_config_id: Service config used to create the secret
|
404
|
+
"""
|
405
|
+
|
406
|
+
response = await self.request.post(
|
407
|
+
"v1beta/config/update",
|
408
|
+
PangeaResponseResult,
|
409
|
+
data={
|
410
|
+
"id": config_id,
|
411
|
+
"updated_at": updated_at,
|
412
|
+
"name": name,
|
413
|
+
"version": version,
|
414
|
+
"enabled_rules": enabled_rules,
|
415
|
+
"enforce_enabled_rules": enforce_enabled_rules,
|
416
|
+
"fpe_vault_secret_id": fpe_vault_secret_id,
|
417
|
+
"redactions": redactions,
|
418
|
+
"rules": rules,
|
419
|
+
"rulesets": rulesets,
|
420
|
+
"salt_vault_secret_id": salt_vault_secret_id,
|
421
|
+
"supported_languages": supported_languages,
|
422
|
+
"vault_service_config_id": vault_service_config_id,
|
423
|
+
},
|
424
|
+
)
|
425
|
+
response.result = TypeAdapter(m.ServiceConfigResult).validate_python(response.json["result"])
|
426
|
+
return cast(PangeaResponse[m.ServiceConfigResult], response)
|
427
|
+
|
428
|
+
async def delete_service_config(self, config_id: str) -> PangeaResponse[m.ServiceConfigResult]:
|
429
|
+
"""
|
430
|
+
Delete a service config.
|
431
|
+
|
432
|
+
OperationId: redact_post_v1beta_config_delete
|
433
|
+
|
434
|
+
Args:
|
435
|
+
config_id: An ID for a service config
|
436
|
+
"""
|
437
|
+
|
438
|
+
response = await self.request.post("v1beta/config/delete", PangeaResponseResult, data={"id": config_id})
|
439
|
+
response.result = TypeAdapter(m.ServiceConfigResult).validate_python(response.json["result"])
|
440
|
+
return cast(PangeaResponse[m.ServiceConfigResult], response)
|
441
|
+
|
442
|
+
async def list_service_configs(
|
443
|
+
self,
|
444
|
+
*,
|
445
|
+
filter: m.ServiceConfigFilter | None = None,
|
446
|
+
last: str | None = None,
|
447
|
+
order: Literal["asc", "desc"] | None = None,
|
448
|
+
order_by: Literal["id", "created_at", "updated_at"] | None = None,
|
449
|
+
size: int | None = None,
|
450
|
+
) -> PangeaResponse[m.ServiceConfigListResult]:
|
451
|
+
"""
|
452
|
+
List service configs.
|
453
|
+
|
454
|
+
OperationId: redact_post_v1beta_config_list
|
455
|
+
|
456
|
+
Args:
|
457
|
+
last: Reflected value from a previous response to obtain the next page of results.
|
458
|
+
order: Order results asc(ending) or desc(ending).
|
459
|
+
order_by: Which field to order results by.
|
460
|
+
size: Maximum results to include in the response.
|
461
|
+
"""
|
462
|
+
|
463
|
+
return await self.request.post(
|
464
|
+
"v1beta/config/list",
|
465
|
+
m.ServiceConfigListResult,
|
466
|
+
data={"filter": filter, "last": last, "order": order, "order_by": order_by, "size": size},
|
467
|
+
)
|
pangea/request.py
CHANGED
@@ -11,14 +11,14 @@ import json
|
|
11
11
|
import logging
|
12
12
|
import time
|
13
13
|
from collections.abc import Iterable, Mapping
|
14
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union, cast
|
14
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union, cast, overload
|
15
15
|
|
16
16
|
import requests
|
17
|
-
from pydantic import BaseModel
|
17
|
+
from pydantic import BaseModel, TypeAdapter
|
18
18
|
from pydantic_core import to_jsonable_python
|
19
19
|
from requests.adapters import HTTPAdapter, Retry
|
20
20
|
from requests_toolbelt import MultipartDecoder # type: ignore[import-untyped]
|
21
|
-
from typing_extensions import TypeAlias, TypeVar, override
|
21
|
+
from typing_extensions import Literal, TypeAlias, TypeVar, override
|
22
22
|
from yarl import URL
|
23
23
|
|
24
24
|
import pangea
|
@@ -220,6 +220,24 @@ class PangeaRequest(PangeaRequestBase):
|
|
220
220
|
def __del__(self) -> None:
|
221
221
|
self.session.close()
|
222
222
|
|
223
|
+
def delete(self, endpoint: str) -> None:
|
224
|
+
"""
|
225
|
+
Makes a DELETE call to a Pangea endpoint.
|
226
|
+
|
227
|
+
Args:
|
228
|
+
endpoint: The Pangea API endpoint.
|
229
|
+
"""
|
230
|
+
|
231
|
+
url = self._url(endpoint)
|
232
|
+
|
233
|
+
self.logger.debug(
|
234
|
+
json.dumps({"service": self.service, "action": "delete", "url": url}, default=default_encoder)
|
235
|
+
)
|
236
|
+
|
237
|
+
requests_response = self._http_delete(url, headers=self._headers())
|
238
|
+
self._check_http_errors(requests_response)
|
239
|
+
|
240
|
+
@overload
|
223
241
|
def post(
|
224
242
|
self,
|
225
243
|
endpoint: str,
|
@@ -228,18 +246,62 @@ class PangeaRequest(PangeaRequestBase):
|
|
228
246
|
files: Optional[list[Tuple]] = None,
|
229
247
|
poll_result: bool = True,
|
230
248
|
url: Optional[str] = None,
|
249
|
+
*,
|
250
|
+
pangea_response: Literal[True] = True,
|
231
251
|
) -> PangeaResponse[TResult]:
|
232
|
-
"""
|
252
|
+
"""
|
253
|
+
Makes the POST call to a Pangea Service endpoint.
|
233
254
|
|
234
255
|
Args:
|
235
|
-
endpoint
|
236
|
-
data
|
256
|
+
endpoint: The Pangea Service API endpoint.
|
257
|
+
data: The POST body payload object
|
237
258
|
|
238
259
|
Returns:
|
239
260
|
PangeaResponse which contains the response in its entirety and
|
240
261
|
various properties to retrieve individual fields
|
241
262
|
"""
|
242
263
|
|
264
|
+
@overload
|
265
|
+
def post(
|
266
|
+
self,
|
267
|
+
endpoint: str,
|
268
|
+
result_class: Type[TResult],
|
269
|
+
data: str | BaseModel | Mapping[str, Any] | None = None,
|
270
|
+
files: Optional[list[Tuple]] = None,
|
271
|
+
poll_result: bool = True,
|
272
|
+
url: Optional[str] = None,
|
273
|
+
*,
|
274
|
+
pangea_response: Literal[False],
|
275
|
+
) -> TResult:
|
276
|
+
"""
|
277
|
+
Makes the POST call to a Pangea Service endpoint.
|
278
|
+
|
279
|
+
Args:
|
280
|
+
endpoint: The Pangea Service API endpoint.
|
281
|
+
data: The POST body payload object
|
282
|
+
"""
|
283
|
+
|
284
|
+
def post(
|
285
|
+
self,
|
286
|
+
endpoint: str,
|
287
|
+
result_class: Type[TResult],
|
288
|
+
data: str | BaseModel | Mapping[str, Any] | None = None,
|
289
|
+
files: Optional[list[Tuple]] = None,
|
290
|
+
poll_result: bool = True,
|
291
|
+
url: Optional[str] = None,
|
292
|
+
*,
|
293
|
+
pangea_response: bool = True,
|
294
|
+
) -> PangeaResponse[TResult] | TResult:
|
295
|
+
"""
|
296
|
+
Makes a POST call to a Pangea Service endpoint.
|
297
|
+
|
298
|
+
Args:
|
299
|
+
endpoint: The Pangea Service API endpoint.
|
300
|
+
data: The POST body payload object
|
301
|
+
pangea_response: Whether or not the response body follows Pangea's
|
302
|
+
standard response schema
|
303
|
+
"""
|
304
|
+
|
243
305
|
if isinstance(data, BaseModel):
|
244
306
|
data = data.model_dump(exclude_none=True)
|
245
307
|
|
@@ -277,9 +339,13 @@ class PangeaRequest(PangeaRequestBase):
|
|
277
339
|
|
278
340
|
self._check_http_errors(requests_response)
|
279
341
|
|
342
|
+
if not pangea_response:
|
343
|
+
type_adapter = TypeAdapter(result_class)
|
344
|
+
return type_adapter.validate_python(requests_response.json())
|
345
|
+
|
280
346
|
if "multipart/form-data" in requests_response.headers.get("content-type", ""):
|
281
347
|
multipart_response = self._process_multipart_response(requests_response)
|
282
|
-
|
348
|
+
pangea_response_obj: PangeaResponse = PangeaResponse(
|
283
349
|
requests_response,
|
284
350
|
result_class=result_class,
|
285
351
|
json=multipart_response.pangea_json,
|
@@ -292,14 +358,14 @@ class PangeaRequest(PangeaRequestBase):
|
|
292
358
|
json.dumps({"service": self.service, "action": "post", "url": url, "response": json_resp})
|
293
359
|
)
|
294
360
|
|
295
|
-
|
361
|
+
pangea_response_obj = PangeaResponse(requests_response, result_class=result_class, json=json_resp)
|
296
362
|
except requests.exceptions.JSONDecodeError as e:
|
297
363
|
raise pe.PangeaException(f"Failed to decode json response. {e}. Body: {requests_response.text}") from e
|
298
364
|
|
299
365
|
if poll_result:
|
300
|
-
|
366
|
+
pangea_response_obj = self._handle_queued_result(pangea_response_obj)
|
301
367
|
|
302
|
-
return self._check_response(
|
368
|
+
return self._check_response(pangea_response_obj)
|
303
369
|
|
304
370
|
def _get_pangea_json(self, decoder: MultipartDecoder) -> Optional[Dict]:
|
305
371
|
# Iterate through parts
|
@@ -342,6 +408,14 @@ class PangeaRequest(PangeaRequestBase):
|
|
342
408
|
if resp.status_code == 503:
|
343
409
|
raise pe.ServiceTemporarilyUnavailable(resp.json())
|
344
410
|
|
411
|
+
def _http_delete(
|
412
|
+
self,
|
413
|
+
url: str,
|
414
|
+
*,
|
415
|
+
headers: Mapping[str, str | bytes | None] = {},
|
416
|
+
) -> requests.Response:
|
417
|
+
return self.session.delete(url, headers=headers)
|
418
|
+
|
345
419
|
def _http_post(
|
346
420
|
self,
|
347
421
|
url: str,
|
@@ -386,37 +460,98 @@ class PangeaRequest(PangeaRequestBase):
|
|
386
460
|
|
387
461
|
return response
|
388
462
|
|
389
|
-
|
390
|
-
|
463
|
+
@overload
|
464
|
+
def get(
|
465
|
+
self,
|
466
|
+
path: str,
|
467
|
+
result_class: Type[TResult],
|
468
|
+
check_response: bool = True,
|
469
|
+
*,
|
470
|
+
params: (
|
471
|
+
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
472
|
+
| None
|
473
|
+
) = None,
|
474
|
+
pangea_response: Literal[True] = True,
|
475
|
+
) -> PangeaResponse[TResult]:
|
476
|
+
"""
|
477
|
+
Makes the GET call to a Pangea Service endpoint.
|
391
478
|
|
392
479
|
Args:
|
393
|
-
|
394
|
-
|
480
|
+
path: Additional URL path
|
481
|
+
params: Dictionary of querystring data to attach to the request
|
395
482
|
|
396
483
|
Returns:
|
397
484
|
PangeaResponse which contains the response in its entirety and
|
398
485
|
various properties to retrieve individual fields
|
399
486
|
"""
|
400
487
|
|
488
|
+
@overload
|
489
|
+
def get(
|
490
|
+
self,
|
491
|
+
path: str,
|
492
|
+
result_class: Type[TResult],
|
493
|
+
check_response: bool = True,
|
494
|
+
*,
|
495
|
+
params: (
|
496
|
+
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
497
|
+
| None
|
498
|
+
) = None,
|
499
|
+
pangea_response: Literal[False] = False,
|
500
|
+
) -> TResult:
|
501
|
+
"""
|
502
|
+
Makes the GET call to a Pangea Service endpoint.
|
503
|
+
|
504
|
+
Args:
|
505
|
+
path: Additional URL path
|
506
|
+
params: Dictionary of querystring data to attach to the request
|
507
|
+
"""
|
508
|
+
|
509
|
+
def get(
|
510
|
+
self,
|
511
|
+
path: str,
|
512
|
+
result_class: Type[TResult],
|
513
|
+
check_response: bool = True,
|
514
|
+
*,
|
515
|
+
params: (
|
516
|
+
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
517
|
+
| None
|
518
|
+
) = None,
|
519
|
+
pangea_response: bool = True,
|
520
|
+
) -> PangeaResponse[TResult] | TResult:
|
521
|
+
"""
|
522
|
+
Makes the GET call to a Pangea Service endpoint.
|
523
|
+
|
524
|
+
Args:
|
525
|
+
path: Additional URL path
|
526
|
+
params: Dictionary of querystring data to attach to the request
|
527
|
+
pangea_response: Whether or not the response body follows Pangea's
|
528
|
+
standard response schema
|
529
|
+
"""
|
530
|
+
|
401
531
|
url = self._url(path)
|
402
532
|
self.logger.debug(json.dumps({"service": self.service, "action": "get", "url": url}))
|
403
|
-
requests_response = self.session.get(url, headers=self._headers())
|
533
|
+
requests_response = self.session.get(url, params=params, headers=self._headers())
|
404
534
|
self._check_http_errors(requests_response)
|
405
|
-
|
535
|
+
|
536
|
+
if not pangea_response:
|
537
|
+
type_adapter = TypeAdapter(result_class)
|
538
|
+
return type_adapter.validate_python(requests_response.json())
|
539
|
+
|
540
|
+
pangea_response_obj: PangeaResponse = PangeaResponse(
|
406
541
|
requests_response, result_class=result_class, json=requests_response.json()
|
407
542
|
)
|
408
543
|
|
409
544
|
self.logger.debug(
|
410
545
|
json.dumps(
|
411
|
-
{"service": self.service, "action": "get", "url": url, "response":
|
546
|
+
{"service": self.service, "action": "get", "url": url, "response": pangea_response_obj.json},
|
412
547
|
default=default_encoder,
|
413
548
|
)
|
414
549
|
)
|
415
550
|
|
416
551
|
if check_response is False:
|
417
|
-
return
|
552
|
+
return pangea_response_obj
|
418
553
|
|
419
|
-
return self._check_response(
|
554
|
+
return self._check_response(pangea_response_obj)
|
420
555
|
|
421
556
|
def download_file(self, url: str, filename: str | None = None) -> AttachedFile:
|
422
557
|
"""
|
pangea/services/__init__.py
CHANGED
@@ -7,6 +7,7 @@ from .authz import AuthZ
|
|
7
7
|
from .embargo import Embargo
|
8
8
|
from .file_scan import FileScan
|
9
9
|
from .intel import DomainIntel, FileIntel, IpIntel, UrlIntel, UserIntel
|
10
|
+
from .management import Management
|
10
11
|
from .prompt_guard import PromptGuard
|
11
12
|
from .redact import Redact
|
12
13
|
from .sanitize import Sanitize
|