From 633ba27f0e5fbf982cf562c595207c4503f0f664 Mon Sep 17 00:00:00 2001 From: Nazar Gargol Date: Fri, 5 Jun 2020 16:08:46 +1200 Subject: [PATCH] Added custom label assignment to imported members no issue - There is a need to be able to label certain import group of members with custom labels. This will allow to distinguish/filter these newly imported members. - Allowed `POST /members/csv/` endpoint to accept `labels` field parameter which assigns labels to every member from imported csv. --- core/server/api/canary/members.js | 36 ++++++++++- .../api/canary/admin/members_spec.js | 62 +++++++++++++++++-- .../fixtures/csv/valid-members-labels.csv | 3 + 3 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 test/utils/fixtures/csv/valid-members-labels.csv diff --git a/core/server/api/canary/members.js b/core/server/api/canary/members.js index a45217da03..e7e6215df7 100644 --- a/core/server/api/canary/members.js +++ b/core/server/api/canary/members.js @@ -67,7 +67,11 @@ const sanitizeInput = (members) => { }; function serializeMemberLabels(labels) { - if (labels) { + if (_.isString(labels)) { + return [{ + name: labels.trim() + }]; + } else if (labels) { return labels.filter((label) => { return !!label; }).map((label) => { @@ -96,6 +100,27 @@ const listMembers = async function (options) { }; }; +const createLabels = async (labels, options) => { + const api = require('./index'); + + return await Promise.all(labels.map((label) => { + return api.labels.add.query({ + data: { + labels: [label] + }, + options: { + context: options.context + } + }).catch((error) => { + if (error.errorType === 'ValidationError') { + return; + } + + throw error; + }); + })); +}; + const members = { docName: 'members', browse: { @@ -341,6 +366,11 @@ const members = { lookup: /created_at/i }]; + // NOTE: custom labels have to be created in advance otherwise there are conflicts + // when processing member creation in parallel later on in import process + const importSetLabels = serializeMemberLabels(frame.data.labels); + await createLabels(importSetLabels, frame.options); + return fsLib.readCSV({ path: filePath, columnsToExtract: columnsToExtract @@ -352,6 +382,8 @@ const members = { const api = require('./index'); entry.labels = (entry.labels && entry.labels.split(',')) || []; const entryLabels = serializeMemberLabels(entry.labels); + const mergedLabels = _.unionBy(entryLabels, importSetLabels, 'name'); + cleanupUndefined(entry); let subscribed; @@ -370,7 +402,7 @@ const members = { subscribed: subscribed, stripe_customer_id: entry.stripe_customer_id, comped: (String(entry.complimentary_plan).toLocaleLowerCase() === 'true'), - labels: entryLabels, + labels: mergedLabels, created_at: entry.created_at === '' ? undefined : entry.created_at }] }, diff --git a/test/regression/api/canary/admin/members_spec.js b/test/regression/api/canary/admin/members_spec.js index 02ee9aee00..51f1b96d5f 100644 --- a/test/regression/api/canary/admin/members_spec.js +++ b/test/regression/api/canary/admin/members_spec.js @@ -137,9 +137,59 @@ describe('Members API', function () { }); it('Can import CSV with minimum one field', function () { + return request + .post(localUtils.API.getApiQuery(`members/csv/`)) + .field('labels', ['global-label-1', 'global-label-1']) + .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/valid-members-labels.csv')) + .set('Origin', config.get('url')) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(201) + .then((res) => { + should.not.exist(res.headers['x-cache-invalidate']); + const jsonResponse = res.body; + + should.exist(jsonResponse); + should.exist(jsonResponse.meta); + should.exist(jsonResponse.meta.stats); + + jsonResponse.meta.stats.imported.should.equal(2); + jsonResponse.meta.stats.duplicates.should.equal(0); + jsonResponse.meta.stats.invalid.should.equal(0); + }) + .then(() => { + return request + .get(localUtils.API.getApiQuery(`members/?search=${encodeURIComponent('member+labels_1@example.com')}`)) + .set('Origin', config.get('url')) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(200); + }) + .then((res) => { + should.not.exist(res.headers['x-cache-invalidate']); + const jsonResponse = res.body; + + should.exist(jsonResponse); + should.exist(jsonResponse.members); + should.exist(jsonResponse.members[0]); + + const importedMember1 = jsonResponse.members[0]; + should(importedMember1.email).equal('member+labels_1@example.com'); + should(importedMember1.name).equal(null); + should(importedMember1.note).equal(null); + importedMember1.subscribed.should.equal(true); + importedMember1.comped.should.equal(false); + importedMember1.stripe.should.not.be.undefined(); + importedMember1.stripe.subscriptions.length.should.equal(0); + importedMember1.labels.length.should.equal(2); + }); + }); + + it('Can import CSV with labels and provide additional labels', function () { return request .post(localUtils.API.getApiQuery(`members/csv/`)) .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/valid-members-defaults.csv')) + .set('Origin', config.get('url')) .expect('Content-Type', /json/) .expect('Cache-Control', testUtils.cacheRules.private) @@ -226,8 +276,8 @@ describe('Members API', function () { should.exist(jsonResponse.total_on_date); should.exist(jsonResponse.new_today); - // 2 from fixtures and 3 imported in previous tests - jsonResponse.total.should.equal(5); + // 2 from fixtures and 5 imported in previous tests + jsonResponse.total.should.equal(7); }); }); @@ -250,8 +300,8 @@ describe('Members API', function () { should.exist(jsonResponse.total_on_date); should.exist(jsonResponse.new_today); - // 2 from fixtures and 3 imported in previous tests - jsonResponse.total.should.equal(5); + // 2 from fixtures and 5 imported in previous tests + jsonResponse.total.should.equal(7); }); }); @@ -274,8 +324,8 @@ describe('Members API', function () { should.exist(jsonResponse.total_on_date); should.exist(jsonResponse.new_today); - // 2 from fixtures and 3 imported in previous tests - jsonResponse.total.should.equal(5); + // 2 from fixtures and 5 imported in previous tests + jsonResponse.total.should.equal(7); }); }); diff --git a/test/utils/fixtures/csv/valid-members-labels.csv b/test/utils/fixtures/csv/valid-members-labels.csv new file mode 100644 index 0000000000..bccb42e17b --- /dev/null +++ b/test/utils/fixtures/csv/valid-members-labels.csv @@ -0,0 +1,3 @@ +email,labels +member+labels_1@example.com,label +member+labels_2@example.com,another-label