feat: complete catalog sensitivity enhancements

This commit is contained in:
Codex
2026-09-04 15:11:18 +02:00
parent b891246664
commit 7b1d69a65b
72 changed files with 33866 additions and 358 deletions
+36 -9
View File
@@ -68,6 +68,17 @@ export interface CatalogDatabase {
secrets: Record<CatalogSecretName, boolean>;
}
export type CatalogDatabaseTestResult = Omit<
CatalogDatabase,
| "workspaceName"
| "workspaceDescription"
| "workspaceAvailable"
| "workspaceRevision"
| "workspaceEvidence"
| "runtimeBinding"
| "activeSyncRun"
>;
export interface CatalogMetrics {
scope: "global" | "database";
databaseId: string | null;
@@ -119,6 +130,7 @@ export interface CatalogColumn {
description: string | null;
generatedDescription: string | null;
sensitive: boolean;
sensitivityReason?: string | null;
lastSyncedDatabaseVersion: number | null;
lastSyncedAt: string | null;
version: number;
@@ -235,7 +247,7 @@ export interface CatalogMetadataDeleteCounts {
relationships: number;
}
export type CatalogDescriptionTarget = "tables" | "columns";
export type CatalogDescriptionTarget = "tables" | "columns" | "database_columns";
export interface CatalogDescriptionConsolidationCounts {
copied: number;
@@ -421,7 +433,7 @@ export const replaceCatalogDatabaseSecrets = (
});
export const testCatalogDatabase = (id: string, version: number) =>
apiFetch<CatalogDatabase>(`/catalog/databases/${encodeURIComponent(id)}/test`, {
apiFetch<CatalogDatabaseTestResult>(`/catalog/databases/${encodeURIComponent(id)}/test`, {
method: "POST",
body: JSON.stringify({ version }),
});
@@ -465,9 +477,10 @@ export const updateCatalogColumnSensitive = (
columnId: string,
version: number,
sensitive: boolean,
sensitivityReason?: string | null,
) => apiFetch<CatalogColumn>(
`/catalog/databases/${encodeURIComponent(databaseId)}/tables/${encodeURIComponent(tableId)}/columns/${encodeURIComponent(columnId)}`,
{ method: "PATCH", body: JSON.stringify({ version, sensitive }) },
{ method: "PATCH", body: JSON.stringify({ version, sensitive, sensitivityReason }) },
);
export const runSensitivityAnalysis = (
@@ -544,14 +557,28 @@ export const deleteCatalogTableMetadata = (
{ method: "POST", body: JSON.stringify({ tableIds, target }) },
);
export const consolidateCatalogDescriptions = (
export function consolidateCatalogDescriptions(
databaseId: string,
target: "database_columns",
): Promise<CatalogDescriptionConsolidationCounts>;
export function consolidateCatalogDescriptions(
databaseId: string,
target: Exclude<CatalogDescriptionTarget, "database_columns">,
targetIds: string[],
): Promise<CatalogDescriptionConsolidationCounts>;
export function consolidateCatalogDescriptions(
databaseId: string,
target: CatalogDescriptionTarget,
targetIds: string[],
) => apiFetch<CatalogDescriptionConsolidationCounts>(
`/catalog/databases/${encodeURIComponent(databaseId)}/descriptions/consolidate`,
{ method: "POST", body: JSON.stringify({ target, targetIds }) },
);
targetIds?: string[],
): Promise<CatalogDescriptionConsolidationCounts> {
return apiFetch<CatalogDescriptionConsolidationCounts>(
`/catalog/databases/${encodeURIComponent(databaseId)}/descriptions/consolidate`,
{
method: "POST",
body: JSON.stringify(target === "database_columns" ? { target } : { target, targetIds }),
},
);
}
export const startCatalogSync = (
databaseId: string,
+1
View File
@@ -153,6 +153,7 @@ test.each([
["sensitive_data_suggestion_history_request_invalid", "Sensitivity analysis history parameters are invalid."],
["sensitive_data_suggestion_history_failed", "Sensitivity analysis history could not be loaded."],
["sensitive_data_suggestion_run_not_found", "The sensitivity analysis run was not found."],
["schema_introspection_failed", "The database schema could not be read. Check the connection and credentials, then try again."],
["relationship_not_found", "The relationship no longer exists. Refresh and try again."],
["relationship_duplicate", "This relationship already exists."],
["relationship_target_not_unique", "The target column must be the only primary-key column of its table."],
+1 -1
View File
@@ -101,7 +101,7 @@ const localCodeMessages: Record<string, string> = {
sensitive_data_suggestion_history_failed: "Sensitivity analysis history could not be loaded.",
sensitive_data_suggestion_run_not_found: "The sensitivity analysis run was not found.",
schema_sync_conflict: "A schema synchronization is already active or no longer current.",
schema_introspection_failed: "The database schema could not be read safely.",
schema_introspection_failed: "The database schema could not be read. Check the connection and credentials, then try again.",
schema_request_invalid: "The schema request is invalid.",
schema_operation_failed: "The schema operation failed.",
sync_run_not_found: "The synchronization run was not found.",
+42 -7
View File
@@ -70,17 +70,52 @@ test("accepts the evidence schema version materialized by the backend", async ()
})));
await expect(getWorkspace("psd-clinical")).resolves.toEqual({
workspace: {
...persistedWorkspace,
evidence: {
source: persistedWorkspace.evidence.source,
policy: persistedWorkspace.evidence.policy,
},
},
workspace: persistedWorkspace,
revision,
});
});
test("validates a workspace read from the backend without losing its Evidence schema version", async () => {
const persistedWorkspace = {
...workspace,
evidence: {
schema_version: 1,
source: {
type: "filesystem",
uri: "psd-clinical/evidence",
patterns: ["curated/**/*.md"],
max_bytes: 10 * 1024 * 1024,
},
policy: { max_chunk_chars: 5000, retain_published_generations: 3 },
},
};
let validationRequest: unknown;
server.use(
http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
workspace: persistedWorkspace,
revision,
})),
http.post("/api/workspaces/validate", async ({ request }) => {
validationRequest = await request.json();
return HttpResponse.json({
workspace: persistedWorkspace,
contract: {},
activatable: true,
diagnostics: [],
authentication: {
ready: true,
mode: "local",
checks: [{ level: "info", code: "auth_ready", message: "Authentication is ready." }],
},
});
}),
);
const loaded = await getWorkspace("psd-clinical");
await expect(validateWorkspace(loaded.workspace)).resolves.toMatchObject({ activatable: true });
expect(validationRequest).toEqual({ workspace: persistedWorkspace });
});
test("decodes runtime requirements but rejects any secret value returned by the server", async () => {
server.use(http.get(
"/api/workspaces/psd-clinical/runtime-configuration",
+1
View File
@@ -56,6 +56,7 @@ export type EvidenceSource =
};
export interface WorkspaceEvidence {
schema_version: 1 | 2;
source: EvidenceSource;
policy: EvidencePolicy;
}