drizzle-cube
Version:
Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.
213 lines (212 loc) • 6.36 kB
JavaScript
import j, { Router as E } from "express";
import R from "cors";
import { S, d as s, f as x, h as $, a as L, b as Q, c as g } from "../compiler-Duyh6Jp-.js";
function N(f) {
const {
cubes: d,
drizzle: m,
schema: q,
extractSecurityContext: y,
engineType: v,
cors: b,
basePath: c = "/cubejs-api/v1",
jsonLimit: h = "10mb",
cache: w
} = f;
if (!d || d.length === 0)
throw new Error("At least one cube must be provided in the cubes array");
const u = E();
b && u.use(R(b)), u.use(j.json({ limit: h })), u.use(j.urlencoded({ extended: !0, limit: h }));
const a = new S({
drizzle: m,
schema: q,
engineType: v,
cache: w
});
return d.forEach((t) => {
a.registerCube(t);
}), u.post(`${c}/load`, async (t, r) => {
try {
const e = t.body.query || t.body, o = await y(t, r), n = a.validateQuery(e);
if (!n.isValid)
return r.status(400).json(s(
`Query validation failed: ${n.errors.join(", ")}`,
400
));
const i = await a.executeMultiCubeQuery(e, o);
r.json(x(e, i, a));
} catch (e) {
console.error("Query execution error:", e), r.status(500).json(s(
e instanceof Error ? e.message : "Query execution failed",
500
));
}
}), u.get(`${c}/load`, async (t, r) => {
try {
const e = t.query.query;
if (!e)
return r.status(400).json(s(
"Query parameter is required",
400
));
let o;
try {
o = JSON.parse(e);
} catch {
return r.status(400).json(s(
"Invalid JSON in query parameter",
400
));
}
const n = await y(t, r), i = a.validateQuery(o);
if (!i.isValid)
return r.status(400).json(s(
`Query validation failed: ${i.errors.join(", ")}`,
400
));
const l = await a.executeMultiCubeQuery(o, n);
r.json(x(o, l, a));
} catch (e) {
console.error("Query execution error:", e), r.status(500).json(s(
e instanceof Error ? e.message : "Query execution failed",
500
));
}
}), u.post(`${c}/batch`, async (t, r) => {
try {
const { queries: e } = t.body;
if (!e || !Array.isArray(e))
return r.status(400).json(s(
'Request body must contain a "queries" array',
400
));
if (e.length === 0)
return r.status(400).json(s(
"Queries array cannot be empty",
400
));
const o = await y(t, r), n = await $(e, o, a);
r.json(n);
} catch (e) {
console.error("Batch execution error:", e), r.status(500).json(s(
e instanceof Error ? e.message : "Batch execution failed",
500
));
}
}), u.get(`${c}/meta`, (t, r) => {
try {
const e = a.getMetadata();
r.json(L(e));
} catch (e) {
console.error("Metadata error:", e), r.status(500).json(s(
e instanceof Error ? e.message : "Failed to fetch metadata",
500
));
}
}), u.post(`${c}/sql`, async (t, r) => {
try {
const e = t.body, o = await y(t, r), n = a.validateQuery(e);
if (!n.isValid)
return r.status(400).json(s(
`Query validation failed: ${n.errors.join(", ")}`,
400
));
const i = e.measures?.[0] || e.dimensions?.[0];
if (!i)
return r.status(400).json(s(
"No measures or dimensions specified",
400
));
const l = i.split(".")[0], p = await a.generateSQL(l, e, o);
r.json(Q(e, p));
} catch (e) {
console.error("SQL generation error:", e), r.status(500).json(s(
e instanceof Error ? e.message : "SQL generation failed",
500
));
}
}), u.get(`${c}/sql`, async (t, r) => {
try {
const e = t.query.query;
if (!e)
return r.status(400).json(s(
"Query parameter is required",
400
));
const o = JSON.parse(e), n = await y(t, r), i = a.validateQuery(o);
if (!i.isValid)
return r.status(400).json(s(
`Query validation failed: ${i.errors.join(", ")}`,
400
));
const l = o.measures?.[0] || o.dimensions?.[0];
if (!l)
return r.status(400).json(s(
"No measures or dimensions specified",
400
));
const p = l.split(".")[0], C = await a.generateSQL(p, o, n);
r.json(Q(o, C));
} catch (e) {
console.error("SQL generation error:", e), r.status(500).json(s(
e instanceof Error ? e.message : "SQL generation failed",
500
));
}
}), u.post(`${c}/dry-run`, async (t, r) => {
try {
const e = t.body.query || t.body, o = await y(t, r), n = await g(e, o, a);
r.json(n);
} catch (e) {
console.error("Dry-run error:", e), r.status(400).json({
error: e instanceof Error ? e.message : "Dry-run validation failed",
valid: !1
});
}
}), u.get(`${c}/dry-run`, async (t, r) => {
try {
const e = t.query.query;
if (!e)
return r.status(400).json({
error: "Query parameter is required",
valid: !1
});
const o = JSON.parse(e), n = await y(t, r), i = await g(o, n, a);
r.json(i);
} catch (e) {
console.error("Dry-run error:", e), r.status(400).json({
error: e instanceof Error ? e.message : "Dry-run validation failed",
valid: !1
});
}
}), u.post(`${c}/explain`, async (t, r) => {
try {
const e = t.body.query || t.body, o = t.body.options || {}, n = await y(t, r), i = a.validateQuery(e);
if (!i.isValid)
return r.status(400).json({
error: `Query validation failed: ${i.errors.join(", ")}`
});
const l = await a.explainQuery(e, n, o);
r.json(l);
} catch (e) {
console.error("Explain error:", e), r.status(500).json({
error: e instanceof Error ? e.message : "Explain query failed"
});
}
}), u.use((t, r, e, o) => {
console.error("Express adapter error:", t), e.headersSent || e.status(500).json(s(t, 500));
}), u;
}
function M(f, d) {
const m = N(d);
return f.use("/", m), f;
}
function J(f) {
const d = j();
return M(d, f);
}
export {
J as createCubeApp,
N as createCubeRouter,
M as mountCubeRoutes
};