fix(bridge): smoke test fixes — skip rows malformees + BASEROW_TABLE_IDS override
Some checks are pending
CI / Lint bridge (Biome) (push) Waiting to run
CI / Type-check bridge (push) Blocked by required conditions
CI / Tests unit bridge (push) Blocked by required conditions
CI / Tests integration bridge (push) Blocked by required conditions
CI / Security scan (push) Waiting to run
CI / Docker build + healthcheck (push) Blocked by required conditions
Some checks are pending
CI / Lint bridge (Biome) (push) Waiting to run
CI / Type-check bridge (push) Blocked by required conditions
CI / Tests unit bridge (push) Blocked by required conditions
CI / Tests integration bridge (push) Blocked by required conditions
CI / Security scan (push) Waiting to run
CI / Docker build + healthcheck (push) Blocked by required conditions
Decouverts via smoke test local contre Baserow + Docmost reels.
1. **BaseRepo.list robuste** : try/catch toDomain par row, skip + log warn
si throw (ex Personne avec splits null != 100). Avant : 500 sur la liste
entiere. Apres : 200 avec items valides + meta.skipped count pour visibilite.
`get()` continue de propager (un get sur row corrompue = legitimement 500
pour investigation manuelle).
2. **BASEROW_TABLE_IDS env override** : BaserowClient.resolveTableIds appelle
/api/database/tables/database/:id/ qui requiert un JWT user (Baserow API
distingue DB tokens reservees aux endpoints rows, et JWT pour les endpoints
admin). En dev/prod simple on passe le mapping directement par env var :
BASEROW_TABLE_IDS={"personne":609,"formation":610,...}. Le code resolveTableIds
reste en place pour Phase 3+ (bridge avec JWT user).
Smoke test post-fix :
- GET /api/health, /api/ready : 200
- Auth : 401 absent / 401 invalide / 200 valide
- GET /personnes (rows test invalides) : 200 data:[] meta.skipped:2
- GET /formations, /projets : 200 avec rows
- GET /personnes/9999 : 404
Tests Vitest : 163/163 verts. tsc + biome ci verts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c8e9b4d4ea
commit
7a3fbe455d
2 changed files with 38 additions and 7 deletions
|
|
@ -57,13 +57,22 @@ export async function buildApp(): Promise<Hono<{ Variables: AuthVariables }>> {
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
// BASEROW_DATABASE_ID requis pour resolveTableIds. Cf .env
|
// Soit BASEROW_TABLE_IDS={"personne":609,...} (preferred — DB tokens n'ont pas
|
||||||
|
// acces a /api/database/tables/database/:id/), soit BASEROW_DATABASE_ID + un JWT
|
||||||
|
// user (Phase 3+). Cf doc 19 §5.
|
||||||
|
const tableIdsRaw = process.env.BASEROW_TABLE_IDS;
|
||||||
const databaseIdRaw = process.env.BASEROW_DATABASE_ID;
|
const databaseIdRaw = process.env.BASEROW_DATABASE_ID;
|
||||||
|
let initOpts: Parameters<typeof initContainer>[0];
|
||||||
|
if (tableIdsRaw) {
|
||||||
|
initOpts = { config, tableIds: JSON.parse(tableIdsRaw) };
|
||||||
|
} else {
|
||||||
const databaseId = databaseIdRaw ? Number.parseInt(databaseIdRaw, 10) : undefined;
|
const databaseId = databaseIdRaw ? Number.parseInt(databaseIdRaw, 10) : undefined;
|
||||||
if (!databaseId || Number.isNaN(databaseId)) {
|
if (!databaseId || Number.isNaN(databaseId)) {
|
||||||
throw new Error('BASEROW_DATABASE_ID env var requis pour resolve table ids');
|
throw new Error('BASEROW_TABLE_IDS ou BASEROW_DATABASE_ID requis');
|
||||||
}
|
}
|
||||||
await initContainer({ config, databaseId });
|
initOpts = { config, databaseId };
|
||||||
|
}
|
||||||
|
await initContainer(initOpts);
|
||||||
const app = await buildApp();
|
const app = await buildApp();
|
||||||
|
|
||||||
serve({ fetch: app.fetch, port: config.port }, (info) => {
|
serve({ fetch: app.fetch, port: config.port }, (info) => {
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,13 @@ abstract class BaseRepo<TDomain> {
|
||||||
|
|
||||||
async list(opts: BaserowListOptions = {}): Promise<{
|
async list(opts: BaserowListOptions = {}): Promise<{
|
||||||
items: TDomain[];
|
items: TDomain[];
|
||||||
meta: { page: number; per_page: number; total: number; total_pages: number };
|
meta: {
|
||||||
|
page: number;
|
||||||
|
per_page: number;
|
||||||
|
total: number;
|
||||||
|
total_pages: number;
|
||||||
|
skipped?: number;
|
||||||
|
};
|
||||||
}> {
|
}> {
|
||||||
const page = opts.page ?? 1;
|
const page = opts.page ?? 1;
|
||||||
const size = Math.min(opts.size ?? 50, 200);
|
const size = Math.min(opts.size ?? 50, 200);
|
||||||
|
|
@ -140,7 +146,22 @@ abstract class BaseRepo<TDomain> {
|
||||||
page,
|
page,
|
||||||
size,
|
size,
|
||||||
});
|
});
|
||||||
const items = res.results.map((row) => this.toDomain(row));
|
// Skip rows that fail domain validation (split != 100, etc.) plutot que
|
||||||
|
// de casser la liste entiere. La row corrompue est loguee pour investigation
|
||||||
|
// manuelle. cf doc 19 §10 : robustness vs visibility.
|
||||||
|
const items: TDomain[] = [];
|
||||||
|
let skipped = 0;
|
||||||
|
for (const row of res.results) {
|
||||||
|
try {
|
||||||
|
items.push(this.toDomain(row));
|
||||||
|
} catch (err) {
|
||||||
|
skipped++;
|
||||||
|
this.logger.warn(
|
||||||
|
{ rowId: row.id, err: err instanceof Error ? err.message : String(err) },
|
||||||
|
'row skipped — invalid domain mapping',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
items,
|
items,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
@ -148,6 +169,7 @@ abstract class BaseRepo<TDomain> {
|
||||||
per_page: size,
|
per_page: size,
|
||||||
total: res.count,
|
total: res.count,
|
||||||
total_pages: Math.max(1, Math.ceil(res.count / size)),
|
total_pages: Math.max(1, Math.ceil(res.count / size)),
|
||||||
|
...(skipped > 0 ? { skipped } : {}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue