76 lines
2.7 KiB
TypeScript
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);
|
|
}
|
|
});
|
|
}
|