openvibers/openvibe.watch Commits

A fired watch's notice reads a named value as words, not JSON (#255)

OP OpenVibers committed 4 days ago · 2cc69e8d6c · 2 files, +27 −1

A feed entry or any record with a title or name reads as 'title — summary'; a list of them as
'N items, first: …'. Other shapes (a price object, a status map) stay JSON. Production sent a job
posting's JSON, cut at 300 characters, to the bell.

Changes

ddiff --git a/server/actions.js b/server/actions.js
iindex 1a131cf..f2625ee 100644
---- a/server/actions.js
++++ b/server/actions.js
@@@ -88,11 +88,22 @@ const REFUSED_URL = new Set(['bad_url', 'port_refused', 'address_refused']);
const clip = (s, n) => { const t = String(s == null ? '' : s).replace(/\s+/g, ' ').trim(); return t.length > n ? `${t.slice(0, n - 1)}…` : t; };
--/** A value as a person reads it in one line. */
++/**
++ * A value as a person reads it in one line. A feed entry or any record with a title or name reads as that title and
++ * a short summary ("(Senior) Product Engineer — Bielefeld · Remote"), never as its JSON; a list says how many and
++ * names the first. Other shapes stay JSON (a price object, a status map).
++ */
function valueText(v) {
if (v == null) return 'no value';
if (typeof v === 'string') return v;
if (typeof v === 'number' || typeof v === 'boolean') return String(v);
++ const named = (o) => (o && typeof o === 'object' && !Array.isArray(o) && [o.title, o.name].find((x) => typeof x === 'string' && x.trim())) || null;
++ const head = named(v);
++ if (head) {
++ const more = [v.summary, v.description].find((x) => typeof x === 'string' && x.trim());
++ return more ? `${clip(head, 160)} — ${clip(more, 120)}` : clip(head, 160);
++ }
++ if (Array.isArray(v) && v.length && named(v[0])) return `${v.length} item${v.length === 1 ? '' : 's'}, first: ${clip(named(v[0]), 160)}`;
try { return JSON.stringify(v); } catch { return String(v); }
}
ddiff --git a/test/actions.test.js b/test/actions.test.js
iindex caef69a..41b9638 100644
---- a/test/actions.test.js
++++ b/test/actions.test.js
@@@ -513,6 +513,21 @@ t('an alert leaves out the source and fetched lines when the watch stored neithe
assert.deepStrictEqual(p.rich_content.evidence, { before: null, after: 'v1', source_url: null, fetched_at: null });
});
++t('a feed entry or a named record reads as its title and summary, never as JSON', () => {
++ const { notificationFor } = require('../server/actions');
++ const at = (value, previousValue) => notificationFor({
++ config: { baseUrl: 'https://openvibe.watch' },
++ watch: { id: 'wch_01JAB2C3D4E5F6G7H8J9K0MNPQ', name: 'Jobs', source: { kind: 'event', pattern: 'work.job.posted' } },
++ run: { id: 'ckr_01JAB2C3D4E5F6G7H8J9K0MNPQ' }, action: {}, value, previousValue, observedAt: null,
++ }).message;
++ const entry = { identity: 'https://openvibe.work/jobs/job_1', kind: 'entry', title: '(Senior) Product Engineer at sustaind GmbH', link: 'https://openvibe.work/jobs/job_1', summary: 'Bielefeld · Remote' };
++ assert.strictEqual(at(entry), '(Senior) Product Engineer at sustaind GmbH — Bielefeld · Remote');
++ assert.strictEqual(at({ name: 'Front door', state: 'open' }), 'Front door');
++ assert.strictEqual(at([entry, entry]), '2 items, first: (Senior) Product Engineer at sustaind GmbH');
++ assert.strictEqual(at({ price: 12 }), '{"price":12}', 'a shape with no name stays as it is');
++ assert.strictEqual(at({ title: 'B' }, { title: 'A' }), 'B (was A)');
++});
++
t('done', async () => { await web.close(); await svc.stop(); });
t.run();