openvibers/openvibe.media Commits
PUT /clips/:id takes description too (docs/object-model.md said so; the handler only took title, visibility and auto_generated), cleaned as on create (#141)
Changes
@@@ -413,12 +413,18 @@ router.put('/:id', tenantAuth({ verb: 'write' }), async (req, res) => {try {const clip = await _getClipForWrite(req, res);if (!clip) return;-- const { title, visibility, auto_generated: autoGen } = req.body || {};++ const { title, description, visibility, auto_generated: autoGen } = req.body || {};if (title !== undefined) {const t = sanitizeClipTitle(title, '');if (!t) return res.status(400).json({ error: 'Title must be 1-200 characters' });await objects.withObject('clip', clip.id, async () => await db.run('UPDATE clips SET title = ? WHERE id = ?', [t, clip.id]));}++ // The same cleaning as a create: no markup, at most 1000 characters; null or '' clears it.++ if (description !== undefined) {++ if (description !== null && typeof description !== 'string') return res.status(400).json({ error: 'description must be a string' });++ const d = (description || '').replace(/<[^>]*>/g, '').trim().slice(0, 1000);++ await objects.withObject('clip', clip.id, async () => await db.run('UPDATE clips SET description = ? WHERE id = ?', [d, clip.id]));++ }// Only the app itself (its automation's own records) may say a clip was machine-made.if (autoGen !== undefined && flag(autoGen) !== null) {if (req.authType !== 'app') return res.status(403).json({ error: 'auto_generated is set by the app only' });@@@ -175,6 +175,11 @@ const sharp = require('sharp');({ obj } = await linked('clips', 'id', upId, 'clip update'));assert.strictEqual(obj.visibility, 'public');assert.deepStrictEqual([model.parseJson(obj.metadata, {}).title, model.parseJson(obj.metadata, {}).auto_generated], ['Better', true]);++ // The description is editable too (docs/object-model.md), cleaned as on create.++ r = await call('PUT', `/clips/${upId}`, { description: ' <b>A person</b> builds a desk. ' });++ assert.strictEqual(r.status, 200);++ assert.strictEqual((await db.get('SELECT description FROM clips WHERE id = ?', [upId])).description, 'A person builds a desk.');++ assert.strictEqual((await call('PUT', `/clips/${upId}`, { description: 42 })).status, 400);const doomedFile = path.join(process.env.CLIPS_PATH, 'doomed.webm');fs.writeFileSync(doomedFile, Buffer.alloc(700, 5));
@@ -413,12 +413,18 @@ router.put('/:id', tenantAuth({ verb: 'write' }), async (req, res) => {
@@ -413,12 +413,18 @@ router.put('/:id', tenantAuth({ verb: 'write' }), async (req, res) => {
try {
try {
const clip = await _getClipForWrite(req, res);
const clip = await _getClipForWrite(req, res);
if (!clip) return;
if (!clip) return;
- const { title, visibility, auto_generated: autoGen } = req.body || {};
+ const { title, description, visibility, auto_generated: autoGen } = req.body || {};
if (title !== undefined) {
if (title !== undefined) {
const t = sanitizeClipTitle(title, '');
const t = sanitizeClipTitle(title, '');
if (!t) return res.status(400).json({ error: 'Title must be 1-200 characters' });
if (!t) return res.status(400).json({ error: 'Title must be 1-200 characters' });
await objects.withObject('clip', clip.id, async () => await db.run('UPDATE clips SET title = ? WHERE id = ?', [t, clip.id]));
await objects.withObject('clip', clip.id, async () => await db.run('UPDATE clips SET title = ? WHERE id = ?', [t, clip.id]));
}
}
+ // The same cleaning as a create: no markup, at most 1000 characters; null or '' clears it.
+ if (description !== undefined) {
+ if (description !== null && typeof description !== 'string') return res.status(400).json({ error: 'description must be a string' });
+ const d = (description || '').replace(/<[^>]*>/g, '').trim().slice(0, 1000);
+ await objects.withObject('clip', clip.id, async () => await db.run('UPDATE clips SET description = ? WHERE id = ?', [d, clip.id]));
+ }
// Only the app itself (its automation's own records) may say a clip was machine-made.
// Only the app itself (its automation's own records) may say a clip was machine-made.
if (autoGen !== undefined && flag(autoGen) !== null) {
if (autoGen !== undefined && flag(autoGen) !== null) {
if (req.authType !== 'app') return res.status(403).json({ error: 'auto_generated is set by the app only' });
if (req.authType !== 'app') return res.status(403).json({ error: 'auto_generated is set by the app only' });
@@ -175,6 +175,11 @@ const sharp = require('sharp');
@@ -175,6 +175,11 @@ const sharp = require('sharp');
({ obj } = await linked('clips', 'id', upId, 'clip update'));
({ obj } = await linked('clips', 'id', upId, 'clip update'));
assert.strictEqual(obj.visibility, 'public');
assert.strictEqual(obj.visibility, 'public');
assert.deepStrictEqual([model.parseJson(obj.metadata, {}).title, model.parseJson(obj.metadata, {}).auto_generated], ['Better', true]);
assert.deepStrictEqual([model.parseJson(obj.metadata, {}).title, model.parseJson(obj.metadata, {}).auto_generated], ['Better', true]);
+ // The description is editable too (docs/object-model.md), cleaned as on create.
+ r = await call('PUT', `/clips/${upId}`, { description: ' <b>A person</b> builds a desk. ' });
+ assert.strictEqual(r.status, 200);
+ assert.strictEqual((await db.get('SELECT description FROM clips WHERE id = ?', [upId])).description, 'A person builds a desk.');
+ assert.strictEqual((await call('PUT', `/clips/${upId}`, { description: 42 })).status, 400);
const doomedFile = path.join(process.env.CLIPS_PATH, 'doomed.webm');
const doomedFile = path.join(process.env.CLIPS_PATH, 'doomed.webm');
fs.writeFileSync(doomedFile, Buffer.alloc(700, 5));
fs.writeFileSync(doomedFile, Buffer.alloc(700, 5));