Reports ported to ReactJS and Knex

Note that the interface for the custom JS code inside a report template has changed. It now offers promise-based interface and exposes knex.
This commit is contained in:
Tomas Bures 2017-07-13 13:27:03 +02:00
parent 6d95fa515e
commit d63eed9ca9
27 changed files with 649 additions and 953 deletions

View file

@ -1,10 +1,10 @@
'use strict';
const reports = require('../../lib/models/reports-REMOVE');
const reportTemplates = require('../../lib/models/report-templates');
const lists = require('../../lib/models/lists');
const subscriptions = require('../../lib/models/subscriptions');
const campaigns = require('../../lib/models/campaigns');
const reports = require('../../models/reports');
const reportTemplates = require('../../models/report-templates');
const lists = require('../../models/lists');
const subscriptions = require('../../models/subscriptions');
const campaigns = require('../../models/campaigns');
const handlebars = require('handlebars');
const handlebarsHelpers = require('../../lib/handlebars-helpers');
const _ = require('../../lib/translate')._;
@ -12,136 +12,82 @@ const hbs = require('hbs');
const vm = require('vm');
const log = require('npmlog');
const fs = require('fs');
const knex = require('../../lib/knex');
handlebarsHelpers.registerHelpers(handlebars);
let reportId = Number(process.argv[2]);
let reportDir;
function resolveEntities(getter, ids, callback) {
const idsRemaining = ids.slice();
const resolved = [];
function doWork() {
if (idsRemaining.length == 0) {
return callback(null, resolved);
}
getter(idsRemaining.shift(), (err, entity) => {
if (err) {
return callback(err);
}
resolved.push(entity);
return doWork();
});
}
setImmediate(doWork);
}
const userFieldTypeToGetter = {
'campaign': (id, callback) => campaigns.get(id, false, callback),
'list': lists.get
const userFieldGetters = {
'campaign': campaigns.getById,
'list': lists.getById
};
function resolveUserFields(userFields, params, callback) {
const userFieldsRemaining = userFields.slice();
const resolved = {};
function doWork() {
if (userFieldsRemaining.length == 0) {
return callback(null, resolved);
}
async function main() {
try {
const reportId = Number(process.argv[2]);
const spec = userFieldsRemaining.shift();
const getter = userFieldTypeToGetter[spec.type];
const report = await reports.getByIdWithTemplate(reportId);
if (getter) {
return resolveEntities(getter, params[spec.id], (err, entities) => {
if (spec.minOccurences == 1 && spec.maxOccurences == 1) {
resolved[spec.id] = entities[0];
} else {
resolved[spec.id] = entities;
}
const inputs = {};
doWork();
});
} else {
return callback(new Error(_('Unknown user field type "' + spec.type + '".')));
}
}
setImmediate(doWork);
}
function doneSuccess() {
process.exit(0);
}
function doneFail() {
process.exit(1)
}
reports.get(reportId, (err, report) => {
if (err || !report) {
log.error('reports', err && err.message || err || _('Could not find report with specified ID'));
doneFail();
}
reportTemplates.get(report.reportTemplate, (err, reportTemplate) => {
if (err) {
log.error('reports', err && err.message || err || _('Could not find report template'));
doneFail();
}
resolveUserFields(reportTemplate.userFieldsObject, report.paramsObject, (err, inputs) => {
if (err) {
log.error('reports', err.message || err);
doneFail();
for (const spec of report.user_fields) {
const getter = userFieldGetters[spec.type];
if (!getter) {
throw new Error(_('Unknown user field type "' + spec.type + '".'));
}
const campaignsProxy = {
results: reports.getCampaignResults,
list: campaigns.list,
get: campaigns.get
};
const subscriptionsProxy = {
list: subscriptions.list
};
const sandbox = {
console,
campaigns: campaignsProxy,
subscriptions: subscriptionsProxy,
inputs,
callback: (err, outputs) => {
if (err) {
log.error('reports', err.message || err);
doneFail();
}
const hbsTmpl = handlebars.compile(reportTemplate.hbs);
const reportText = hbsTmpl(outputs);
process.stdout.write(reportText);
doneSuccess();
}
};
const script = new vm.Script(reportTemplate.js);
try {
script.runInNewContext(sandbox, {displayErrors: true, timeout: 120000});
} catch (err) {
console.error(err);
doneFail();
const entities = [];
for (const id of report.params[spec.id]) {
entities.push(await getter(id));
}
});
});
});
if (spec.minOccurences == 1 && spec.maxOccurences == 1) {
inputs[spec.id] = entities[0];
} else {
inputs[spec.id] = entities;
}
}
const campaignsProxy = {
getResults: reports.getCampaignResults,
getById: campaigns.getById
};
const subscriptionsProxy = {
list: subscriptions.list
};
const sandbox = {
console,
campaigns: campaignsProxy,
subscriptions: subscriptionsProxy,
knex,
process,
inputs,
render: data => {
const hbsTmpl = handlebars.compile(report.hbs);
const reportText = hbsTmpl(data);
process.stdout.write(reportText);
}
};
const js =
'(async function() {' +
report.js +
'})().then(() => process.exit(0)).catch(err => { console.error(err); process.exit(1); })';
const script = new vm.Script(js);
script.runInNewContext(sandbox, {displayErrors: true, timeout: 120000});
} catch (err) {
console.error(err);
process.exit(1);
}
}
main();