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');
|
2018-11-18 14:38:52 +00:00
|
|
|
const interoperableErrors = require('../../shared/interoperable-errors');
|
2017-07-13 11:27:03 +00:00
|
|
|
const fields = require('./fields');
|
2017-07-24 11:43:32 +00:00
|
|
|
const namespaceHelpers = require('../lib/namespace-helpers');
|
2017-07-24 23:14:17 +00:00
|
|
|
const shares = require('./shares');
|
2018-09-29 18:08:49 +00:00
|
|
|
const reportHelpers = require('../lib/report-helpers');
|
|
|
|
const fs = require('fs-extra-promise');
|
2018-12-21 18:09:18 +00:00
|
|
|
const contextHelpers = require('../lib/context-helpers');
|
|
|
|
const {LinkId} = require('./links');
|
|
|
|
const subscriptions = require('./subscriptions');
|
|
|
|
const {Readable} = require('stream');
|
2017-07-09 20:38:57 +00:00
|
|
|
|
2018-11-18 14:38:52 +00:00
|
|
|
const ReportState = require('../../shared/reports').ReportState;
|
2017-07-09 20:38:57 +00:00
|
|
|
|
2017-07-24 11:43:32 +00:00
|
|
|
const allowedKeys = new Set(['name', 'description', 'report_template', 'params', 'namespace']);
|
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
|
|
|
}
|
|
|
|
|
2018-11-17 01:54:23 +00:00
|
|
|
async function getByIdWithTemplate(context, id, withPermissions = true) {
|
2017-08-13 18:11:58 +00:00
|
|
|
return await knex.transaction(async tx => {
|
2017-08-11 16:16:44 +00:00
|
|
|
await shares.enforceEntityPermissionTx(tx, context, 'report', id, 'view');
|
|
|
|
|
2017-08-13 18:11:58 +00:00
|
|
|
const entity = await tx('reports')
|
2017-08-11 16:16:44 +00:00
|
|
|
.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', 'reports.namespace', 'report_templates.user_fields', 'report_templates.mime_type', 'report_templates.hbs', 'report_templates.js'])
|
|
|
|
.first();
|
2017-07-24 11:43:32 +00:00
|
|
|
|
2017-08-11 16:16:44 +00:00
|
|
|
entity.user_fields = JSON.parse(entity.user_fields);
|
|
|
|
entity.params = JSON.parse(entity.params);
|
|
|
|
|
2018-11-17 01:54:23 +00:00
|
|
|
if (withPermissions) {
|
|
|
|
entity.permissions = await shares.getPermissionsTx(tx, context, 'report', id);
|
|
|
|
}
|
2017-07-11 09:28:44 +00:00
|
|
|
|
2017-08-13 18:11:58 +00:00
|
|
|
return entity;
|
|
|
|
});
|
2017-07-09 20:38:57 +00:00
|
|
|
}
|
|
|
|
|
2017-07-26 19:42:05 +00:00
|
|
|
async function listDTAjax(context, params) {
|
|
|
|
return await dtHelpers.ajaxListWithPermissions(
|
|
|
|
context,
|
|
|
|
[
|
|
|
|
{ entityTypeId: 'report', requiredOperations: ['view'] },
|
|
|
|
{ entityTypeId: 'reportTemplate', requiredOperations: ['view'] }
|
|
|
|
],
|
2017-07-24 11:43:32 +00:00
|
|
|
params,
|
2017-07-26 19:42:05 +00:00
|
|
|
builder => builder.from('reports')
|
2017-07-24 11:43:32 +00:00
|
|
|
.innerJoin('report_templates', 'reports.report_template', 'report_templates.id')
|
|
|
|
.innerJoin('namespaces', 'namespaces.id', 'reports.namespace'),
|
2017-07-26 19:42:05 +00:00
|
|
|
[
|
|
|
|
'reports.id', 'reports.name', 'report_templates.name', 'reports.description',
|
|
|
|
'reports.last_run', 'namespaces.name', 'reports.state', 'report_templates.mime_type'
|
|
|
|
]
|
2017-07-24 11:43:32 +00:00
|
|
|
);
|
2017-07-09 20:38:57 +00:00
|
|
|
}
|
|
|
|
|
2017-07-26 19:42:05 +00:00
|
|
|
async function create(context, entity) {
|
2017-07-13 11:27:03 +00:00
|
|
|
let id;
|
2017-07-09 21:16:47 +00:00
|
|
|
await knex.transaction(async tx => {
|
2017-08-11 06:51:30 +00:00
|
|
|
await shares.enforceEntityPermissionTx(tx, context, 'namespace', entity.namespace, 'createReport');
|
|
|
|
await shares.enforceEntityPermissionTx(tx, context, 'reportTemplate', entity.report_template, 'execute');
|
2017-07-24 11:43:32 +00:00
|
|
|
|
2017-08-11 06:51:30 +00:00
|
|
|
await namespaceHelpers.validateEntity(tx, entity);
|
2017-07-09 21:16:47 +00:00
|
|
|
|
2017-07-11 09:28:44 +00:00
|
|
|
entity.params = JSON.stringify(entity.params);
|
|
|
|
|
2017-07-27 19:41:25 +00:00
|
|
|
const ids = await tx('reports').insert(filterObject(entity, allowedKeys));
|
|
|
|
id = ids[0];
|
2017-07-24 23:14:17 +00:00
|
|
|
|
2017-08-14 20:53:29 +00:00
|
|
|
await shares.rebuildPermissionsTx(tx, { entityTypeId: 'report', entityId: id });
|
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-26 19:42:05 +00:00
|
|
|
async function updateWithConsistencyCheck(context, entity) {
|
2017-07-09 20:38:57 +00:00
|
|
|
await knex.transaction(async tx => {
|
2017-08-11 06:51:30 +00:00
|
|
|
await shares.enforceEntityPermissionTx(tx, context, 'report', entity.id, 'edit');
|
|
|
|
await shares.enforceEntityPermissionTx(tx, context, 'reportTemplate', entity.report_template, 'execute');
|
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
const existing = await tx('reports').where('id', entity.id).first();
|
2017-07-26 19:42:05 +00:00
|
|
|
if (!existing) {
|
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);
|
2017-08-11 16:16:44 +00:00
|
|
|
if (existingHash !== entity.originalHash) {
|
2017-07-09 20:38:57 +00:00
|
|
|
throw new interoperableErrors.ChangedError();
|
|
|
|
}
|
|
|
|
|
2017-07-24 11:43:32 +00:00
|
|
|
await namespaceHelpers.validateEntity(tx, entity);
|
2017-07-29 19:42:07 +00:00
|
|
|
await namespaceHelpers.validateMove(context, entity, existing, 'report', 'createReport', 'delete');
|
2017-07-26 19:42:05 +00:00
|
|
|
|
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-24 23:14:17 +00:00
|
|
|
|
2017-08-14 20:53:29 +00:00
|
|
|
await shares.rebuildPermissionsTx(tx, { entityTypeId: 'report', entityId: entity.id });
|
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-08-14 20:53:29 +00:00
|
|
|
async function removeTx(tx, context, id) {
|
|
|
|
await shares.enforceEntityPermissionTx(tx, context, 'report', id, 'delete');
|
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
const report = await tx('reports').where('id', id).first();
|
2018-09-29 18:08:49 +00:00
|
|
|
|
|
|
|
await fs.removeAsync(reportHelpers.getReportContentFile(report));
|
|
|
|
await fs.removeAsync(reportHelpers.getReportOutputFile(report));
|
2017-08-14 20:53:29 +00:00
|
|
|
|
2018-09-29 18:08:49 +00:00
|
|
|
await tx('reports').where('id', id).del();
|
2017-08-14 20:53:29 +00:00
|
|
|
}
|
|
|
|
|
2017-07-26 19:42:05 +00:00
|
|
|
async function remove(context, id) {
|
2017-08-14 20:53:29 +00:00
|
|
|
await knex.transaction(async tx => {
|
|
|
|
await removeTx(tx, context, id);
|
|
|
|
});
|
|
|
|
}
|
2017-07-26 19:42:05 +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);
|
|
|
|
}
|
|
|
|
|
2019-04-22 20:46:48 +00:00
|
|
|
async function getCampaignCommonListFields(campaign) {
|
|
|
|
const listFields = {};
|
|
|
|
let firstIteration = true;
|
|
|
|
for (const cpgList of campaign.lists) {
|
|
|
|
const cpgListId = cpgList.list;
|
|
|
|
|
|
|
|
const flds = await fields.list(contextHelpers.getAdminContext(), cpgListId);
|
|
|
|
|
|
|
|
const assignedFlds = new Set();
|
|
|
|
|
|
|
|
for (const fld of flds) {
|
|
|
|
/* Dropdown and checkbox groups have field.column == null
|
|
|
|
For the time being, we don't group options and we don't expand enums. We just provide it as it is in the DB. */
|
|
|
|
if (fld.column) {
|
|
|
|
const fldKey = 'field:' + fld.key.toLowerCase();
|
|
|
|
|
|
|
|
if (firstIteration) {
|
|
|
|
listFields[fldKey] = {
|
|
|
|
key: fld.key,
|
|
|
|
name: fld.name,
|
|
|
|
description: fld.description
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fldKey in listFields) {
|
|
|
|
assignedFlds.add(fldKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const fldKey in listFields) {
|
|
|
|
if (!assignedFlds.has(fldKey)) {
|
|
|
|
delete listFields[fldKey];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
firstIteration = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return listFields;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function _getCampaignStatistics(campaign, select, joins, unionQryFn, listQryFn, asStream) {
|
2018-12-21 18:09:18 +00:00
|
|
|
const subsQrys = [];
|
2019-04-22 20:46:48 +00:00
|
|
|
joins = joins || [];
|
|
|
|
|
|
|
|
const knexJoinFns = [];
|
|
|
|
|
|
|
|
const commonFieldsMapping = {
|
|
|
|
'subscription:status': 'subscriptions.status',
|
|
|
|
'subscription:id': 'subscriptions.id',
|
|
|
|
'subscription:cid': 'subscriptions.cid',
|
|
|
|
'subscription:email': 'subscriptions.email'
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const join of joins) {
|
|
|
|
const prefix = join.prefix;
|
|
|
|
const type = join.type;
|
|
|
|
const onConditions = join.onConditions || {};
|
|
|
|
|
|
|
|
const getConds = (alias, cpgListId) => {
|
|
|
|
const conds = {
|
|
|
|
[alias + '.campaign']: knex.raw('?', [campaign.id]),
|
|
|
|
[alias + '.list']: knex.raw('?', [cpgListId]),
|
|
|
|
[alias + '.subscription']: 'subscriptions.id',
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const onConditionKey in onConditions) {
|
|
|
|
conds[alias + '.' + onConditionKey] = onConditions[onConditionKey];
|
|
|
|
}
|
|
|
|
|
|
|
|
return conds;
|
|
|
|
};
|
2018-12-21 18:09:18 +00:00
|
|
|
|
2019-04-22 20:46:48 +00:00
|
|
|
if (type === 'messages') {
|
|
|
|
const alias = 'campaign_messages_' + prefix;
|
|
|
|
|
|
|
|
commonFieldsMapping[`${prefix}:status`] = alias + '.status';
|
|
|
|
|
|
|
|
knexJoinFns.push((qry, cpgListId) => qry.leftJoin('campaign_messages AS ' + alias, getConds(alias, cpgListId)));
|
|
|
|
|
|
|
|
} else if (type === 'links') {
|
|
|
|
const alias = 'campaign_links_' + prefix;
|
|
|
|
|
|
|
|
commonFieldsMapping[`${prefix}:count`] = {raw: 'COALESCE(`' + alias + '`.`count`, 0)'};
|
|
|
|
commonFieldsMapping[`${prefix}:link`] = alias + '.link';
|
|
|
|
commonFieldsMapping[`${prefix}:country`] = alias + '.country';
|
|
|
|
commonFieldsMapping[`${prefix}:deviceType`] = alias + '.device_type';
|
2019-09-05 13:51:17 +00:00
|
|
|
commonFieldsMapping[`${prefix}:ip`] = alias + '.ip';
|
|
|
|
commonFieldsMapping[`${prefix}:created`] = alias + '.created';
|
2019-04-22 20:46:48 +00:00
|
|
|
|
|
|
|
knexJoinFns.push((qry, cpgListId) => qry.leftJoin('campaign_links AS ' + alias, getConds(alias, cpgListId)));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
throw new Error(`Unknown join type "${type}"`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const listsFields = {};
|
|
|
|
const permittedListFields = new Set();
|
2018-12-21 18:09:18 +00:00
|
|
|
let firstIteration = true;
|
|
|
|
for (const cpgList of campaign.lists) {
|
|
|
|
const cpgListId = cpgList.list;
|
|
|
|
|
2019-04-22 20:46:48 +00:00
|
|
|
const listFields = {};
|
|
|
|
listsFields[cpgListId] = listFields;
|
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
const flds = await fields.list(contextHelpers.getAdminContext(), cpgListId);
|
|
|
|
|
|
|
|
const assignedFlds = new Set();
|
|
|
|
|
|
|
|
for (const fld of flds) {
|
|
|
|
/* Dropdown and checkbox groups have field.column == null
|
|
|
|
For the time being, we don't group options and we don't expand enums. We just provide it as it is in the DB. */
|
|
|
|
if (fld.column) {
|
|
|
|
const fldKey = 'field:' + fld.key.toLowerCase();
|
2019-04-22 20:46:48 +00:00
|
|
|
|
|
|
|
listFields[fldKey] = 'subscriptions.' + fld.column;
|
|
|
|
|
|
|
|
if (firstIteration) {
|
|
|
|
permittedListFields.add(fldKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (permittedListFields.has(fldKey)) {
|
2018-12-21 18:09:18 +00:00
|
|
|
assignedFlds.add(fldKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-07-09 21:16:47 +00:00
|
|
|
|
2019-04-22 20:46:48 +00:00
|
|
|
for (const fldKey in [...permittedListFields]) {
|
2018-12-21 18:09:18 +00:00
|
|
|
if (!assignedFlds.has(fldKey)) {
|
2019-04-22 20:46:48 +00:00
|
|
|
permittedListFields.delete(fldKey);
|
2018-12-21 18:09:18 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-13 11:27:03 +00:00
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
firstIteration = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const cpgList of campaign.lists) {
|
|
|
|
const cpgListId = cpgList.list;
|
2019-04-22 20:46:48 +00:00
|
|
|
const listFields = listsFields[cpgListId];
|
2018-12-21 18:09:18 +00:00
|
|
|
|
2019-04-22 20:46:48 +00:00
|
|
|
for (const fldKey in listFields) {
|
|
|
|
if (!permittedListFields.has(fldKey)) {
|
|
|
|
delete listFields[fldKey];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const cpgList of campaign.lists) {
|
|
|
|
const cpgListId = cpgList.list;
|
2018-12-21 18:09:18 +00:00
|
|
|
|
|
|
|
const fieldsMapping = {
|
|
|
|
...commonFieldsMapping,
|
2019-04-22 20:46:48 +00:00
|
|
|
...listsFields[cpgListId],
|
|
|
|
'list:id': {raw: knex.raw('?', [cpgListId])}
|
2018-12-21 18:09:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const getSelField = item => {
|
|
|
|
const itemMapping = fieldsMapping[item];
|
|
|
|
if (typeof itemMapping === 'string') {
|
2019-04-22 20:46:48 +00:00
|
|
|
return itemMapping + ' AS ' + item;
|
2018-12-21 18:09:18 +00:00
|
|
|
} else if (itemMapping.raw) {
|
|
|
|
return knex.raw(fieldsMapping[item].raw + ' AS `' + item + '`');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let selFields = [];
|
|
|
|
for (let idx = 0; idx < select.length; idx++) {
|
|
|
|
const item = select[idx];
|
|
|
|
if (item in fieldsMapping) {
|
|
|
|
selFields.push(getSelField(item));
|
|
|
|
} else if (item === '*') {
|
|
|
|
selFields = selFields.concat(Object.keys(fieldsMapping).map(entry => getSelField(entry)));
|
|
|
|
} else {
|
|
|
|
selFields.push(item);
|
|
|
|
}
|
|
|
|
}
|
2017-07-13 11:27:03 +00:00
|
|
|
|
2019-04-22 20:46:48 +00:00
|
|
|
let query = knex(`subscription__${cpgListId} AS subscriptions`).select(selFields);
|
|
|
|
|
|
|
|
for (const knexJoinFn of knexJoinFns) {
|
|
|
|
query = knexJoinFn(query, cpgListId);
|
|
|
|
}
|
2018-12-21 18:09:18 +00:00
|
|
|
|
|
|
|
if (listQryFn) {
|
2019-04-22 00:41:40 +00:00
|
|
|
query = listQryFn(
|
|
|
|
query,
|
2019-04-22 20:46:48 +00:00
|
|
|
colId => {
|
|
|
|
if (colId in fieldsMapping) {
|
|
|
|
return fieldsMapping[colId];
|
|
|
|
} else {
|
|
|
|
throw new Error(`Unknown column id ${colId}`);
|
|
|
|
}
|
|
|
|
}
|
2019-04-22 00:41:40 +00:00
|
|
|
);
|
2017-07-13 11:27:03 +00:00
|
|
|
}
|
2018-12-21 18:09:18 +00:00
|
|
|
|
|
|
|
subsQrys.push(query.toSQL().toNative());
|
2017-08-13 18:11:58 +00:00
|
|
|
}
|
2017-07-13 11:27:03 +00:00
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
if (subsQrys.length > 0) {
|
|
|
|
let subsSql, subsBindings;
|
|
|
|
|
2019-04-22 20:46:48 +00:00
|
|
|
const fieldsSet = new Set([...Object.keys(commonFieldsMapping), ...permittedListFields, 'list:id']);
|
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
const applyUnionQryFn = (subsSql, subsBindings) => {
|
|
|
|
if (unionQryFn) {
|
|
|
|
return unionQryFn(
|
|
|
|
knex.from(function() {
|
|
|
|
return knex.raw('(' + subsSql + ')', subsBindings);
|
2019-04-22 00:41:40 +00:00
|
|
|
}),
|
2019-04-22 20:46:48 +00:00
|
|
|
colId => {
|
|
|
|
if (fieldsSet.has(colId)) {
|
|
|
|
return colId;
|
|
|
|
} else {
|
|
|
|
throw new Error(`Unknown column id ${colId}`);
|
|
|
|
}
|
|
|
|
}
|
2018-12-21 18:09:18 +00:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return knex.raw(subsSql, subsBindings);
|
|
|
|
}
|
2018-12-23 19:27:29 +00:00
|
|
|
};
|
2018-12-21 18:09:18 +00:00
|
|
|
|
|
|
|
if (subsQrys.length === 1) {
|
|
|
|
subsSql = subsQrys[0].sql;
|
|
|
|
subsBindings = subsQrys[0].bindings;
|
2017-07-13 11:27:03 +00:00
|
|
|
} else {
|
2018-12-21 18:09:18 +00:00
|
|
|
subsSql = subsQrys.map(qry => '(' + qry.sql + ')').join(' UNION ALL ');
|
|
|
|
subsBindings = Array.prototype.concat(...subsQrys.map(qry => qry.bindings));
|
2018-12-23 19:27:29 +00:00
|
|
|
}
|
2018-12-21 18:09:18 +00:00
|
|
|
|
2018-12-23 19:27:29 +00:00
|
|
|
if (asStream) {
|
|
|
|
return applyUnionQryFn(subsSql, subsBindings).stream();
|
2019-04-22 00:41:40 +00:00
|
|
|
|
2018-12-23 19:27:29 +00:00
|
|
|
} else {
|
|
|
|
const res = await applyUnionQryFn(subsSql, subsBindings);
|
|
|
|
if (res[0] && Array.isArray(res[0])) {
|
|
|
|
return res[0]; // UNION ALL generates an array with result and schema
|
2018-12-21 18:09:18 +00:00
|
|
|
} else {
|
2018-12-23 19:27:29 +00:00
|
|
|
return res;
|
2018-12-21 18:09:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (asStream) {
|
|
|
|
const result = new Readable({
|
|
|
|
objectMode: true,
|
|
|
|
});
|
|
|
|
result.push(null);
|
|
|
|
return result;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return [];
|
2017-07-13 11:27:03 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-21 18:09:18 +00:00
|
|
|
}
|
2017-07-13 11:27:03 +00:00
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
async function _getCampaignOpenStatistics(campaign, select, unionQryFn, listQryFn, asStream) {
|
|
|
|
if (!listQryFn) {
|
|
|
|
listQryFn = qry => qry;
|
|
|
|
}
|
2017-07-13 11:27:03 +00:00
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
return await _getCampaignStatistics(
|
|
|
|
campaign,
|
|
|
|
select,
|
2019-04-22 20:46:48 +00:00
|
|
|
[{type: 'messages', prefix: 'tracker'}, {type: 'links', prefix: 'tracker'}],
|
2018-12-21 18:09:18 +00:00
|
|
|
unionQryFn,
|
2019-04-22 00:41:40 +00:00
|
|
|
(qry, col) => listQryFn(
|
2018-12-21 18:09:18 +00:00
|
|
|
qry.where(function() {
|
2019-04-22 20:46:48 +00:00
|
|
|
this.whereNull(col('tracker:link')).orWhere(col('tracker:link'), LinkId.OPEN)
|
2019-04-22 00:41:40 +00:00
|
|
|
}),
|
|
|
|
col
|
2018-12-21 18:09:18 +00:00
|
|
|
),
|
|
|
|
asStream
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function _getCampaignClickStatistics(campaign, select, unionQryFn, listQryFn) {
|
|
|
|
if (!listQryFn) {
|
|
|
|
listQryFn = qry => qry;
|
2017-07-13 11:27:03 +00:00
|
|
|
}
|
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
return await _getCampaignStatistics(
|
|
|
|
campaign,
|
|
|
|
select,
|
2019-04-22 20:46:48 +00:00
|
|
|
[{type: 'messages', prefix: 'tracker'}, {type: 'links', prefix: 'tracker'}],
|
2018-12-21 18:09:18 +00:00
|
|
|
unionQryFn,
|
2019-04-22 00:41:40 +00:00
|
|
|
(qry, col) => listQryFn(
|
2018-12-21 18:09:18 +00:00
|
|
|
qry.where(function() {
|
2019-04-22 20:46:48 +00:00
|
|
|
this.whereNull(col('tracker:link')).orWhere(col('tracker:link'), LinkId.GENERAL_CLICK)
|
2019-04-22 00:41:40 +00:00
|
|
|
}),
|
|
|
|
col
|
2018-12-21 18:09:18 +00:00
|
|
|
),
|
|
|
|
asStream
|
|
|
|
);
|
2017-07-13 11:27:03 +00:00
|
|
|
}
|
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
async function _getCampaignLinkClickStatistics(campaign, select, unionQryFn, listQryFn) {
|
|
|
|
if (!listQryFn) {
|
|
|
|
listQryFn = qry => qry;
|
|
|
|
}
|
|
|
|
|
|
|
|
return await _getCampaignStatistics(
|
|
|
|
campaign,
|
|
|
|
select,
|
2019-04-22 20:46:48 +00:00
|
|
|
[{type: 'messages', prefix: 'tracker'}, {type: 'links', prefix: 'tracker'}],
|
2018-12-21 18:09:18 +00:00
|
|
|
unionQryFn,
|
2019-04-22 00:41:40 +00:00
|
|
|
(qry, col) => listQryFn(
|
2018-12-21 18:09:18 +00:00
|
|
|
qry.where(function() {
|
2019-04-22 20:46:48 +00:00
|
|
|
this.whereNull(col('tracker:link')).orWhere(col('tracker:link'), '>', LinkId.GENERAL_CLICK)
|
2019-04-22 00:41:40 +00:00
|
|
|
}),
|
|
|
|
col
|
2018-12-21 18:09:18 +00:00
|
|
|
),
|
|
|
|
asStream
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-04-22 20:46:48 +00:00
|
|
|
async function getCampaignStatistics(campaign, select, joins, unionQryFn, listQryFn) {
|
|
|
|
return await _getCampaignStatistics(campaign, select, joins, unionQryFn, listQryFn, false);
|
2019-04-22 00:41:40 +00:00
|
|
|
}
|
|
|
|
|
2019-04-22 20:46:48 +00:00
|
|
|
async function getCampaignStatisticsStream(campaign, select, joins, unionQryFn, listQryFn) {
|
|
|
|
return await _getCampaignStatistics(campaign, select, joins, unionQryFn, listQryFn, true);
|
2019-04-22 00:41:40 +00:00
|
|
|
}
|
|
|
|
|
2018-12-21 18:09:18 +00:00
|
|
|
async function getCampaignOpenStatistics(campaign, select, unionQryFn, listQryFn) {
|
|
|
|
return await _getCampaignOpenStatistics(campaign, select, unionQryFn, listQryFn, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getCampaignOpenStatisticsStream(campaign, select, unionQryFn, listQryFn) {
|
|
|
|
return await _getCampaignOpenStatistics(campaign, select, unionQryFn, listQryFn, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getCampaignClickStatistics(campaign, select, unionQryFn, listQryFn) {
|
|
|
|
return await _getCampaignClickStatistics(campaign, select, unionQryFn, listQryFn, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getCampaignClickStatisticsStream(campaign, select, unionQryFn, listQryFn) {
|
|
|
|
return await _getCampaignClickStatistics(campaign, select, unionQryFn, listQryFn, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getCampaignLinkClickStatistics(campaign, select, unionQryFn, listQryFn) {
|
|
|
|
return await _getCampaignLinkClickStatistics(campaign, select, unionQryFn, listQryFn, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getCampaignLinkClickStatisticsStream(campaign, select, unionQryFn, listQryFn) {
|
|
|
|
return await _getCampaignLinkClickStatistics(campaign, select, unionQryFn, listQryFn, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-13 11:27:03 +00:00
|
|
|
|
2017-07-09 21:16:47 +00:00
|
|
|
|
2018-09-09 22:55:44 +00:00
|
|
|
module.exports.ReportState = ReportState;
|
|
|
|
module.exports.hash = hash;
|
|
|
|
module.exports.getByIdWithTemplate = getByIdWithTemplate;
|
|
|
|
module.exports.listDTAjax = listDTAjax;
|
|
|
|
module.exports.create = create;
|
|
|
|
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
|
|
|
|
module.exports.remove = remove;
|
|
|
|
module.exports.updateFields = updateFields;
|
|
|
|
module.exports.listByState = listByState;
|
|
|
|
module.exports.bulkChangeState = bulkChangeState;
|
2019-04-22 20:46:48 +00:00
|
|
|
module.exports.getCampaignCommonListFields = getCampaignCommonListFields;
|
2019-04-22 00:41:40 +00:00
|
|
|
module.exports.getCampaignStatistics = getCampaignStatistics;
|
|
|
|
module.exports.getCampaignStatisticsStream = getCampaignStatisticsStream;
|
2018-12-21 18:09:18 +00:00
|
|
|
module.exports.getCampaignOpenStatistics = getCampaignOpenStatistics;
|
|
|
|
module.exports.getCampaignClickStatistics = getCampaignClickStatistics;
|
|
|
|
module.exports.getCampaignLinkClickStatistics = getCampaignLinkClickStatistics;
|
|
|
|
module.exports.getCampaignOpenStatisticsStream = getCampaignOpenStatisticsStream;
|
|
|
|
module.exports.getCampaignClickStatisticsStream = getCampaignClickStatisticsStream;
|
|
|
|
module.exports.getCampaignLinkClickStatisticsStream = getCampaignLinkClickStatisticsStream;
|