Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/pangea-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Changed

- Redact: `unredact` is now more generic.

## 6.8.0 - 2025-10-15

### Added
Expand Down
10 changes: 6 additions & 4 deletions packages/pangea-sdk/pangea/asyncio/services/redact.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import Dict, List, Optional, Union

import pangea.services.redact as m
from pangea._typing import T
from pangea.asyncio.services.base import ServiceBaseAsync
from pangea.config import PangeaConfig
from pangea.response import PangeaResponse
Expand Down Expand Up @@ -182,11 +183,11 @@ async def redact_structured(
"v1/redact_structured", m.StructuredResult, data=input.model_dump(exclude_none=True)
)

async def unredact(self, redacted_data: m.RedactedData, fpe_context: str) -> PangeaResponse[m.UnredactResult]:
async def unredact(self, redacted_data: T, fpe_context: str) -> PangeaResponse[m.UnredactResult[T]]:
"""
Unredact

Decrypt or unredact fpe redactions
Decrypt or unredact FPE redactions

OperationId: redact_post_v1_unredact

Expand All @@ -202,5 +203,6 @@ async def unredact(self, redacted_data: m.RedactedData, fpe_context: str) -> Pan
available response fields can be found in our
[API Documentation](https://pangea.cloud/docs/api/redact#unredact-post)
"""
input = m.UnredactRequest(redacted_data=redacted_data, fpe_context=fpe_context)
return await self.request.post("v1/unredact", m.UnredactResult, data=input.model_dump(exclude_none=True))
return await self.request.post(
"v1/unredact", m.UnredactResult, data={"redacted_data": redacted_data, "fpe_context": fpe_context}
)
37 changes: 11 additions & 26 deletions packages/pangea-sdk/pangea/services/redact.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import enum
from typing import Dict, List, Optional, Union

from typing_extensions import Generic

from pangea._typing import T
from pangea.config import PangeaConfig
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
from pangea.services.base import ServiceBase
Expand Down Expand Up @@ -174,28 +177,9 @@ class StructuredResult(PangeaResponseResult):
"""FPE context used to encrypt and redact data"""


class UnredactRequest(APIRequestModel):
"""
Class input to unredact data request

Arguments:
redacted_data: Data to unredact
fpe_context (base64): FPE context used to decrypt and unredact data
"""

redacted_data: RedactedData
fpe_context: str


RedactedData = Union[str, Dict]


class UnredactResult(PangeaResponseResult):
"""
Result class after an unredact request
"""

data: RedactedData
class UnredactResult(PangeaResponseResult, Generic[T]):
data: T
"""The unredacted data"""


class Redact(ServiceBase):
Expand Down Expand Up @@ -364,11 +348,11 @@ def redact_structured(
)
return self.request.post("v1/redact_structured", StructuredResult, data=input.model_dump(exclude_none=True))

def unredact(self, redacted_data: RedactedData, fpe_context: str) -> PangeaResponse[UnredactResult]:
def unredact(self, redacted_data: T, fpe_context: str) -> PangeaResponse[UnredactResult[T]]:
"""
Unredact

Decrypt or unredact fpe redactions
Decrypt or unredact FPE redactions

OperationId: redact_post_v1_unredact

Expand All @@ -384,5 +368,6 @@ def unredact(self, redacted_data: RedactedData, fpe_context: str) -> PangeaRespo
available response fields can be found in our
[API Documentation](https://pangea.cloud/docs/api/redact#unredact-post)
"""
input = UnredactRequest(redacted_data=redacted_data, fpe_context=fpe_context)
return self.request.post("v1/unredact", UnredactResult, data=input.model_dump(exclude_none=True))
return self.request.post(
"v1/unredact", UnredactResult, data={"redacted_data": redacted_data, "fpe_context": fpe_context}
)
5 changes: 5 additions & 0 deletions packages/pangea-sdk/scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ pnpm dlx start-server-and-test --expect 404 \
4010 \
"uv run pytest tests/integration2/test_prompt_guard.py"

pnpx start-server-and-test --expect 404 \
"pnpx @stoplight/prism-cli mock -d --json-schema-faker-fillProperties=false tests/testdata/specs/redact.openapi.json" \
4010 \
"uv run pytest tests/integration2/test_redact.py"

pnpm dlx start-server-and-test --expect 404 \
"pnpm dlx @stoplight/prism-cli mock -d --json-schema-faker-fillProperties=false tests/testdata/specs/share.openapi.json" \
4010 \
Expand Down
46 changes: 46 additions & 0 deletions packages/pangea-sdk/tests/integration2/test_redact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from __future__ import annotations

import os
from collections.abc import AsyncIterator, Iterator

import pytest

from pangea import PangeaConfig
from pangea.asyncio.services import RedactAsync
from pangea.services import Redact

base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")


@pytest.fixture(scope="session")
def client(request: pytest.FixtureRequest) -> Iterator[Redact]:
yield Redact(token="my_api_token", config=PangeaConfig(base_url_template=base_url))


@pytest.fixture(scope="session")
async def async_client(request: pytest.FixtureRequest) -> AsyncIterator[RedactAsync]:
async with RedactAsync(token="my_api_token", config=PangeaConfig(base_url_template=base_url)) as client:
yield client


UNREDACT_FIXTURES = [
"redacted_data",
{"redacted_data": "redacted_data"},
[0, 1, 2],
]


class TestRedact:
def test_unredact(self, client: Redact) -> None:
for redacted_data in UNREDACT_FIXTURES:
response = client.unredact(redacted_data, "fpe_context")
assert response.status == "Success"
assert response.result


class TestShareAsync:
async def test_update(self, async_client: RedactAsync) -> None:
for redacted_data in UNREDACT_FIXTURES:
response = await async_client.unredact(redacted_data, "fpe_context")
assert response.status == "Success"
assert response.result
Loading