Ghost/core/server/models/base/plugins/bulk-operations.js
Fabien 'egg' O'Carroll 1dd520754d
Added bulkEdit to bulk-operations bookshelf plugin (#13223)
refs https://github.com/TryGhost/Team/issues/946

This refactor pulls out the core logic so that we can easily add other
bulk operations without having to duplicate even more logic.

It also gives a consistent return value between bulk operations, renaming
`unsuccessfulIds` and `unsuccessfulRecords` to `unsuccessfulData`

We also add a bulkEdit method which will be used to bulk unsubscribe members
from the newsletter.
2021-08-13 12:19:02 +01:00

101 lines
3.0 KiB
JavaScript

const _ = require('lodash');
const errors = require('@tryghost/errors');
const logging = require('@tryghost/logging');
const CHUNK_SIZE = 100;
function createBulkOperation(singular, multiple) {
return async function (knex, table, data, options) {
const result = {
successful: 0,
unsuccessful: 0,
unsuccessfulData: [],
errors: []
};
for (const chunkedData of _.chunk(data, CHUNK_SIZE)) {
try {
await multiple(knex, table, chunkedData, options);
result.successful += chunkedData.length;
} catch (errToIgnore) {
for (const singularData of chunkedData) {
try {
await singular(knex, table, singularData, options);
result.successful += 1;
} catch (err) {
err.errorDetails = singularData;
result.errors.push(err);
result.unsuccessfulData.push(singularData);
result.unsuccessful += 1;
}
}
}
}
return result;
};
}
async function insertSingle(knex, table, record) {
await knex(table).insert(record);
}
async function insertMultiple(knex, table, chunk) {
await knex(table).insert(chunk);
}
async function editSingle(knex, table, id, options) {
await knex(table).where('id', id).update(options.data);
}
async function editMultiple(knex, table, chunk, options) {
await knex(table).whereIn('id', chunk).update(options.data);
}
async function delSingle(knex, table, id) {
try {
await knex(table).where('id', id).del();
} catch (err) {
const importError = new errors.DataImportError({
message: `Failed to remove entry from ${table}`,
context: `Entry id: ${id}`,
err: err
});
logging.error(importError);
throw importError;
}
}
async function delMultiple(knex, table, chunk) {
await knex(table).whereIn('id', chunk).del();
}
const insert = createBulkOperation(insertSingle, insertMultiple);
const edit = createBulkOperation(editSingle, editMultiple);
const del = createBulkOperation(delSingle, delMultiple);
/**
* @param {import('bookshelf')} Bookshelf
*/
module.exports = function (Bookshelf) {
Bookshelf.Model = Bookshelf.Model.extend({}, {
bulkAdd: function bulkAdd(data, tableName) {
tableName = tableName || this.prototype.tableName;
return insert(Bookshelf.knex, tableName, data);
},
bulkEdit: function bulkEdit(data, tableName, options) {
tableName = tableName || this.prototype.tableName;
return edit(Bookshelf.knex, tableName, data, options);
},
bulkDestroy: function bulkDestroy(data, tableName) {
tableName = tableName || this.prototype.tableName;
return del(Bookshelf.knex, tableName, data);
}
});
};