Files
ThothII/backend/src/routes/catalog-description-consolidation.ts
T
Codex cffa60772e
Publish documentation / publish (push) Successful in 2m12s
feat: complete catalog-driven preprocessing
2026-09-06 17:49:35 +02:00

76 lines
2.7 KiB
TypeScript

import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { z } from "zod";
import { isPrincipalContext, requirePermission } from "../auth/authorization.js";
import type { CatalogOperationCoordinator } from "../catalog/operation-coordinator.js";
import {
CatalogOperationInProgressError,
CatalogUnavailableError,
type CatalogRepository,
} from "../catalog/types.js";
const idSchema = z.uuid();
const consolidationSchema = z.discriminatedUnion("target", [
z.object({
target: z.enum(["tables", "columns"]),
targetIds: z.array(idSchema).min(1).max(10_000),
}).strict(),
z.object({ target: z.enum(["database", "database_columns"]) }).strict(),
]);
function manage(request: FastifyRequest, reply: FastifyReply) {
return isPrincipalContext(requirePermission(request, reply, "database.manage"));
}
function safeError(reply: FastifyReply, error: unknown) {
if (error instanceof CatalogUnavailableError) {
return reply.code(503).send({ code: "catalog_unavailable", message: "Database catalog is unavailable." });
}
if (error instanceof CatalogOperationInProgressError) {
return reply.code(409).send({
code: "database_operation_in_progress",
message: "A database operation is already in progress.",
});
}
if (error instanceof z.ZodError) {
return reply.code(400).send({
code: "description_consolidation_invalid",
message: "Description consolidation request is invalid.",
});
}
return reply.code(500).send({
code: "description_consolidation_failed",
message: "Description consolidation failed.",
});
}
export function catalogDescriptionConsolidationRoutes(
app: FastifyInstance,
deps: { repository: CatalogRepository; operations: CatalogOperationCoordinator },
): void {
app.post("/catalog/databases/:databaseId/descriptions/consolidate", async (request, reply) => {
if (!manage(request, reply)) return reply;
try {
const databaseId = idSchema.parse((request.params as { databaseId?: unknown }).databaseId);
const input = consolidationSchema.parse(request.body);
const targetIds = "targetIds" in input ? [...new Set(input.targetIds)] : [];
const result = await deps.operations.run(
databaseId,
async () => await deps.repository.consolidateGeneratedDescriptions(
databaseId,
input.target,
targetIds,
),
);
if (!result) {
return reply.code(404).send({
code: "catalog_target_not_found",
message: "The database or one or more selected catalog targets were not found.",
});
}
return result;
} catch (error) {
return safeError(reply, error);
}
});
}