feat: expose tenant-safe dataset registry HTTP surface
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { assertTenantMatchesVerified } from "@/src/http/tenant-guard";
|
||||
import { siaxErrorResponse } from "@/src/http/siax-error-response";
|
||||
import { SiaxError } from "@/src/platform/errors";
|
||||
import { datasetService } from "@/src/services/datasets";
|
||||
import type { DatasetStatus, DatasetType } from "@/src/types/dataset";
|
||||
|
||||
const DATASET_TYPES = new Set<DatasetType>(["raw", "structured", "enriched", "curated", "feature", "derived"]);
|
||||
const DATASET_STATUSES = new Set<DatasetStatus>(["active", "archived", "deprecated"]);
|
||||
|
||||
function actorId(request: NextRequest): string {
|
||||
return request.headers.get("x-0pt-verified-sub") ?? "local-dev";
|
||||
}
|
||||
|
||||
function stringField(body: Record<string, unknown>, field: string): string {
|
||||
const value = body[field];
|
||||
if (typeof value !== "string" || !value.trim()) {
|
||||
throw new SiaxError("INVALID_REQUEST", "0pt.dataset", `${field} is required`);
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const raw = await request.json();
|
||||
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
||||
throw new SiaxError("INVALID_REQUEST", "0pt.dataset", "JSON object body is required");
|
||||
}
|
||||
const body = raw as Record<string, unknown>;
|
||||
const tenantId = stringField(body, "tenant_id");
|
||||
const guard = assertTenantMatchesVerified(request, tenantId);
|
||||
if (guard) return guard;
|
||||
|
||||
const datasetType = stringField(body, "dataset_type") as DatasetType;
|
||||
if (!DATASET_TYPES.has(datasetType)) {
|
||||
throw new SiaxError("INVALID_REQUEST", "0pt.dataset", "unsupported dataset_type");
|
||||
}
|
||||
const status = body.status === undefined ? undefined : String(body.status) as DatasetStatus;
|
||||
if (status !== undefined && !DATASET_STATUSES.has(status)) {
|
||||
throw new SiaxError("INVALID_REQUEST", "0pt.dataset", "unsupported status");
|
||||
}
|
||||
|
||||
const dataset = await datasetService.registerDataset({
|
||||
dataset_id: stringField(body, "dataset_id"),
|
||||
tenant_id: tenantId,
|
||||
project_id: stringField(body, "project_id"),
|
||||
name: stringField(body, "name"),
|
||||
dataset_type: datasetType,
|
||||
...(status ? { status } : {}),
|
||||
...(typeof body.description === "string" && body.description.trim()
|
||||
? { description: body.description.trim() }
|
||||
: {}),
|
||||
...(typeof body.correlation_id === "string" && body.correlation_id.trim()
|
||||
? { correlation_id: body.correlation_id.trim() }
|
||||
: {}),
|
||||
created_by: actorId(request),
|
||||
});
|
||||
return NextResponse.json(dataset, { status: 201 });
|
||||
} catch (error: unknown) {
|
||||
return siaxErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const tenantId = searchParams.get("tenant_id")?.trim();
|
||||
if (!tenantId) throw new SiaxError("INVALID_REQUEST", "0pt.dataset", "tenant_id is required");
|
||||
const guard = assertTenantMatchesVerified(request, tenantId);
|
||||
if (guard) return guard;
|
||||
|
||||
const typeRaw = searchParams.get("dataset_type") ?? undefined;
|
||||
const statusRaw = searchParams.get("status") ?? undefined;
|
||||
if (typeRaw && !DATASET_TYPES.has(typeRaw as DatasetType)) {
|
||||
throw new SiaxError("INVALID_REQUEST", "0pt.dataset", "unsupported dataset_type");
|
||||
}
|
||||
if (statusRaw && !DATASET_STATUSES.has(statusRaw as DatasetStatus)) {
|
||||
throw new SiaxError("INVALID_REQUEST", "0pt.dataset", "unsupported status");
|
||||
}
|
||||
|
||||
return NextResponse.json(await datasetService.listDatasets({
|
||||
tenant_id: tenantId,
|
||||
project_id: searchParams.get("project_id") ?? undefined,
|
||||
dataset_type: typeRaw as DatasetType | undefined,
|
||||
status: statusRaw as DatasetStatus | undefined,
|
||||
}));
|
||||
} catch (error: unknown) {
|
||||
return siaxErrorResponse(error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user