diff --git a/core/server/api/canary/members.js b/core/server/api/canary/members.js index 88392606dc..bfd6231cc1 100644 --- a/core/server/api/canary/members.js +++ b/core/server/api/canary/members.js @@ -128,6 +128,28 @@ const members = { } }, + exportCSV: { + headers: { + disposition: { + type: 'csv', + value() { + const datetime = (new Date()).toJSON().substring(0, 10); + return `members.${datetime}.csv`; + } + } + }, + response: { + format: 'plain' + }, + permissions: { + method: 'browse' + }, + validation: {}, + query(frame) { + return membersService.api.members.list(frame.options); + } + }, + importCSV: { statusCode: 201, permissions: { diff --git a/core/server/api/canary/utils/serializers/output/members.js b/core/server/api/canary/utils/serializers/output/members.js index b455817f97..312a8429e6 100644 --- a/core/server/api/canary/utils/serializers/output/members.js +++ b/core/server/api/canary/utils/serializers/output/members.js @@ -38,6 +38,37 @@ module.exports = { }; }, + exportCSV(models, apiConfig, frame) { + debug('exportCSV'); + + const fields = ['id', 'email', 'name', 'created_at', 'deleted_at']; + + function formatCSV(data) { + let csv = `${fields.join(',')}\r\n`, + entry, + field, + j, + i; + + for (j = 0; j < data.length; j = j + 1) { + entry = data[j]; + + for (i = 0; i < fields.length; i = i + 1) { + field = fields[i]; + csv += entry[field] !== null ? entry[field] : ''; + if (i !== fields.length - 1) { + csv += ','; + } + } + csv += '\r\n'; + } + + return csv; + } + + frame.response = formatCSV(models.members); + }, + importCSV(data, apiConfig, frame) { debug('importCSV'); diff --git a/core/server/api/v2/members.js b/core/server/api/v2/members.js index 88392606dc..bfd6231cc1 100644 --- a/core/server/api/v2/members.js +++ b/core/server/api/v2/members.js @@ -128,6 +128,28 @@ const members = { } }, + exportCSV: { + headers: { + disposition: { + type: 'csv', + value() { + const datetime = (new Date()).toJSON().substring(0, 10); + return `members.${datetime}.csv`; + } + } + }, + response: { + format: 'plain' + }, + permissions: { + method: 'browse' + }, + validation: {}, + query(frame) { + return membersService.api.members.list(frame.options); + } + }, + importCSV: { statusCode: 201, permissions: { diff --git a/core/server/api/v2/utils/serializers/output/members.js b/core/server/api/v2/utils/serializers/output/members.js index 66a8c64ee2..4eeca69f5e 100644 --- a/core/server/api/v2/utils/serializers/output/members.js +++ b/core/server/api/v2/utils/serializers/output/members.js @@ -38,6 +38,37 @@ module.exports = { }; }, + exportCSV(models, apiConfig, frame) { + debug('exportCSV'); + + const fields = ['id', 'email', 'name', 'created_at', 'deleted_at']; + + function formatCSV(data) { + let csv = `${fields.join(',')}\r\n`, + entry, + field, + j, + i; + + for (j = 0; j < data.length; j = j + 1) { + entry = data[j]; + + for (i = 0; i < fields.length; i = i + 1) { + field = fields[i]; + csv += entry[field] !== null ? entry[field] : ''; + if (i !== fields.length - 1) { + csv += ','; + } + } + csv += '\r\n'; + } + + return csv; + } + + frame.response = formatCSV(models.members); + }, + importCSV(data, apiConfig, frame) { debug('importCSV'); diff --git a/core/server/web/api/canary/admin/routes.js b/core/server/web/api/canary/admin/routes.js index f7f3738365..6246d8f8dc 100644 --- a/core/server/web/api/canary/admin/routes.js +++ b/core/server/web/api/canary/admin/routes.js @@ -105,6 +105,7 @@ module.exports = function apiRoutes() { router.get('/members', shared.middlewares.labs.members, mw.authAdminApi, http(apiCanary.members.browse)); router.post('/members', shared.middlewares.labs.members, mw.authAdminApi, http(apiCanary.members.add)); + router.get('/members/csv', shared.middlewares.labs.members, mw.authAdminApi, http(apiCanary.members.exportCSV)); router.post('/members/csv', shared.middlewares.labs.members, mw.authAdminApi, diff --git a/core/server/web/api/v2/admin/routes.js b/core/server/web/api/v2/admin/routes.js index 1c9abf5b01..a17e3bdc8e 100644 --- a/core/server/web/api/v2/admin/routes.js +++ b/core/server/web/api/v2/admin/routes.js @@ -105,6 +105,7 @@ module.exports = function apiRoutes() { router.get('/members', shared.middlewares.labs.members, mw.authAdminApi, http(apiv2.members.browse)); router.post('/members', shared.middlewares.labs.members, mw.authAdminApi, http(apiv2.members.add)); + router.get('/members/csv', shared.middlewares.labs.members, mw.authAdminApi, http(apiv2.members.exportCSV)); router.post('/members/csv', shared.middlewares.labs.members, mw.authAdminApi, diff --git a/core/test/regression/api/canary/admin/members_spec.js b/core/test/regression/api/canary/admin/members_spec.js index 9eb1b01b0e..779f2ac1f2 100644 --- a/core/test/regression/api/canary/admin/members_spec.js +++ b/core/test/regression/api/canary/admin/members_spec.js @@ -215,7 +215,7 @@ describe('Members API', function () { }); }); - it.skip('Can export CSV', function () { + it('Can export CSV', function () { return request .get(localUtils.API.getApiQuery(`members/csv/`)) .set('Origin', config.get('url')) @@ -225,8 +225,9 @@ describe('Members API', function () { .then((res) => { should.not.exist(res.headers['x-cache-invalidate']); res.headers['content-disposition'].should.match(/Attachment;\sfilename="members/); - res.text.should.match(/id,email,created_at,deleted_at/); + res.text.should.match(/id,email,name,created_at,deleted_at/); res.text.should.match(/member1@test.com/); + res.text.should.match(/Mr Egg/); }); }); diff --git a/core/test/regression/api/v2/admin/members_spec.js b/core/test/regression/api/v2/admin/members_spec.js index 9eb1b01b0e..779f2ac1f2 100644 --- a/core/test/regression/api/v2/admin/members_spec.js +++ b/core/test/regression/api/v2/admin/members_spec.js @@ -215,7 +215,7 @@ describe('Members API', function () { }); }); - it.skip('Can export CSV', function () { + it('Can export CSV', function () { return request .get(localUtils.API.getApiQuery(`members/csv/`)) .set('Origin', config.get('url')) @@ -225,8 +225,9 @@ describe('Members API', function () { .then((res) => { should.not.exist(res.headers['x-cache-invalidate']); res.headers['content-disposition'].should.match(/Attachment;\sfilename="members/); - res.text.should.match(/id,email,created_at,deleted_at/); + res.text.should.match(/id,email,name,created_at,deleted_at/); res.text.should.match(/member1@test.com/); + res.text.should.match(/Mr Egg/); }); });