2017-07-09 20:38:57 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const knex = require('../lib/knex');
|
|
|
|
const hasher = require('node-object-hash')();
|
|
|
|
const { enforce, filterObject } = require('../lib/helpers');
|
|
|
|
const dtHelpers = require('../lib/dt-helpers');
|
|
|
|
const interoperableErrors = require('../shared/interoperable-errors');
|
2017-07-13 11:27:03 +00:00
|
|
|
const fields = require('./fields');
|
2017-07-09 20:38:57 +00:00
|
|
|
|
2017-07-13 11:27:03 +00:00
|
|
|
const ReportState = require('../shared/reports').ReportState;
|
2017-07-09 20:38:57 +00:00
|
|
|
|
2017-07-13 11:27:03 +00:00
|
|
|
const allowedKeys = new Set(['name', 'description', 'report_template', 'params']);
|
2017-07-09 21:16:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
function hash(entity) {
|
|
|
|
return hasher.hash(filterObject(entity, allowedKeys));
|
2017-07-09 20:38:57 +00:00
|
|
|
}
|
|
|
|
|
2017-07-13 11:27:03 +00:00
|
|
|
async function getByIdWithTemplate(id) {
|
|
|
|
const entity = await knex('reports').where('reports.id', id).innerJoin('report_templates', 'reports.report_template', 'report_templates.id').select(['reports.id', 'reports.name', 'reports.description', 'reports.report_template', 'reports.params', 'reports.state', 'report_templates.user_fields', 'report_templates.mime_type', 'report_templates.hbs', 'report_templates.js']).first();
|
2017-07-09 21:16:47 +00:00
|
|
|
if (!entity) {
|
2017-07-09 20:38:57 +00:00
|
|
|
throw new interoperableErrors.NotFoundError();
|
|
|
|
}
|
|
|
|
|
2017-07-11 09:28:44 +00:00
|
|
|
entity.user_fields = JSON.parse(entity.user_fields);
|
|
|
|
entity.params = JSON.parse(entity.params);
|
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
return entity;
|
2017-07-09 20:38:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function listDTAjax(params) {
|
2017-07-13 11:27:03 +00:00
|
|
|
return await dtHelpers.ajaxList(params, tx => tx('reports').innerJoin('report_templates', 'reports.report_template', 'report_templates.id'), ['reports.id', 'reports.name', 'report_templates.name', 'reports.description', 'reports.last_run', 'reports.state', 'report_templates.mime_type']);
|
2017-07-09 20:38:57 +00:00
|
|
|
}
|
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
async function create(entity) {
|
2017-07-13 11:27:03 +00:00
|
|
|
let id;
|
2017-07-09 21:16:47 +00:00
|
|
|
await knex.transaction(async tx => {
|
|
|
|
if (!await tx('report_templates').select(['id']).where('id', entity.report_template).first()) {
|
|
|
|
throw new interoperableErrors.DependencyNotFoundError();
|
|
|
|
}
|
|
|
|
|
2017-07-11 09:28:44 +00:00
|
|
|
entity.params = JSON.stringify(entity.params);
|
|
|
|
|
2017-07-13 11:27:03 +00:00
|
|
|
id = await tx('reports').insert(filterObject(entity, allowedKeys));
|
2017-07-09 21:16:47 +00:00
|
|
|
});
|
2017-07-13 11:27:03 +00:00
|
|
|
|
|
|
|
const reportProcessor = require('../lib/report-processor');
|
|
|
|
await reportProcessor.start(id);
|
|
|
|
return id;
|
2017-07-09 20:38:57 +00:00
|
|
|
}
|
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
async function updateWithConsistencyCheck(entity) {
|
2017-07-09 20:38:57 +00:00
|
|
|
await knex.transaction(async tx => {
|
2017-07-09 21:16:47 +00:00
|
|
|
const existing = await tx('reports').where('id', entity.id).first();
|
|
|
|
if (!entity) {
|
2017-07-09 20:38:57 +00:00
|
|
|
throw new interoperableErrors.NotFoundError();
|
|
|
|
}
|
|
|
|
|
2017-07-11 09:28:44 +00:00
|
|
|
existing.params = JSON.parse(existing.params);
|
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
const existingHash = hash(existing);
|
|
|
|
if (existingHash != entity.originalHash) {
|
2017-07-09 20:38:57 +00:00
|
|
|
throw new interoperableErrors.ChangedError();
|
|
|
|
}
|
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
if (!await tx('report_templates').select(['id']).where('id', entity.report_template).first()) {
|
|
|
|
throw new interoperableErrors.DependencyNotFoundError();
|
|
|
|
}
|
|
|
|
|
2017-07-11 09:28:44 +00:00
|
|
|
entity.params = JSON.stringify(entity.params);
|
|
|
|
|
2017-07-13 11:27:03 +00:00
|
|
|
const filteredUpdates = filterObject(entity, allowedKeys);
|
|
|
|
filteredUpdates.state = ReportState.SCHEDULED;
|
|
|
|
|
|
|
|
await tx('reports').where('id', entity.id).update(filteredUpdates);
|
2017-07-09 20:38:57 +00:00
|
|
|
});
|
2017-07-13 11:27:03 +00:00
|
|
|
|
|
|
|
// This require is here to avoid cyclic dependency
|
|
|
|
const reportProcessor = require('../lib/report-processor');
|
|
|
|
await reportProcessor.start(entity.id);
|
2017-07-09 20:38:57 +00:00
|
|
|
}
|
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
async function remove(id) {
|
|
|
|
await knex('reports').where('id', id).del();
|
2017-07-09 20:38:57 +00:00
|
|
|
}
|
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
async function updateFields(id, fields) {
|
|
|
|
return await knex('reports').where('id', id).update(fields);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function listByState(state, limit) {
|
|
|
|
return await knex('reports').where('state', state).limit(limit);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function bulkChangeState(oldState, newState) {
|
|
|
|
return await knex('reports').where('state', oldState).update('state', newState);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-13 11:27:03 +00:00
|
|
|
const campaignFieldsMapping = {
|
|
|
|
tracker_count: 'tracker.count',
|
|
|
|
country: 'tracker.country',
|
|
|
|
device_type: 'tracker.device_type',
|
|
|
|
status: 'campaign.status',
|
|
|
|
first_name: 'subscribers.first_name',
|
|
|
|
last_name: 'subscribers.last_name',
|
|
|
|
email: 'subscribers.email'
|
|
|
|
};
|
|
|
|
|
|
|
|
function customFieldName(id) {
|
|
|
|
return id.replace(/MERGE_/, 'CUSTOM_').toLowerCase();
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getCampaignResults(campaign, select, extra) {
|
|
|
|
const fieldList = await fields.list(campaign.list);
|
|
|
|
|
|
|
|
const fieldsMapping = fieldList.reduce((map, field) => {
|
|
|
|
/* Dropdowns and checkboxes are aggregated. As such, they have field.column == null and the options are in field.options.
|
|
|
|
TODO - For the time being, we ignore groupped fields. */
|
|
|
|
if (field.column) {
|
|
|
|
map[customFieldName(field.key)] = 'subscribers.' + field.column;
|
|
|
|
}
|
|
|
|
return map;
|
|
|
|
}, Object.assign({}, campaignFieldsMapping));
|
|
|
|
|
|
|
|
let selFields = [];
|
|
|
|
for (let idx = 0; idx < select.length; idx++) {
|
|
|
|
const item = select[idx];
|
|
|
|
if (item in fieldsMapping) {
|
|
|
|
selFields.push(fieldsMapping[item] + ' AS ' + item);
|
|
|
|
} else if (item === '*') {
|
|
|
|
selFields = selFields.concat(Object.keys(fieldsMapping).map(item => fieldsMapping[item] + ' AS ' + item));
|
|
|
|
} else {
|
|
|
|
selFields.push(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let query = knex(`subscription__${campaign.list} AS subscribers`)
|
|
|
|
.innerJoin(`campaign__${campaign.id} AS campaign`, 'subscribers.id', 'campaign.subscription')
|
|
|
|
.leftJoin(`campaign_tracker__${campaign.id} AS tracker`, 'subscribers.id', 'tracker.subscriber')
|
|
|
|
.select(selFields);
|
|
|
|
|
|
|
|
if (extra) {
|
|
|
|
query = extra(query);
|
|
|
|
}
|
|
|
|
|
|
|
|
return await query;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
|
2017-07-09 20:38:57 +00:00
|
|
|
module.exports = {
|
2017-07-09 21:16:47 +00:00
|
|
|
ReportState,
|
2017-07-09 20:38:57 +00:00
|
|
|
hash,
|
2017-07-13 11:27:03 +00:00
|
|
|
getByIdWithTemplate,
|
2017-07-09 20:38:57 +00:00
|
|
|
listDTAjax,
|
|
|
|
create,
|
|
|
|
updateWithConsistencyCheck,
|
2017-07-09 21:16:47 +00:00
|
|
|
remove,
|
|
|
|
updateFields,
|
|
|
|
listByState,
|
2017-07-13 11:27:03 +00:00
|
|
|
bulkChangeState,
|
|
|
|
getCampaignResults
|
2017-07-09 20:38:57 +00:00
|
|
|
};
|