From 038b0a034cabe1e160c453b8da58a5b841d0e90e Mon Sep 17 00:00:00 2001 From: gets0ul Date: Thu, 24 Oct 2019 13:22:45 +0700 Subject: [PATCH] - support filtering orgConfigs by multiple orgId - filter orgConfigs data from ES - add unit tests for the changes --- src/routes/orgConfig/list.js | 56 +++++++++---- src/routes/orgConfig/list.spec.js | 134 +++++++++++++++++++++++++++--- 2 files changed, 164 insertions(+), 26 deletions(-) diff --git a/src/routes/orgConfig/list.js b/src/routes/orgConfig/list.js index b77b3dc3..6e5fb747 100644 --- a/src/routes/orgConfig/list.js +++ b/src/routes/orgConfig/list.js @@ -3,6 +3,7 @@ */ import validate from 'express-validation'; import Joi from 'joi'; +import _ from 'lodash'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import models from '../../models'; import util from '../../util'; @@ -20,24 +21,51 @@ module.exports = [ validate(schema), permissions('orgConfig.view'), (req, res, next) => { - util.fetchFromES('orgConfigs') - .then((data) => { - // handle filters - const filters = req.query; - // Throw error if orgId is not present in filter - if (!filters.orgId) { - next(util.buildApiError('Missing filter orgId', 400)); - } - if (!util.isValidFilter(filters, ['orgId', 'configName'])) { - util.handleError('Invalid filters', null, req, next); - } - req.log.debug(filters); + // handle filters + const filters = req.query; + // Throw error if orgId is not present in filter + if (!filters.orgId) { + next(util.buildApiError('Missing filter orgId', 400)); + } + if (!util.isValidFilter(filters, ['orgId', 'configName'])) { + util.handleError('Invalid filters', null, req, next); + } + req.log.debug(filters); + const orgIds = filters.orgId.split(','); + // build filter query for ES + const must = [{ + terms: { + 'orgConfigs.orgId': orgIds, + }, + }]; + if (filters.configName) { + must.push({ + term: { + 'orgConfigs.configName': filters.configName, + }, + }); + } + + util.fetchFromES('orgConfigs', { + query: { + nested: { + path: 'orgConfigs', + query: { + bool: { + must, + }, + }, + inner_hits: {}, + }, + }, + }, 'metadata') + .then((data) => { if (data.orgConfigs.length === 0) { req.log.debug('No orgConfig found in ES'); // Get all organization config - const where = filters || {}; + const where = filters ? _.assign({}, filters, { orgId: { $in: orgIds } }) : {}; models.OrgConfig.findAll({ where, attributes: { exclude: ['deletedAt', 'deletedBy'] }, @@ -49,7 +77,7 @@ module.exports = [ .catch(next); } else { req.log.debug('orgConfigs found in ES'); - res.json(data.orgConfigs); + res.json(data.orgConfigs.hits.hits.map(hit => hit._source)); // eslint-disable-line no-underscore-dangle } }); }, diff --git a/src/routes/orgConfig/list.spec.js b/src/routes/orgConfig/list.spec.js index 69317422..498f1187 100644 --- a/src/routes/orgConfig/list.spec.js +++ b/src/routes/orgConfig/list.spec.js @@ -3,6 +3,8 @@ */ import chai from 'chai'; import request from 'supertest'; +import config from 'config'; +import _ from 'lodash'; import models from '../../models'; import server from '../../app'; @@ -10,6 +12,21 @@ import testUtil from '../../tests/util'; const should = chai.should(); +const ES_ORGCONFIG_INDEX = config.get('elasticsearchConfig.metadataIndexName'); +const ES_ORGCONFIG_TYPE = config.get('elasticsearchConfig.metadataDocType'); + +const validateOrgConfig = (resJson, orgConfig) => { + resJson.id.should.be.eql(orgConfig.id); + resJson.orgId.should.be.eql(orgConfig.orgId); + resJson.configName.should.be.eql(orgConfig.configName); + resJson.configValue.should.be.eql(orgConfig.configValue); + should.exist(resJson.createdAt); + resJson.updatedBy.should.be.eql(orgConfig.updatedBy); + should.exist(resJson.updatedAt); + should.not.exist(resJson.deletedBy); + should.not.exist(resJson.deletedAt); +}; + describe('LIST organization config', () => { const orgConfigPath = '/v5/projects/metadata/orgConfig'; const configs = [ @@ -23,17 +40,50 @@ describe('LIST organization config', () => { }, { id: 2, - orgId: 'ORG1', + orgId: 'ORG2', configName: 'project_catalog_url', configValue: '/projects/2', createdBy: 1, updatedBy: 1, }, + { + id: 3, + orgId: 'ORG3', + configName: 'project_catalog_url', + configValue: '/projects/3', + createdBy: 1, + updatedBy: 1, + }, + { + id: 4, + orgId: 'ORG4', + configName: 'project_catalog_url', + configValue: '/projects/4', + createdBy: 1, + updatedBy: 1, + }, ]; beforeEach((done) => { testUtil.clearDb() - .then(() => models.OrgConfig.bulkCreate(configs).then(() => done())); + .then(() => models.OrgConfig.bulkCreate(configs, { returning: true }), + ).then(async (createdConfigs) => { + // Index to ES only orgConfigs with id: 3 and 4 + const indexedConfigs = _(createdConfigs).filter(createdConfig => createdConfig.toJSON().id > 2) + .map((filteredConfig) => { + const orgConfigJson = _.omit(filteredConfig.toJSON(), 'deletedAt', 'deletedBy'); + return orgConfigJson; + }).value(); + + await server.services.es.index({ + index: ES_ORGCONFIG_INDEX, + type: ES_ORGCONFIG_TYPE, + body: { + orgConfigs: indexedConfigs, + }, + }); + done(); + }); }); after((done) => { testUtil.clearDb(done); @@ -48,19 +98,79 @@ describe('LIST organization config', () => { }) .expect(200) .end((err, res) => { - const config = configs[0]; + const config1 = configs[0]; + + const resJson = res.body; + resJson.should.have.length(1); + validateOrgConfig(resJson[0], config1); + + done(); + }); + }); + + it('should return 200 for admin with filter (ES)', (done) => { + request(server) + .get(`${orgConfigPath}?orgId=${configs[2].orgId}&configName=${configs[2].configName}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(200) + .end((err, res) => { + const config3 = configs[2]; const resJson = res.body; resJson.should.have.length(1); - resJson[0].id.should.be.eql(config.id); - resJson[0].orgId.should.be.eql(config.orgId); - resJson[0].configName.should.be.eql(config.configName); - resJson[0].configValue.should.be.eql(config.configValue); - should.exist(resJson[0].createdAt); - resJson[0].updatedBy.should.be.eql(config.updatedBy); - should.exist(resJson[0].updatedAt); - should.not.exist(resJson[0].deletedBy); - should.not.exist(resJson[0].deletedAt); + validateOrgConfig(resJson[0], config3); + + done(); + }); + }); + + it('should return 200 for admin and filter by multiple orgId (DB)', (done) => { + request(server) + .get(`${orgConfigPath}?orgId=${configs[0].orgId},${configs[1].orgId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(200) + .end((err, res) => { + const config1 = configs[0]; + const config2 = configs[1]; + + const resJson = res.body; + resJson.should.have.length(2); + resJson.forEach((result) => { + if (result.id === 1) { + validateOrgConfig(result, config1); + } else { + validateOrgConfig(result, config2); + } + }); + + done(); + }); + }); + + it('should return 200 for admin and filter by multiple orgId (ES)', (done) => { + request(server) + .get(`${orgConfigPath}?orgId=${configs[2].orgId},${configs[3].orgId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(200) + .end((err, res) => { + const config3 = configs[2]; + const config4 = configs[3]; + + const resJson = res.body; + resJson.should.have.length(2); + resJson.forEach((result) => { + if (result.id === 3) { + validateOrgConfig(result, config3); + } else { + validateOrgConfig(result, config4); + } + }); done(); });