feat(datasources): enforce policy-aware data visibility
Module Package Release / publish-packages (push) Successful in 11s
Module Package Release / publish-packages (push) Successful in 11s
This commit is contained in:
@@ -28,6 +28,8 @@ export type DatasourceGovernance = {
|
||||
classification: string;
|
||||
privacy_profile_ref?: string | null;
|
||||
retention_policy_ref?: string | null;
|
||||
access_policy_ref?: string | null;
|
||||
visibility_policy: Record<string, unknown>;
|
||||
hold_refs: string[];
|
||||
publication_state: string;
|
||||
transfer_agreement_ref?: string | null;
|
||||
@@ -43,6 +45,7 @@ export type DatasourceField = {
|
||||
name: string;
|
||||
data_type: string;
|
||||
nullable: boolean;
|
||||
classification: string;
|
||||
};
|
||||
|
||||
export type Datasource = {
|
||||
|
||||
@@ -73,6 +73,7 @@ import {
|
||||
import {
|
||||
DATASOURCE_FIELDS_DOCUMENTATION,
|
||||
DATASOURCE_GOVERNANCE_DOCUMENTATION,
|
||||
DATASOURCE_VISIBILITY_DOCUMENTATION,
|
||||
DATASOURCES_DOCUMENTATION,
|
||||
DATASOURCES_I18N
|
||||
} from "./interfacePatterns";
|
||||
@@ -898,6 +899,7 @@ function GovernanceDialog({
|
||||
const [draft, setDraft] = useState<DatasourceGovernance | null>(null);
|
||||
const [freshness, setFreshness] = useState("{}");
|
||||
const [quality, setQuality] = useState("{}");
|
||||
const [visibility, setVisibility] = useState("{}");
|
||||
const [baselineKey, setBaselineKey] = useState("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
@@ -908,14 +910,16 @@ function GovernanceDialog({
|
||||
const nextDraft = structuredClone(datasource.governance);
|
||||
const nextFreshness = JSON.stringify(datasource.governance.freshness_policy, null, 2);
|
||||
const nextQuality = JSON.stringify(datasource.governance.quality_policy, null, 2);
|
||||
const nextVisibility = JSON.stringify(datasource.governance.visibility_policy ?? {}, null, 2);
|
||||
setDraft(nextDraft);
|
||||
setFreshness(nextFreshness);
|
||||
setQuality(nextQuality);
|
||||
setBaselineKey(JSON.stringify({ draft: nextDraft, freshness: nextFreshness, quality: nextQuality }));
|
||||
setVisibility(nextVisibility);
|
||||
setBaselineKey(JSON.stringify({ draft: nextDraft, freshness: nextFreshness, quality: nextQuality, visibility: nextVisibility }));
|
||||
setError("");
|
||||
}, [datasource, open]);
|
||||
|
||||
const dirty = Boolean(open && draft && JSON.stringify({ draft, freshness, quality }) !== baselineKey);
|
||||
const dirty = Boolean(open && draft && JSON.stringify({ draft, freshness, quality, visibility }) !== baselineKey);
|
||||
|
||||
const save = async (): Promise<boolean> => {
|
||||
if (!datasource || !draft) return false;
|
||||
@@ -925,7 +929,8 @@ function GovernanceDialog({
|
||||
const updated = await updateDatasourceGovernance(settings, datasource.ref, {
|
||||
...draft,
|
||||
freshness_policy: parseObject(freshness, "Freshness policy"),
|
||||
quality_policy: parseObject(quality, "Quality policy")
|
||||
quality_policy: parseObject(quality, "Quality policy"),
|
||||
visibility_policy: parseObject(visibility, "Visibility policy")
|
||||
});
|
||||
await onSaved(updated);
|
||||
return true;
|
||||
@@ -1020,6 +1025,9 @@ function GovernanceDialog({
|
||||
<FormField label="Retention policy" interfaceId="datasources.field.retention-policy" helpContextId="datasources.field.retention-policy" helpModuleId="datasources" documentation={DATASOURCE_GOVERNANCE_DOCUMENTATION}>
|
||||
<input value={draft.retention_policy_ref ?? ""} onChange={(event) => setValue("retention_policy_ref", event.target.value || null)} />
|
||||
</FormField>
|
||||
<FormField label="Access policy reference" interfaceId="datasources.field.access-policy" helpContextId="datasources.field.access-policy" helpModuleId="datasources" documentation={DATASOURCE_VISIBILITY_DOCUMENTATION}>
|
||||
<input value={draft.access_policy_ref ?? ""} onChange={(event) => setValue("access_policy_ref", event.target.value || null)} placeholder="Optional Policy module target" />
|
||||
</FormField>
|
||||
<FormField label="Transfer agreement" interfaceId="datasources.field.transfer-agreement" helpContextId="datasources.field.transfer-agreement" helpModuleId="datasources" documentation={DATASOURCE_GOVERNANCE_DOCUMENTATION}>
|
||||
<input value={draft.transfer_agreement_ref ?? ""} onChange={(event) => setValue("transfer_agreement_ref", event.target.value || null)} />
|
||||
</FormField>
|
||||
@@ -1040,6 +1048,10 @@ function GovernanceDialog({
|
||||
<GovernanceListField label="Known limits" values={draft.known_limits} onChange={(values) => setValue("known_limits", values)} />
|
||||
</FormGrid>
|
||||
<FormGrid columns={2} gap="small" collapseAt="narrow">
|
||||
<FormField label="Visibility policy (JSON)" interfaceId="datasources.field.visibility-policy" helpContextId="datasources.field.visibility-policy" helpModuleId="datasources" documentation={DATASOURCE_VISIBILITY_DOCUMENTATION}>
|
||||
<textarea value={visibility} onChange={(event) => setVisibility(event.target.value)} spellCheck={false} />
|
||||
<small>Configure source and materialization ACLs, field redaction or omission, and principal-bound row filters.</small>
|
||||
</FormField>
|
||||
<FormField label="Freshness policy (JSON)" documentation={DATASOURCE_GOVERNANCE_DOCUMENTATION}>
|
||||
<textarea value={freshness} onChange={(event) => setFreshness(event.target.value)} spellCheck={false} />
|
||||
</FormField>
|
||||
@@ -1463,17 +1475,18 @@ function SchemaTable({ fields }: { fields: Datasource["schema"] }) {
|
||||
<div className="datasources-table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Field</th><th>Type</th><th>Nullable</th></tr>
|
||||
<tr><th>Field</th><th>Type</th><th>Classification</th><th>Nullable</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fields.map((field) => (
|
||||
<tr key={field.name}>
|
||||
<td>{field.name}</td>
|
||||
<td>{field.data_type}</td>
|
||||
<td>{readableToken(field.classification)}</td>
|
||||
<td>{field.nullable ? "Yes" : "No"}</td>
|
||||
</tr>
|
||||
))}
|
||||
{!fields.length ? <tr><td colSpan={3}>Schema not available</td></tr> : null}
|
||||
{!fields.length ? <tr><td colSpan={4}>Schema not available</td></tr> : null}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,11 @@ export const DATASOURCE_GOVERNANCE_DOCUMENTATION = {
|
||||
documentationType: "admin"
|
||||
} satisfies DocumentationHelpReference;
|
||||
|
||||
export const DATASOURCE_VISIBILITY_DOCUMENTATION = {
|
||||
topicId: "datasources.visibility",
|
||||
documentationType: "admin"
|
||||
} satisfies DocumentationHelpReference;
|
||||
|
||||
export const DATASOURCES_I18N = {
|
||||
loading: "i18n:govoplan-datasources.loading_reason",
|
||||
working: "i18n:govoplan-datasources.working_reason",
|
||||
|
||||
@@ -63,6 +63,10 @@ const en = {
|
||||
"Semantic definition": "Semantic definition",
|
||||
"Freshness policy (JSON)": "Freshness policy (JSON)",
|
||||
"Quality policy (JSON)": "Quality policy (JSON)",
|
||||
"Access policy reference": "Access policy reference",
|
||||
"Optional Policy module target": "Optional Policy module target",
|
||||
"Visibility policy (JSON)": "Visibility policy (JSON)",
|
||||
"Configure source and materialization ACLs, field redaction or omission, and principal-bound row filters.": "Configure source and materialization ACLs, field redaction or omission, and principal-bound row filters.",
|
||||
"Quality policy": "Quality policy",
|
||||
"Policy hash": "Policy hash",
|
||||
"Schema change": "Schema change",
|
||||
@@ -136,6 +140,10 @@ const de: Record<keyof typeof en, string> = {
|
||||
"Semantic definition": "Semantische Definition",
|
||||
"Freshness policy (JSON)": "Aktualitätsrichtlinie (JSON)",
|
||||
"Quality policy (JSON)": "Qualitätsrichtlinie (JSON)",
|
||||
"Access policy reference": "Zugriffsrichtlinienreferenz",
|
||||
"Optional Policy module target": "Optionales Ziel im Richtlinienmodul",
|
||||
"Visibility policy (JSON)": "Sichtbarkeitsrichtlinie (JSON)",
|
||||
"Configure source and materialization ACLs, field redaction or omission, and principal-bound row filters.": "Konfigurieren Sie Zugriffslisten für Quellen und Materialisierungen, Feldschwärzung oder -auslassung sowie an den Akteur gebundene Zeilenfilter.",
|
||||
"Quality policy": "Qualitätsrichtlinie",
|
||||
"Policy hash": "Richtlinien-Hash",
|
||||
"Schema change": "Schemaänderung",
|
||||
|
||||
Reference in New Issue
Block a user