feat(bridge): allow slug -> table_id resolution via BASEROW_TABLE_IDS — Patch 029
Callers can now hit /api/v1/views/table/personne instead of /table/609 and the bridge resolves the slug to the numeric Baserow table ID using the BASEROW_TABLE_IDS env var (already present, just unused before). Resolution accepts either a digits-only string (preserved behavior) or a slug. Falls back to a 400 validation error when neither matches. Note: this fixes the *routing* layer only. The downstream Baserow API still requires a user JWT for some operations (notably GET /views/table) and returns 401 PERMISSION_DENIED with the current DB-token-based bridge config. That's a separate architectural concern — tracked for a later patch (bridge user-JWT exchange). Patch 029.
This commit is contained in:
parent
3ea5f822f1
commit
30b148694c
2 changed files with 45 additions and 3 deletions
|
|
@ -39,6 +39,27 @@ const ConfigSchema = z.object({
|
|||
// global (XADD MAXLEN ~). Défaut 10 000 events. Les events plus anciens sont
|
||||
// purgés automatiquement par Redis (mode ~ = approximatif, plus performant).
|
||||
streamMaxLen: z.coerce.number().int().positive().default(10_000),
|
||||
// Optional slug -> table_id map so callers can use human-friendly slugs
|
||||
// (e.g. /api/v1/views/table/personne) instead of the numeric Baserow ID.
|
||||
// Format: JSON object string like '{"personne":609,"formation":610}'.
|
||||
baserowTableIds: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((raw) => {
|
||||
if (!raw) return {} as Record<string, number>;
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
||||
const out: Record<string, number> = {};
|
||||
for (const [k, v] of Object.entries(parsed)) {
|
||||
if (typeof v === 'number' && Number.isInteger(v) && v > 0) {
|
||||
out[k.toLowerCase()] = v;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
} catch {
|
||||
return {} as Record<string, number>;
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
export type Config = z.infer<typeof ConfigSchema>;
|
||||
|
|
@ -68,6 +89,7 @@ export function loadConfig(): Config {
|
|||
rateLimitMutationMax: process.env.RATE_LIMIT_MUTATION_MAX,
|
||||
rateLimitMutationWindow: process.env.RATE_LIMIT_MUTATION_WINDOW,
|
||||
streamMaxLen: process.env.STREAM_MAXLEN,
|
||||
baserowTableIds: process.env.BASEROW_TABLE_IDS,
|
||||
});
|
||||
|
||||
if (!parsed.success) {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,25 @@ function parseIntParam(raw: string, label: string): number {
|
|||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a table param to a numeric Baserow ID. Accepts either a digits-only
|
||||
* string (e.g. "609") or a slug (e.g. "personne") that maps via the
|
||||
* BASEROW_TABLE_IDS env (parsed in config). Throws a 400 validation error if
|
||||
* neither resolves.
|
||||
*/
|
||||
function resolveTableId(raw: string, container: ReturnType<typeof getContainer>): number {
|
||||
if (/^\d+$/.test(raw)) {
|
||||
const n = Number.parseInt(raw, 10);
|
||||
if (n > 0) return n;
|
||||
}
|
||||
const map = container.config.baserowTableIds ?? {};
|
||||
const id = map[raw.toLowerCase()];
|
||||
if (typeof id === 'number' && id > 0) return id;
|
||||
throw errors.validation([
|
||||
{ message: `tableId must be a positive integer or a known slug (got: ${raw})` },
|
||||
]);
|
||||
}
|
||||
|
||||
function parseIntQuery(url: URL, name: string, defaultVal: number, max?: number): number {
|
||||
const raw = url.searchParams.get(name);
|
||||
if (!raw) return defaultVal;
|
||||
|
|
@ -97,8 +116,9 @@ function parseIntQuery(url: URL, name: string, defaultVal: number, max?: number)
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
viewsRoutes.get('/table/:tableId', requireScope('read:tables'), async (c) => {
|
||||
const tableId = parseIntParam(c.req.param('tableId'), 'tableId');
|
||||
const { repos, redis } = getContainer();
|
||||
const container = getContainer();
|
||||
const tableId = resolveTableId(c.req.param('tableId'), container);
|
||||
const { repos, redis } = container;
|
||||
|
||||
const views = await repos.views.listByTable(tableId, redis);
|
||||
|
||||
|
|
@ -126,7 +146,7 @@ viewsRoutes.get('/:viewId/data', requireScope('read:tables'), async (c) => {
|
|||
if (!tableIdRaw) {
|
||||
throw errors.validation([{ message: 'tableId query param required' }]);
|
||||
}
|
||||
const tableId = parseIntParam(tableIdRaw, 'tableId');
|
||||
const tableId = resolveTableId(tableIdRaw, getContainer());
|
||||
|
||||
const result = await repos.views.getViewData(viewId, tableId, {
|
||||
page,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue