openvibers/openvibe.bot Commits

Each resource names its page on openvibe.bot (links.console: a robot's /r/<id>, a machine's /machines), so OpenVibe.Services links to it (#198)

OP OpenVibers committed 17 days ago · b961dd1ee7 · 3 files, +19 −9

Changes

ddiff --git a/server/api/resource-index.js b/server/api/resource-index.js
iindex 7fbb42c..47b25df 100644
---- a/server/api/resource-index.js
++++ b/server/api/resource-index.js
@@@ -24,8 +24,11 @@ const SUBJECT_RE = /^(usr|agt)_[0-9A-HJKMNP-TV-Z]{26}$/;
const DEFAULT_LIMIT = 100;
const MAX_LIMIT = 1000;
++/** Where the resource's own page is (links.console): https only, for OpenVibe.Services to link to. */
++const consoleOf = (baseUrl, path) => (/^https:\/\//.test(String(baseUrl || '')) ? { links: { console: `${String(baseUrl).replace(/\/+$/, '')}${path}` } } : {});
++
/** Only the public fields in common.resource-summary@1. Person-owned robots have no OVRN. */
--function robotSummary(row) {
++function robotSummary(row, baseUrl = null) {
const summary = {
id: row.id, kind: KIND, service: SERVICE,
name: row.name, state: row.estop_latched ? 'estopped' : 'ready',
@@@ -35,11 +38,11 @@ function robotSummary(row) {
if (USER_SUBJECT_RE.test(String(row.owner_subject || ''))) summary.owner = { type: 'user', id: row.owner_subject };
const ovrn = contracts.resources.nameOf(summary);
if (ovrn) summary.ovrn = ovrn;
-- return summary;
++ return { ...summary, ...consoleOf(baseUrl, `/r/${encodeURIComponent(row.id)}`) };
}
/** A person's own machine (devices.owner_subject): online while its Node is connected. */
--function machineSummary(row, online) {
++function machineSummary(row, online, baseUrl = null) {
const summary = {
id: row.id, kind: MACHINE_KIND, service: SERVICE,
name: row.name || row.id, state: online ? 'online' : 'offline',
@@@ -47,7 +50,7 @@ function machineSummary(row, online) {
updated_at: new Date(row.updated_at).toISOString(),
};
if (USER_SUBJECT_RE.test(String(row.owner_subject || ''))) summary.owner = { type: 'user', id: row.owner_subject };
-- return summary;
++ return { ...summary, ...consoleOf(baseUrl, '/machines') };
}
const encodeCursor = (id, kind = KIND) => Buffer.from(JSON.stringify([kind, id])).toString('base64url');
@@@ -79,7 +82,7 @@ function filtersOf(query) {
return { project, kind, owner, limit, cursor };
}
--function router({ db, apiAuth, hub = null }) {
++function router({ db, apiAuth, hub = null, baseUrl = null }) {
const r = express.Router();
const wrap = (fn) => (req, res, next) => Promise.resolve(fn(req, res)).catch(next);
const bad = (res, detail, ctx) => contracts.http.sendProblem(res, 400, 'resources.bad_query', { detail, ctx });
@@@ -104,7 +107,7 @@ function router({ db, apiAuth, hub = null }) {
const rows = await db.many(`SELECT id, owner_subject, name, estop_latched, created_at, updated_at
FROM robots WHERE ($1::text IS NULL OR id > $1) AND ($3::text IS NULL OR owner_subject = $3) ORDER BY id LIMIT $2`,
[cur ? cur.id : null, filters.limit + 1, filters.owner]);
-- out.push(...rows.map(robotSummary));
++ out.push(...rows.map((row) => robotSummary(row, baseUrl)));
}
if ((!filters.kind || filters.kind === MACHINE_KIND) && out.length <= filters.limit) {
const after = cur && cur.kind === MACHINE_KIND ? cur.id : null;
@@@ -112,7 +115,7 @@ function router({ db, apiAuth, hub = null }) {
FROM devices WHERE owner_subject IS NOT NULL AND revoked_at IS NULL AND ($1::text IS NULL OR id > $1)
AND ($3::text IS NULL OR owner_subject = $3) ORDER BY id LIMIT $2`,
[after, filters.limit + 1 - out.length, filters.owner]);
-- out.push(...rows.map((row) => machineSummary(row, !!(hub && hub.isOnline(row.id)))));
++ out.push(...rows.map((row) => machineSummary(row, !!(hub && hub.isOnline(row.id)), baseUrl)));
}
const resources = out.slice(0, filters.limit);
const last = resources[resources.length - 1];
ddiff --git a/server/api/v1.js b/server/api/v1.js
iindex 7bfce81..3de1c98 100644
---- a/server/api/v1.js
++++ b/server/api/v1.js
@@@ -78,7 +78,7 @@ function v1Router({ domain, apiAuth, limits, hub, jobs: jobService, config, subm
r.use(limits.reads('bot.read'));
// Control first (bot.resource.control), then the index (bot.resource.read) for everything else under /resources.
r.use('/resources/control', limits('bot.control', { minute: 120, hour: 1200 }), resourceControl.router({ domain, apiAuth, hub }));
-- r.use('/resources', resourceIndex.router({ db: domain.db, apiAuth, hub }));
++ r.use('/resources', resourceIndex.router({ db: domain.db, apiAuth, hub, baseUrl: config.baseUrl }));
const manage = limits('bot.robot.manage', { minute: 30, hour: 300 });
const control = limits('bot.control', { minute: 120, hour: 1200 });
const machineScreen = createMachineScreen({ domain, hub });
ddiff --git a/test/resource-index.test.js b/test/resource-index.test.js
iindex 04fa60d..8572773 100644
---- a/test/resource-index.test.js
++++ b/test/resource-index.test.js
@@@ -21,7 +21,7 @@ const { boot, check, done } = require('./helpers/app');
assert.deepStrictEqual(contracts.validate('common.resource-list-result@1', response.json).errors, []);
assert.deepStrictEqual(response.json.resources.map((r) => r.id), [first.id, second.id, third.id].sort());
for (const summary of response.json.resources) {
-- assert.deepStrictEqual(Object.keys(summary).sort(), ['created_at', 'id', 'kind', 'name', 'owner', 'service', 'state', 'updated_at']);
++ assert.deepStrictEqual(Object.keys(summary).sort().filter((k) => k !== 'links'), ['created_at', 'id', 'kind', 'name', 'owner', 'service', 'state', 'updated_at']);
assert.strictEqual(summary.kind, 'bot.robot');
assert.strictEqual(summary.service, 'bot');
assert.strictEqual(summary.ovrn, undefined);
@@@ -117,6 +117,13 @@ const { boot, check, done } = require('./helpers/app');
assert.deepStrictEqual(all.json.resources.map((r) => [r.kind, r.id]), [...[first.id, second.id].sort().map((id) => ['bot.robot', id]), ['bot.machine', deviceId]], 'robots, then machines');
const machine = all.json.resources.find((r) => r.id === deviceId);
assert.deepStrictEqual([machine.state, machine.owner, machine.service], ['online', { type: 'user', id: alice.subject }, 'bot']);
++ // Its page on openvibe.bot (links.console) when Bot's address is https, for Services to link to.
++ const { machineSummary, robotSummary } = require('../server/api/resource-index');
++ const row = { id: deviceId, owner_subject: alice.subject, name: 'desk-pc', created_at: new Date(), updated_at: new Date() };
++ assert.deepStrictEqual(machineSummary(row, true, 'https://openvibe.bot/').links, { console: 'https://openvibe.bot/machines' });
++ assert.deepStrictEqual(robotSummary({ ...row, id: first.id, estop_latched: false }, 'https://openvibe.bot').links, { console: `https://openvibe.bot/r/${first.id}` });
++ assert.strictEqual(machineSummary(row, true, 'http://localhost:4630').links, undefined, 'https only');
++ assert.deepStrictEqual(contracts.validate('common.resource-summary@1', machineSummary(row, true, 'https://openvibe.bot')).errors, []);
assert.deepStrictEqual((await get('?kind=bot.machine')).json.resources.map((r) => r.id), [deviceId]);
assert.deepStrictEqual((await get('?kind=bot.robot')).json.resources.map((r) => r.kind), ['bot.robot', 'bot.robot']);
assert.deepStrictEqual((await get(`?owner=${bob.subject}`)).json.resources.map((r) => r.id), [second.id], 'never another person\'s');