feat: complete catalog sensitivity enhancements
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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."],
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -56,6 +56,7 @@ export type EvidenceSource =
|
||||
};
|
||||
|
||||
export interface WorkspaceEvidence {
|
||||
schema_version: 1 | 2;
|
||||
source: EvidenceSource;
|
||||
policy: EvidencePolicy;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user