Source code for datamasque.client.models.ifm
"""Typed request and response shapes for the IFM (in-flight masking) HTTP API."""
from datetime import datetime
from typing import Any, Callable, Optional
from pydantic import BaseModel, ConfigDict, model_validator
from datamasque.client.exceptions import DataMasqueUserError
[docs]
class RulesetPlanOptions(BaseModel):
"""
Server-defined defaults applied when a mask request omits the corresponding fields.
All keys are optional;
callers can supply any subset (or none) and the IFM server fills in remaining defaults.
"""
model_config = ConfigDict(extra="forbid")
enabled: Optional[bool] = None
# NB: Encoding and charset are not currently implemented for IFM.
# These fields are here just to ensure we can round-trip a `RulesetPlan` object.
default_encoding: Optional[str] = None
default_charset: Optional[str] = None
default_log_level: Optional[str] = None
[docs]
class IfmLog(BaseModel):
"""A single log entry produced by IFM during a mask call or a ruleset-plan validation."""
model_config = ConfigDict(extra="allow")
log_level: str
timestamp: str
message: str
[docs]
class IfmRulesetPlanRef(BaseModel):
"""Reference to a ruleset plan embedded in a mask response."""
model_config = ConfigDict(extra="allow")
name: str
serial: int
[docs]
class RulesetPlan(BaseModel):
"""
Unified model for IFM ruleset plans.
Collapses the list/detail/create/update response shapes into one model
with optional fields for parts that differ by endpoint.
"""
model_config = ConfigDict(extra="allow")
name: str
serial: int
created_time: datetime
modified_time: datetime
options: RulesetPlanOptions
ruleset_yaml: Optional[str] = None
logs: Optional[list[IfmLog]] = None
url: Optional[str] = None
[docs]
class RulesetPlanCreateRequest(BaseModel):
"""Request body for `POST /ifm/ruleset-plans/`."""
model_config = ConfigDict(extra="forbid")
name: str
ruleset_yaml: str
options: Optional[RulesetPlanOptions] = None
[docs]
class RulesetPlanUpdateRequest(BaseModel):
"""Request body for `PUT /ifm/ruleset-plans/{name}/`."""
model_config = ConfigDict(extra="forbid")
ruleset_yaml: str
options: Optional[RulesetPlanOptions] = None
[docs]
class RulesetPlanPartialUpdateRequest(BaseModel):
"""Request body for `PATCH /ifm/ruleset-plans/{name}/` — every field is optional."""
model_config = ConfigDict(extra="forbid")
ruleset_yaml: Optional[str] = None
options: Optional[RulesetPlanOptions] = None
[docs]
class IfmMaskRequest(BaseModel):
"""
Request body for `POST /ruleset-plans/{name}/mask/`.
`data` is the list of records to be masked;
every other field overrides server defaults configured on the plan.
"""
model_config = ConfigDict(extra="forbid")
data: list[Any]
disable_instance_secret: Optional[bool] = None
run_secret: Optional[str] = None
hash_values: Optional[Any] = None
log_level: Optional[str] = None
request_id: Optional[str] = None
ai_engine_url: Optional[str] = None
[docs]
class IfmMaskResult(BaseModel):
"""
Response shape for `POST /ruleset-plans/{name}/mask/`.
`success` is populated by the client based on the HTTP status the server returned:
- `True` — masking completed;
`data` carries the masked records (possibly an empty list if the request had no input).
- `False` — the server rejected the request with a soft failure
(e.g. a masking function received an unsupported value type);
`data` is omitted and details surface in `logs`.
Hard failures (plan not found, auth, transport) still raise rather than producing an `IfmMaskResult`.
"""
model_config = ConfigDict(extra="allow")
success: bool
request_id: Optional[str] = None
ruleset_plan: Optional[IfmRulesetPlanRef] = None
logs: Optional[list[IfmLog]] = None
data: Optional[list[Any]] = None
[docs]
class IfmTokenInfo(BaseModel):
"""Response body for `GET /verify-token/` — the list of scopes granted to the current JWT."""
model_config = ConfigDict(extra="allow")
scopes: list[str]
[docs]
class DataMasqueIfmInstanceConfig(BaseModel):
"""
Connection configuration for `DataMasqueIfmClient`.
`admin_server_base_url` is where JWTs are obtained and refreshed;
`ifm_base_url` is where the IFM API itself lives
(typically a separate hostname or the admin server with `/ifm` prefix).
Exactly one of `password` or `token_source` must be set.
`token_source` is a user-supplied callable that returns the bare JWT access token string —
the value issued by the admin server's `/api/auth/jwt/login/` endpoint;
the client prepends it with `Bearer ` when sending the `Authorization` header.
The client calls `token_source` on each authentication and refresh,
so the callable is free to fetch and refresh tokens out-of-band (e.g. from a secrets manager).
"""
model_config = ConfigDict(arbitrary_types_allowed=True)
admin_server_base_url: str
ifm_base_url: str
username: str
password: Optional[str] = None
verify_ssl: bool = True
token_source: Optional[Callable[[], str]] = None
@model_validator(mode="after")
def _validate_auth_source(self) -> "DataMasqueIfmInstanceConfig":
if (self.password is None) == (self.token_source is None):
raise DataMasqueUserError(
"Exactly one of `password` or `token_source` must be provided to `DataMasqueIfmInstanceConfig`."
)
return self