feat(backend): persist per-experiment plot_config via PUT /:id/plot-config
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -224,6 +224,29 @@ function validateSubjectTemplate(template) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Upper bound on the stored UI-preference blob. Generous for the known fields;
|
||||
// guards against a client persisting an arbitrarily large object.
|
||||
const PLOT_CONFIG_MAX_BYTES = 16 * 1024;
|
||||
|
||||
// The blob is intentionally schema-loose (clients may add display keys over time);
|
||||
// only hiddenSubjects is structurally validated since the backend reasons about it.
|
||||
function validatePlotConfig(config) {
|
||||
if (config === null || typeof config !== 'object' || Array.isArray(config)) {
|
||||
return 'plot_config must be an object';
|
||||
}
|
||||
// req.body is already JSON-parsed, so stringify cannot throw here.
|
||||
if (Buffer.byteLength(JSON.stringify(config), 'utf8') > PLOT_CONFIG_MAX_BYTES) {
|
||||
return 'plot_config too large';
|
||||
}
|
||||
if ('hiddenSubjects' in config) {
|
||||
const hidden = config.hiddenSubjects;
|
||||
if (!Array.isArray(hidden) || hidden.length > 500 || !hidden.every((x) => typeof x === 'string')) {
|
||||
return 'hiddenSubjects invalid';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ── Template endpoints ─────────────────────────────────────────────────────────
|
||||
|
||||
// GET /api/experiments/:id/template
|
||||
@@ -308,6 +331,24 @@ router.put('/:id/subject-template', async (req, res, next) => {
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// PUT /api/experiments/:id/plot-config — persist UI display settings (no audit log)
|
||||
router.put('/:id/plot-config', async (req, res, next) => {
|
||||
try {
|
||||
const experiment = await prisma.experiment.findUnique({ where: { id: req.params.id } });
|
||||
if (!experiment) return res.status(404).json({ error: 'Experiment not found' });
|
||||
|
||||
const config = req.body;
|
||||
const validationError = validatePlotConfig(config);
|
||||
if (validationError) return res.status(422).json({ error: validationError });
|
||||
|
||||
await prisma.experiment.update({
|
||||
where: { id: req.params.id },
|
||||
data: { plot_config: config },
|
||||
});
|
||||
res.json(config);
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// GET /api/experiments/:id/calendar?field=<fieldId|builtinKey>
|
||||
// Returns { field, days: { "YYYY-MM-DD": count } } where count = subjects with non-empty value that day.
|
||||
router.get('/:id/calendar', async (req, res, next) => {
|
||||
|
||||
Reference in New Issue
Block a user