WiP on admin interface for subscribers.
TODO: - format data based on field info in listDTAjax - integrate with the whole subscription machinery
This commit is contained in:
parent
e6bd9cd943
commit
6f5b50e932
38 changed files with 1233 additions and 181 deletions
107
models/fields.js
107
models/fields.js
|
@ -18,55 +18,82 @@ const hashKeys = allowedKeysCreate;
|
|||
|
||||
const fieldTypes = {};
|
||||
|
||||
const Cardinality = {
|
||||
SINGLE: 0,
|
||||
MULTIPLE: 1
|
||||
};
|
||||
|
||||
fieldTypes.text = fieldTypes.website = {
|
||||
validate: entity => {},
|
||||
addColumn: (table, name) => table.string(name),
|
||||
indexed: true,
|
||||
grouped: false
|
||||
grouped: false,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.SINGLE
|
||||
};
|
||||
|
||||
fieldTypes.longtext = fieldTypes.gpg = {
|
||||
validate: entity => {},
|
||||
addColumn: (table, name) => table.text(name),
|
||||
indexed: false,
|
||||
grouped: false
|
||||
grouped: false,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.SINGLE
|
||||
};
|
||||
|
||||
fieldTypes.json = {
|
||||
validate: entity => {},
|
||||
addColumn: (table, name) => table.json(name),
|
||||
indexed: false,
|
||||
grouped: false
|
||||
grouped: false,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.SINGLE
|
||||
};
|
||||
|
||||
fieldTypes.number = {
|
||||
validate: entity => {},
|
||||
addColumn: (table, name) => table.integer(name),
|
||||
indexed: true,
|
||||
grouped: false
|
||||
grouped: false,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.SINGLE
|
||||
};
|
||||
|
||||
fieldTypes.checkbox = fieldTypes['radio-grouped'] = fieldTypes['dropdown-grouped'] = {
|
||||
fieldTypes['checkbox-grouped'] = {
|
||||
validate: entity => {},
|
||||
indexed: true,
|
||||
grouped: true
|
||||
grouped: true,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.MULTIPLE
|
||||
};
|
||||
|
||||
fieldTypes['radio-grouped'] = fieldTypes['dropdown-grouped'] = {
|
||||
validate: entity => {},
|
||||
indexed: true,
|
||||
grouped: true,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.SINGLE
|
||||
};
|
||||
|
||||
fieldTypes['radio-enum'] = fieldTypes['dropdown-enum'] = {
|
||||
validate: entity => {
|
||||
enforce(entity.settings.options, 'Options missing in settings');
|
||||
enforce(Object.keys(entity.settings.options).includes(entity.default_value), 'Default value not present in options');
|
||||
enforce(entity.default_value === null || entity.settings.options.find(x => x.key === entity.default_value), 'Default value not present in options');
|
||||
},
|
||||
addColumn: (table, name) => table.string(name),
|
||||
indexed: true,
|
||||
grouped: false
|
||||
grouped: false,
|
||||
enumerated: true,
|
||||
cardinality: Cardinality.SINGLE
|
||||
};
|
||||
|
||||
fieldTypes.option = {
|
||||
validate: entity => {},
|
||||
addColumn: (table, name) => table.boolean(name),
|
||||
indexed: true,
|
||||
grouped: false
|
||||
grouped: false,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.SINGLE
|
||||
};
|
||||
|
||||
fieldTypes['date'] = fieldTypes['birthday'] = {
|
||||
|
@ -75,11 +102,17 @@ fieldTypes['date'] = fieldTypes['birthday'] = {
|
|||
},
|
||||
addColumn: (table, name) => table.dateTime(name),
|
||||
indexed: true,
|
||||
grouped: false
|
||||
grouped: false,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.SINGLE
|
||||
};
|
||||
|
||||
const groupedTypes = Object.keys(fieldTypes).filter(key => fieldTypes[key].grouped);
|
||||
|
||||
function getFieldType(type) {
|
||||
return fieldTypes[type];
|
||||
}
|
||||
|
||||
function hash(entity) {
|
||||
return hasher.hash(filterObject(entity, hashKeys));
|
||||
}
|
||||
|
@ -90,6 +123,8 @@ async function getById(context, listId, id) {
|
|||
|
||||
const entity = await tx('custom_fields').where({list: listId, id}).first();
|
||||
|
||||
entity.settings = JSON.parse(entity.settings);
|
||||
|
||||
const orderFields = {
|
||||
order_list: 'orderListBefore',
|
||||
order_subscribe: 'orderSubscribeBefore',
|
||||
|
@ -114,16 +149,55 @@ async function getById(context, listId, id) {
|
|||
}
|
||||
|
||||
async function listTx(tx, listId) {
|
||||
return await tx('custom_fields').where({list: listId}).select(['id', 'name', 'type', 'key', 'column', 'order_list', 'order_subscribe', 'order_manage']).orderBy('id', 'asc');
|
||||
return await tx('custom_fields').where({list: listId}).select(['id', 'name', 'type', 'key', 'column', 'order_list', 'settings', 'group', 'order_subscribe', 'order_manage']).orderBy(knex.raw('-order_list'), 'desc').orderBy('id', 'asc');
|
||||
}
|
||||
|
||||
async function list(context, listId) {
|
||||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageFields');
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, ['manageFields', 'manageSegments']);
|
||||
return await listTx(tx, listId);
|
||||
});
|
||||
}
|
||||
|
||||
async function listGroupedTx(tx, listId) {
|
||||
const flds = await tx('custom_fields').where({list: listId}).select(['id', 'name', 'type', 'column', 'settings', 'group', 'default_value']).orderBy(knex.raw('-order_list'), 'desc').orderBy('id', 'asc');
|
||||
|
||||
const fldsById = {};
|
||||
for (const fld of flds) {
|
||||
fld.settings = JSON.parse(fld.settings);
|
||||
|
||||
fldsById[fld.id] = fld;
|
||||
|
||||
if (fieldTypes[fld.type].grouped) {
|
||||
fld.settings.options = [];
|
||||
fld.groupedOptions = {};
|
||||
}
|
||||
}
|
||||
|
||||
for (const fld of flds) {
|
||||
if (fld.group) {
|
||||
const group = fldsById[fld.group];
|
||||
group.settings.options.push({ key: fld.column, label: fld.name });
|
||||
group.groupedOptions[fld.column] = fld;
|
||||
}
|
||||
}
|
||||
|
||||
const groupedFlds = flds.filter(fld => !fld.group);
|
||||
|
||||
for (const fld of flds) {
|
||||
delete fld.group;
|
||||
}
|
||||
|
||||
return groupedFlds;
|
||||
}
|
||||
|
||||
async function listGrouped(context, listId) {
|
||||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, ['manageSubscriptions']);
|
||||
return await listGroupedTx(tx, listId);
|
||||
});
|
||||
}
|
||||
|
||||
async function listByOrderListTx(tx, listId, extraColumns = []) {
|
||||
return await tx('custom_fields').where({list: listId}).whereNotNull('order_list').select(['name', ...extraColumns]).orderBy('order_list', 'asc');
|
||||
}
|
||||
|
@ -241,7 +315,7 @@ async function _validateAndPreprocess(tx, listId, entity, isCreate) {
|
|||
|
||||
enforce(validators.mergeTagValid(entity.key), 'Merge tag is not valid.');
|
||||
|
||||
const existingWithKeyQuery = knex('custom_fields').where({
|
||||
const existingWithKeyQuery = tx('custom_fields').where({
|
||||
list: listId,
|
||||
key: entity.key
|
||||
});
|
||||
|
@ -349,6 +423,7 @@ async function updateWithConsistencyCheck(context, listId, entity) {
|
|||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
|
||||
existing.settings = JSON.parse(existing.settings);
|
||||
const existingHash = hash(existing);
|
||||
if (existingHash !== entity.originalHash) {
|
||||
throw new interoperableErrors.ChangedError();
|
||||
|
@ -357,7 +432,7 @@ async function updateWithConsistencyCheck(context, listId, entity) {
|
|||
enforce(entity.type === existing.type, 'Field type cannot be changed');
|
||||
await _validateAndPreprocess(tx, listId, entity, false);
|
||||
|
||||
await tx('custom_fields').where('id', entity.id).update(filterObject(entity, allowedKeysUpdate));
|
||||
await tx('custom_fields').where({list: listId, id: entity.id}).update(filterObject(entity, allowedKeysUpdate));
|
||||
await _sortIn(tx, listId, entity.id, entity.orderListBefore, entity.orderSubscribeBefore, entity.orderManageBefore);
|
||||
});
|
||||
}
|
||||
|
@ -401,10 +476,14 @@ async function removeAllByListIdTx(tx, context, listId) {
|
|||
|
||||
// This is to handle circular dependency with segments.js
|
||||
Object.assign(module.exports, {
|
||||
Cardinality,
|
||||
getFieldType,
|
||||
hash,
|
||||
getById,
|
||||
list,
|
||||
listTx,
|
||||
listGrouped,
|
||||
listGroupedTx,
|
||||
listByOrderListTx,
|
||||
listDTAjax,
|
||||
listGroupedDTAjax,
|
||||
|
|
|
@ -214,7 +214,7 @@ function hash(entity) {
|
|||
|
||||
async function listDTAjax(context, listId, params) {
|
||||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, ['viewSubscriptions', 'manageSegments']);
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageSegments');
|
||||
|
||||
return await dtHelpers.ajaxListTx(
|
||||
tx,
|
||||
|
@ -227,7 +227,7 @@ async function listDTAjax(context, listId, params) {
|
|||
});
|
||||
}
|
||||
|
||||
async function list(context, listId) {
|
||||
async function listIdName(context, listId) {
|
||||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, ['viewSubscriptions', 'manageSegments']);
|
||||
|
||||
|
@ -237,7 +237,7 @@ async function list(context, listId) {
|
|||
|
||||
async function getById(context, listId, id) {
|
||||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, ['viewSubscriptions', 'manageSegments']);
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageSegments');
|
||||
const entity = await tx('segments').where({id, list: listId}).first();
|
||||
entity.settings = JSON.parse(entity.settings);
|
||||
return entity;
|
||||
|
@ -400,7 +400,7 @@ async function getQueryGeneratorTx(tx, listId, id) {
|
|||
Object.assign(module.exports, {
|
||||
hash,
|
||||
listDTAjax,
|
||||
list,
|
||||
listIdName,
|
||||
getById,
|
||||
create,
|
||||
updateWithConsistencyCheck,
|
||||
|
|
|
@ -1,24 +1,149 @@
|
|||
'use strict';
|
||||
|
||||
const knex = require('../lib/knex');
|
||||
const hasher = require('node-object-hash')();
|
||||
const shortid = require('shortid');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const fields = require('./fields');
|
||||
const { SubscriptionStatus } = require('../shared/lists');
|
||||
const { SubscriptionStatus, getFieldKey } = require('../shared/lists');
|
||||
const segments = require('./segments');
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const moment = require('moment');
|
||||
|
||||
const allowedKeysBase = new Set(['email', 'tz', 'is_test', 'status']);
|
||||
|
||||
function getTableName(listId) {
|
||||
return `subscription__${listId}`;
|
||||
}
|
||||
|
||||
async function getGroupedFieldsMap(tx, listId) {
|
||||
const groupedFields = await fields.listGroupedTx(tx, listId);
|
||||
const result = {};
|
||||
for (const fld of groupedFields) {
|
||||
result[getFieldKey(fld)] = fld;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function groupSubscription(groupedFieldsMap, entity) {
|
||||
for (const fldKey in groupedFieldsMap) {
|
||||
const fld = groupedFieldsMap[fldKey];
|
||||
const fieldType = fields.getFieldType(fld.type);
|
||||
|
||||
if (fieldType.grouped) {
|
||||
|
||||
let value = null;
|
||||
|
||||
if (fieldType.cardinality === fields.Cardinality.SINGLE) {
|
||||
for (const optionKey in fld.groupedOptions) {
|
||||
const option = fld.groupedOptions[optionKey];
|
||||
|
||||
if (entity[option.column]) {
|
||||
value = option.column;
|
||||
}
|
||||
|
||||
delete entity[option.column];
|
||||
}
|
||||
|
||||
} else {
|
||||
value = [];
|
||||
for (const optionKey in fld.groupedOptions) {
|
||||
const option = fld.groupedOptions[optionKey];
|
||||
|
||||
if (entity[option.column]) {
|
||||
value.push(option.column);
|
||||
}
|
||||
|
||||
delete entity[option.column];
|
||||
}
|
||||
}
|
||||
|
||||
entity[fldKey] = value;
|
||||
|
||||
} else if (fieldType.enumerated) {
|
||||
// This is enum-xxx type. We just make sure that the options we give out match the field settings.
|
||||
// If the field settings gets changed, there can be discrepancies between the field and the subscription data.
|
||||
|
||||
const allowedKeys = new Set(fld.settings.options.map(x => x.key));
|
||||
|
||||
if (!allowedKeys.has(entity[fldKey])) {
|
||||
entity[fldKey] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ungroupSubscription(groupedFieldsMap, entity) {
|
||||
for (const fldKey in groupedFieldsMap) {
|
||||
const fld = groupedFieldsMap[fldKey];
|
||||
|
||||
const fieldType = fields.getFieldType(fld.type);
|
||||
if (fieldType.grouped) {
|
||||
|
||||
if (fieldType.cardinality === fields.Cardinality.SINGLE) {
|
||||
const value = entity[fldKey];
|
||||
for (const optionKey in fld.groupedOptions) {
|
||||
const option = fld.groupedOptions[optionKey];
|
||||
entity[option.column] = option.column === value;
|
||||
}
|
||||
|
||||
} else {
|
||||
const values = entity[fldKey];
|
||||
for (const optionKey in fld.groupedOptions) {
|
||||
const option = fld.groupedOptions[optionKey];
|
||||
entity[option.column] = values.includes(option.column);
|
||||
}
|
||||
}
|
||||
|
||||
delete entity[fldKey];
|
||||
|
||||
} else if (fieldType.enumerated) {
|
||||
// This is enum-xxx type. We just make sure that the options we give out match the field settings.
|
||||
// If the field settings gets changed, there can be discrepancies between the field and the subscription data.
|
||||
|
||||
const allowedKeys = new Set(fld.settings.options.map(x => x.key));
|
||||
|
||||
if (!allowedKeys.has(entity[fldKey])) {
|
||||
entity[fldKey] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const allowedKeysBase = new Set(['cid', 'email']);
|
||||
|
||||
function hash(entity) {
|
||||
const allowedKeys = allowedKeysBase.slice();
|
||||
|
||||
// TODO add keys from custom fields
|
||||
function getAllowedKeys(groupedFieldsMap) {
|
||||
return new Set([
|
||||
...allowedKeysBase,
|
||||
...Object.keys(groupedFieldsMap)
|
||||
]);
|
||||
}
|
||||
|
||||
function hashByAllowedKeys(allowedKeys, entity) {
|
||||
return hasher.hash(filterObject(entity, allowedKeys));
|
||||
}
|
||||
|
||||
async function hashByList(listId, entity) {
|
||||
return await knex.transaction(async tx => {
|
||||
const groupedFieldsMap = await getGroupedFieldsMap(tx, listId);
|
||||
const allowedKeys = getAllowedKeys(groupedFieldsMap);
|
||||
return hashByAllowedKeys(allowedKeys, entity);
|
||||
});
|
||||
}
|
||||
|
||||
async function getById(context, listId, id) {
|
||||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'viewSubscriptions');
|
||||
|
||||
const entity = await tx(getTableName(listId)).where('id', id).first();
|
||||
|
||||
const groupedFieldsMap = await getGroupedFieldsMap(tx, listId);
|
||||
groupSubscription(groupedFieldsMap, entity);
|
||||
|
||||
return entity;
|
||||
});
|
||||
}
|
||||
|
||||
async function listDTAjax(context, listId, segmentId, params) {
|
||||
return await knex.transaction(async tx => {
|
||||
|
@ -31,13 +156,14 @@ async function listDTAjax(context, listId, segmentId, params) {
|
|||
tx,
|
||||
params,
|
||||
builder => {
|
||||
const query = builder.from(`subscription__${listId}`);
|
||||
const query = builder.from(getTableName(listId));
|
||||
query.where(function() {
|
||||
addSegmentQuery(this);
|
||||
});
|
||||
return query;
|
||||
},
|
||||
['id', 'cid', 'email', 'status', 'created', ...flds.map(fld => fld.column)]
|
||||
// FIXME - adapt data in custom columns to render them properly
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -46,12 +172,189 @@ async function list(context, listId) {
|
|||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'viewSubscriptions');
|
||||
|
||||
return await tx(`subscription__${listId}`);
|
||||
const entities = await tx(getTableName(listId));
|
||||
|
||||
const groupedFieldsMap = await getGroupedFieldsMap(tx, listId);
|
||||
|
||||
for (const entity of entities) {
|
||||
groupSubscription(groupedFieldsMap, entity);
|
||||
}
|
||||
|
||||
return entities;
|
||||
});
|
||||
}
|
||||
|
||||
async function serverValidate(context, listId, data) {
|
||||
return await knex.transaction(async tx => {
|
||||
const result = {};
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageSubscriptions');
|
||||
|
||||
if (data.email) {
|
||||
const existingKeyQuery = tx(getTableName(listId)).where('email', data.email);
|
||||
|
||||
if (data.id) {
|
||||
existingKeyQuery.whereNot('id', data.id);
|
||||
}
|
||||
|
||||
const existingKey = await existingKeyQuery.first();
|
||||
result.key = {
|
||||
exists: !!existingKey
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
async function _validateAndPreprocess(tx, listId, groupedFieldsMap, entity, isCreate) {
|
||||
enforce(entity.email, 'Email must be set');
|
||||
|
||||
const existingWithKeyQuery = tx(getTableName(listId)).where('email', entity.email);
|
||||
|
||||
if (!isCreate) {
|
||||
existingWithKeyQuery.whereNot('id', entity.id);
|
||||
}
|
||||
const existingWithKey = await existingWithKeyQuery.first();
|
||||
if (existingWithKey) {
|
||||
throw new interoperableErrors.DuplicitEmailError();
|
||||
}
|
||||
|
||||
enforce(entity.status >= 0 && entity.status < SubscriptionStatus.MAX, 'Invalid status');
|
||||
|
||||
for (const key in groupedFieldsMap) {
|
||||
const fld = groupedFieldsMap[key];
|
||||
if (fld.type === 'date' || fld.type === 'birthday') {
|
||||
entity[getFieldKey(fld)] = moment(entity[getFieldKey(fld)]).toDate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function create(context, listId, entity) {
|
||||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageSubscriptions');
|
||||
|
||||
const groupedFieldsMap = await getGroupedFieldsMap(tx, listId);
|
||||
const allowedKeys = getAllowedKeys(groupedFieldsMap);
|
||||
|
||||
await _validateAndPreprocess(tx, listId, groupedFieldsMap, entity, true);
|
||||
|
||||
const filteredEntity = filterObject(entity, allowedKeys);
|
||||
filteredEntity.cid = shortid.generate();
|
||||
filteredEntity.status_change = new Date();
|
||||
|
||||
ungroupSubscription(groupedFieldsMap, filteredEntity);
|
||||
|
||||
// FIXME - process:
|
||||
// filteredEntity.opt_in_ip =
|
||||
// filteredEntity.opt_in_country =
|
||||
// filteredEntity.imported =
|
||||
|
||||
const ids = await tx(getTableName(listId)).insert(filteredEntity);
|
||||
const id = ids[0];
|
||||
|
||||
if (entity.status === SubscriptionStatus.SUBSCRIBED) {
|
||||
await tx('lists').where('id', listId).increment('subscribers', 1);
|
||||
}
|
||||
|
||||
return id;
|
||||
});
|
||||
}
|
||||
|
||||
async function updateWithConsistencyCheck(context, listId, entity) {
|
||||
await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageSubscriptions');
|
||||
|
||||
const existing = await tx(getTableName(listId)).where('id', entity.id).first();
|
||||
if (!existing) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
|
||||
const groupedFieldsMap = await getGroupedFieldsMap(tx, listId);
|
||||
const allowedKeys = getAllowedKeys(groupedFieldsMap);
|
||||
|
||||
groupSubscription(groupedFieldsMap, existing);
|
||||
|
||||
const existingHash = hashByAllowedKeys(allowedKeys, existing);
|
||||
if (existingHash !== entity.originalHash) {
|
||||
throw new interoperableErrors.ChangedError();
|
||||
}
|
||||
|
||||
await _validateAndPreprocess(tx, listId, groupedFieldsMap, entity, false);
|
||||
|
||||
const filteredEntity = filterObject(entity, allowedKeys);
|
||||
|
||||
ungroupSubscription(groupedFieldsMap, filteredEntity);
|
||||
|
||||
if (existing.status !== entity.status) {
|
||||
filteredEntity.status_change = new Date();
|
||||
}
|
||||
|
||||
await tx(getTableName(listId)).where('id', entity.id).update(filteredEntity);
|
||||
|
||||
|
||||
let countIncrement = 0;
|
||||
if (existing.status === SubscriptionStatus.SUBSCRIBED && entity.status !== SubscriptionStatus.SUBSCRIBED) {
|
||||
countIncrement = -1;
|
||||
} else if (existing.status !== SubscriptionStatus.SUBSCRIBED && entity.status === SubscriptionStatus.SUBSCRIBED) {
|
||||
countIncrement = 1;
|
||||
}
|
||||
|
||||
if (countIncrement) {
|
||||
await tx('lists').where('id', listId).increment('subscribers', countIncrement);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function removeTx(tx, context, listId, id) {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageSubscriptions');
|
||||
|
||||
const existing = await tx(getTableName(listId)).where('id', id).first();
|
||||
if (!existing) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
|
||||
await tx(getTableName(listId)).where('id', id).del();
|
||||
|
||||
if (existing.status === SubscriptionStatus.SUBSCRIBED) {
|
||||
await tx('lists').where('id', listId).decrement('subscribers', 1);
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(context, listId, id) {
|
||||
await knex.transaction(async tx => {
|
||||
await removeTx(tx, context, listId, id);
|
||||
});
|
||||
}
|
||||
|
||||
async function unsubscribe(context, listId, id) {
|
||||
await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageSubscriptions');
|
||||
|
||||
const existing = await tx(getTableName(listId)).where('id', id).first();
|
||||
if (!existing) {
|
||||
throw new interoperableErrors.NotFoundError();
|
||||
}
|
||||
|
||||
if (existing.status === SubscriptionStatus.SUBSCRIBED) {
|
||||
await tx(getTableName(listId)).where('id', id).update({
|
||||
status: SubscriptionStatus.UNSUBSCRIBED
|
||||
});
|
||||
|
||||
await tx('lists').where('id', listId).decrement('subscribers', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
hashByList,
|
||||
getById,
|
||||
list,
|
||||
listDTAjax
|
||||
listDTAjax,
|
||||
serverValidate,
|
||||
create,
|
||||
updateWithConsistencyCheck,
|
||||
remove,
|
||||
unsubscribe
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue