Added import threshold check to importer

refs https://github.com/TryGhost/Team/issues/912

- We need a way to check if the import threshold has been reached within the members importer. See more deets in refed issue
This commit is contained in:
Naz 2021-07-23 20:29:19 +04:00
parent f90e36b816
commit a90d996f33
2 changed files with 101 additions and 5 deletions

View File

@ -23,8 +23,9 @@ module.exports = class MembersCSVImporter {
* @param {({name, at, job, data, offloaded}) => void} options.addJob - Method registering an async job * @param {({name, at, job, data, offloaded}) => void} options.addJob - Method registering an async job
* @param {Object} options.knex - An instance of the Ghost Database connection * @param {Object} options.knex - An instance of the Ghost Database connection
* @param {Function} options.urlFor - function generating urls * @param {Function} options.urlFor - function generating urls
* @param {number} options.importThreshold - threshold to activate freeze flag if reached
*/ */
constructor({storagePath, getTimezone, getMembersApi, sendEmail, isSet, addJob, knex, urlFor}) { constructor({storagePath, getTimezone, getMembersApi, sendEmail, isSet, addJob, knex, urlFor, importThreshold}) {
this._storagePath = storagePath; this._storagePath = storagePath;
this._getTimezone = getTimezone; this._getTimezone = getTimezone;
this._getMembersApi = getMembersApi; this._getMembersApi = getMembersApi;
@ -33,6 +34,7 @@ module.exports = class MembersCSVImporter {
this._addJob = addJob; this._addJob = addJob;
this._knex = knex; this._knex = knex;
this._urlFor = urlFor; this._urlFor = urlFor;
this._importThreshold = importThreshold;
} }
/** /**
@ -267,19 +269,27 @@ module.exports = class MembersCSVImporter {
* @param {Object} config.LabelModel - instance of Ghosts Label model * @param {Object} config.LabelModel - instance of Ghosts Label model
*/ */
async process({pathToCSV, headerMapping, globalLabels, importLabel, user, LabelModel}) { async process({pathToCSV, headerMapping, globalLabels, importLabel, user, LabelModel}) {
const meta = {};
const job = await this.prepare(pathToCSV, headerMapping, globalLabels); const job = await this.prepare(pathToCSV, headerMapping, globalLabels);
if (this._isSet('checkEmailList') && this._importThreshold) {
meta.freeze = job.batches > this._importThreshold;
} else {
meta.freeze = false;
}
if (job.batches <= 500 && !job.metadata.hasStripeData) { if (job.batches <= 500 && !job.metadata.hasStripeData) {
const result = await this.perform(job.id); const result = await this.perform(job.id);
const importLabelModel = result.imported ? await LabelModel.findOne(importLabel) : null; const importLabelModel = result.imported ? await LabelModel.findOne(importLabel) : null;
return { return {
meta: { meta: Object.assign(meta, {
stats: { stats: {
imported: result.imported, imported: result.imported,
invalid: result.errors invalid: result.errors
}, },
import_label: importLabelModel import_label: importLabelModel
} })
}; };
} else { } else {
const emailRecipient = user.email; const emailRecipient = user.email;
@ -310,7 +320,9 @@ module.exports = class MembersCSVImporter {
offloaded: false offloaded: false
}); });
return {}; return {
meta
};
} }
} }
}; };

View File

@ -61,12 +61,16 @@ describe('Importer', function () {
findOne: sinon.stub().resolves(null) findOne: sinon.stub().resolves(null)
}; };
const isSetStub = sinon.stub()
.withArgs('checkEmailList')
.returns('true');
const importer = new MembersCSVImporter({ const importer = new MembersCSVImporter({
storagePath: csvPath, storagePath: csvPath,
getTimezone: sinon.stub().returns('UTC'), getTimezone: sinon.stub().returns('UTC'),
getMembersApi: () => membersApi, getMembersApi: () => membersApi,
sendEmail: sinon.stub(), sendEmail: sinon.stub(),
isSet: sinon.stub(), isSet: isSetStub,
addJob: sinon.stub(), addJob: sinon.stub(),
knex: knexStub, knex: knexStub,
urlFor: sinon.stub() urlFor: sinon.stub()
@ -92,6 +96,86 @@ describe('Importer', function () {
should.exist(result.meta.stats.invalid); should.exist(result.meta.stats.invalid);
should.equal(result.meta.import_label, null); should.equal(result.meta.import_label, null);
// freeze not triggered if it's not provided
should.exist(result.meta.freeze);
result.meta.freeze.should.be.false();
fsWriteSpy.calledOnce.should.be.true();
});
it('should trigger a freeze when over the freeze threshold', async function () {
const defaultProduct = {
id: 'default_product_id'
};
const membersApi = {
productRepository: {
list: async () => {
return {
data: [defaultProduct]
};
}
},
members: {
get: async () => {
return null;
},
create: async (row) => {
return row;
}
}
};
const knexStub = {
transaction: sinon.stub().resolves({
rollback: () => {},
commit: () => {}
})
};
const LabelModelStub = {
findOne: sinon.stub().resolves(null)
};
const isSetStub = sinon.stub()
.withArgs('checkEmailList')
.returns('true');
const importer = new MembersCSVImporter({
storagePath: csvPath,
getTimezone: sinon.stub().returns('UTC'),
getMembersApi: () => membersApi,
sendEmail: sinon.stub(),
isSet: isSetStub,
addJob: sinon.stub(),
knex: knexStub,
urlFor: sinon.stub(),
importThreshold: 1
});
const result = await importer.process({
pathToCSV: `${csvPath}/single-column-with-header.csv`,
headerMapping: {},
importLabel: {
name: 'test import'
},
user: {
email: 'test@example.com'
},
LabelModel: LabelModelStub
});
should.exist(result.meta);
should.exist(result.meta.stats);
should.exist(result.meta.stats.imported);
result.meta.stats.imported.should.equal(2);
should.exist(result.meta.stats.invalid);
should.equal(result.meta.import_label, null);
should.exist(result.meta.freeze);
result.meta.freeze.should.be.true();
fsWriteSpy.calledOnce.should.be.true(); fsWriteSpy.calledOnce.should.be.true();
}); });
}); });