From 619d2bf6cf217f3e20b4ba0dfc30e334c9280ffc Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 6 Dec 2017 11:48:34 +0530 Subject: [PATCH 01/10] =?UTF-8?q?Github=20issue#1243,=20Connect=20Admin=20?= =?UTF-8?q?role=20=E2=80=94=20Added=20support=20for=20=E2=80=98Connect=20A?= =?UTF-8?q?dmin=E2=80=99=20role?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants.js | 7 ++++--- src/permissions/project.delete.js | 1 + src/permissions/project.edit.js | 1 + src/permissions/project.view.js | 1 + src/permissions/projectMember.delete.js | 1 + src/routes/projects/list.js | 3 ++- src/routes/projects/update.js | 2 +- 7 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/constants.js b/src/constants.js index 1d2c0d62..70208f4d 100644 --- a/src/constants.js +++ b/src/constants.js @@ -27,9 +27,10 @@ export const PROJECT_MEMBER_ROLE = { }; export const USER_ROLE = { - TOPCODER_ADMIN: 'administrator', - MANAGER: 'Connect Manager', - COPILOT: 'Connect Copilot', + TOPCODER_ADMIN : 'administrator', + MANAGER : 'Connect Manager', + COPILOT : 'Connect Copilot', + CONNECT_ADMIN : 'Connect Admin' }; diff --git a/src/permissions/project.delete.js b/src/permissions/project.delete.js index c07479d3..6bfe4a6b 100644 --- a/src/permissions/project.delete.js +++ b/src/permissions/project.delete.js @@ -21,6 +21,7 @@ module.exports = freq => new Promise((resolve, reject) => { req.context.currentProjectMembers = members; // check if auth user has acecss to this project const hasAccess = util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) || + util.hasRole(req, USER_ROLE.CONNECT_ADMIN) || !_.isUndefined(_.find(members, m => m.userId === req.authUser.userId && ((m.role === PROJECT_MEMBER_ROLE.CUSTOMER && m.isPrimary) || m.role === PROJECT_MEMBER_ROLE.MANAGER))); diff --git a/src/permissions/project.edit.js b/src/permissions/project.edit.js index 57d56847..372763d2 100644 --- a/src/permissions/project.edit.js +++ b/src/permissions/project.edit.js @@ -20,6 +20,7 @@ module.exports = freq => new Promise((resolve, reject) => { req.context.currentProjectMembers = members; // check if auth user has acecss to this project const hasAccess = util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) + || util.hasRole(req, USER_ROLE.CONNECT_ADMIN) || util.hasRole(req, USER_ROLE.MANAGER) || !_.isUndefined(_.find(members, m => m.userId === req.authUser.userId)); diff --git a/src/permissions/project.view.js b/src/permissions/project.view.js index 91776d1a..72b15875 100644 --- a/src/permissions/project.view.js +++ b/src/permissions/project.view.js @@ -21,6 +21,7 @@ module.exports = freq => new Promise((resolve, reject) => { req.context.currentProjectMembers = members; // check if auth user has acecss to this project const hasAccess = util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) + || util.hasRole(req, USER_ROLE.CONNECT_ADMIN) || util.hasRole(req, USER_ROLE.MANAGER) || !_.isUndefined(_.find(members, m => m.userId === currentUserId)); diff --git a/src/permissions/projectMember.delete.js b/src/permissions/projectMember.delete.js index 634bb557..7dedcebf 100644 --- a/src/permissions/projectMember.delete.js +++ b/src/permissions/projectMember.delete.js @@ -26,6 +26,7 @@ module.exports = freq => new Promise((resolve, reject) => { const memberToBeRemoved = _.find(members, m => m.id === prjMemberId); // check if auth user has acecss to this project const hasAccess = util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) + || util.hasRole(req, USER_ROLE.CONNECT_ADMIN) || (authMember && memberToBeRemoved && (authMember.role === PROJECT_MEMBER_ROLE.MANAGER || (authMember.role === PROJECT_MEMBER_ROLE.CUSTOMER && authMember.isPrimary && memberToBeRemoved.role === PROJECT_MEMBER_ROLE.CUSTOMER) || diff --git a/src/routes/projects/list.js b/src/routes/projects/list.js index 6801a320..565368c4 100755 --- a/src/routes/projects/list.js +++ b/src/routes/projects/list.js @@ -214,10 +214,11 @@ module.exports = [ limit: Math.min(req.query.limit || 20, 20), offset: req.query.offset || 0, }; - req.log.debug(criteria); + req.log.info(criteria); if (!memberOnly && (util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) + || util.hasRole(req, USER_ROLE.CONNECT_ADMIN) || util.hasRole(req, USER_ROLE.MANAGER))) { // admins & topcoder managers can see all projects return retrieveProjects(req, criteria, sort, req.query.fields) diff --git a/src/routes/projects/update.js b/src/routes/projects/update.js index deb1175c..b8507a7c 100644 --- a/src/routes/projects/update.js +++ b/src/routes/projects/update.js @@ -160,7 +160,7 @@ module.exports = [ ].map(x => x.toLowerCase()); const matchRole = role => _.indexOf(validRoles, role.toLowerCase()) >= 0; if (updatedProps.status === PROJECT_STATUS.ACTIVE && - !util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) && + (!util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) || !util.hasRole(req, USER_ROLE.CONNECT_ADMIN)) && _.isUndefined(_.find(members, m => m.userId === req.authUser.userId && matchRole(m.role))) ) { From 88646b90ac8e9ec5fe4d2fc7057fb831be8a69dc Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 6 Dec 2017 11:50:36 +0530 Subject: [PATCH 02/10] =?UTF-8?q?Github=20issue#1243,=20Connect=20Admin=20?= =?UTF-8?q?role=20=E2=80=94=20Deploying=20feature=20branch=20to=20dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index f5993dff..ac34c5e4 100644 --- a/circle.yml +++ b/circle.yml @@ -24,7 +24,7 @@ dependencies: deployment: development: - branch: dev + branch: [dev, 'feature/connect-admin-role'] commands: - ./ebs_deploy.sh tc-project-service DEV $CIRCLE_BUILD_NUM From 50f60aa141cfa2371ef9fd4eddd6caf9e49f85ed Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 6 Dec 2017 11:59:54 +0530 Subject: [PATCH 03/10] lint fix --- src/constants.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/constants.js b/src/constants.js index 70208f4d..a19cf1fe 100644 --- a/src/constants.js +++ b/src/constants.js @@ -27,10 +27,10 @@ export const PROJECT_MEMBER_ROLE = { }; export const USER_ROLE = { - TOPCODER_ADMIN : 'administrator', - MANAGER : 'Connect Manager', - COPILOT : 'Connect Copilot', - CONNECT_ADMIN : 'Connect Admin' + TOPCODER_ADMIN: 'administrator', + MANAGER: 'Connect Manager', + COPILOT: 'Connect Copilot', + CONNECT_ADMIN: 'Connect Admin', }; From 50b20877a5fe599666de2da6b14225610d534a00 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 6 Dec 2017 14:24:12 +0530 Subject: [PATCH 04/10] =?UTF-8?q?Github=20issue#1243,=20Connect=20Admin=20?= =?UTF-8?q?role=20=E2=80=94=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants.js | 2 ++ src/permissions/project.delete.js | 5 ++--- src/permissions/project.edit.js | 3 +-- src/permissions/project.view.js | 3 +-- src/permissions/projectMember.delete.js | 4 +--- src/routes/projects/list-db.js | 2 +- src/routes/projects/list.js | 3 +-- src/routes/projects/update.js | 8 +++---- src/util.js | 28 ++++++++++++++++++++++++- 9 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/constants.js b/src/constants.js index a19cf1fe..de3cecc0 100644 --- a/src/constants.js +++ b/src/constants.js @@ -33,6 +33,8 @@ export const USER_ROLE = { CONNECT_ADMIN: 'Connect Admin', }; +export const ADMIN_ROLES = [USER_ROLE.CONNECT_ADMIN, USER_ROLE.TOPCODER_ADMIN]; + export const EVENT = { ROUTING_KEY: { diff --git a/src/permissions/project.delete.js b/src/permissions/project.delete.js index 6bfe4a6b..9fe49fc8 100644 --- a/src/permissions/project.delete.js +++ b/src/permissions/project.delete.js @@ -4,7 +4,7 @@ import _ from 'lodash'; import util from '../util'; import models from '../models'; -import { USER_ROLE, PROJECT_MEMBER_ROLE } from '../constants'; +import { PROJECT_MEMBER_ROLE } from '../constants'; /** * Super admin, Topcoder Managers are allowed to edit any project @@ -20,8 +20,7 @@ module.exports = freq => new Promise((resolve, reject) => { req.context = req.context || {}; req.context.currentProjectMembers = members; // check if auth user has acecss to this project - const hasAccess = util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) || - util.hasRole(req, USER_ROLE.CONNECT_ADMIN) || + const hasAccess = util.hasAdminRole(req) || !_.isUndefined(_.find(members, m => m.userId === req.authUser.userId && ((m.role === PROJECT_MEMBER_ROLE.CUSTOMER && m.isPrimary) || m.role === PROJECT_MEMBER_ROLE.MANAGER))); diff --git a/src/permissions/project.edit.js b/src/permissions/project.edit.js index 372763d2..760e672e 100644 --- a/src/permissions/project.edit.js +++ b/src/permissions/project.edit.js @@ -19,8 +19,7 @@ module.exports = freq => new Promise((resolve, reject) => { req.context = req.context || {}; req.context.currentProjectMembers = members; // check if auth user has acecss to this project - const hasAccess = util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) - || util.hasRole(req, USER_ROLE.CONNECT_ADMIN) + const hasAccess = util.hasAdminRole(req) || util.hasRole(req, USER_ROLE.MANAGER) || !_.isUndefined(_.find(members, m => m.userId === req.authUser.userId)); diff --git a/src/permissions/project.view.js b/src/permissions/project.view.js index 72b15875..b3ed1404 100644 --- a/src/permissions/project.view.js +++ b/src/permissions/project.view.js @@ -20,8 +20,7 @@ module.exports = freq => new Promise((resolve, reject) => { req.context = req.context || {}; req.context.currentProjectMembers = members; // check if auth user has acecss to this project - const hasAccess = util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) - || util.hasRole(req, USER_ROLE.CONNECT_ADMIN) + const hasAccess = util.hasAdminRole(req) || util.hasRole(req, USER_ROLE.MANAGER) || !_.isUndefined(_.find(members, m => m.userId === currentUserId)); diff --git a/src/permissions/projectMember.delete.js b/src/permissions/projectMember.delete.js index 7dedcebf..0f0ec42c 100644 --- a/src/permissions/projectMember.delete.js +++ b/src/permissions/projectMember.delete.js @@ -2,7 +2,6 @@ import _ from 'lodash'; import util from '../util'; import models from '../models'; import { - USER_ROLE, PROJECT_MEMBER_ROLE, } from '../constants'; @@ -25,8 +24,7 @@ module.exports = freq => new Promise((resolve, reject) => { const prjMemberId = _.parseInt(req.params.id); const memberToBeRemoved = _.find(members, m => m.id === prjMemberId); // check if auth user has acecss to this project - const hasAccess = util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) - || util.hasRole(req, USER_ROLE.CONNECT_ADMIN) + const hasAccess = util.hasAdminRole(req) || (authMember && memberToBeRemoved && (authMember.role === PROJECT_MEMBER_ROLE.MANAGER || (authMember.role === PROJECT_MEMBER_ROLE.CUSTOMER && authMember.isPrimary && memberToBeRemoved.role === PROJECT_MEMBER_ROLE.CUSTOMER) || diff --git a/src/routes/projects/list-db.js b/src/routes/projects/list-db.js index 8908a602..a2ce93fa 100644 --- a/src/routes/projects/list-db.js +++ b/src/routes/projects/list-db.js @@ -123,7 +123,7 @@ module.exports = [ req.log.debug(criteria); if (!memberOnly - && (util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) + && (util.hasAdminRole(req) || util.hasRole(req, USER_ROLE.MANAGER))) { // admins & topcoder managers can see all projects return retrieveProjects(req, criteria, sort, req.query.fields) diff --git a/src/routes/projects/list.js b/src/routes/projects/list.js index 565368c4..f1cd2c9a 100755 --- a/src/routes/projects/list.js +++ b/src/routes/projects/list.js @@ -217,8 +217,7 @@ module.exports = [ req.log.info(criteria); if (!memberOnly - && (util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) - || util.hasRole(req, USER_ROLE.CONNECT_ADMIN) + && (util.hasAdminRole(req) || util.hasRole(req, USER_ROLE.MANAGER))) { // admins & topcoder managers can see all projects return retrieveProjects(req, criteria, sort, req.query.fields) diff --git a/src/routes/projects/update.js b/src/routes/projects/update.js index b8507a7c..985c7c82 100644 --- a/src/routes/projects/update.js +++ b/src/routes/projects/update.js @@ -82,7 +82,7 @@ const updateProjectValdiations = { }; // NOTE- decided to disable all additional checks for now. -const validateUpdates = (existingProject, updatedProps, authUser) => { +const validateUpdates = (existingProject, updatedProps, req) => { const errors = []; switch (existingProject.status) { case PROJECT_STATUS.COMPLETED: @@ -101,7 +101,7 @@ const validateUpdates = (existingProject, updatedProps, authUser) => { // } } if (_.has(updatedProps, 'directProjectId') && - _.intersection(authUser.roles, [USER_ROLE.MANAGER, USER_ROLE.TOPCODER_ADMIN]).length === 0) { + !util.hasRoles(req, [USER_ROLE.MANAGER, USER_ROLE.TOPCODER_ADMIN])) { errors.push('Don\'t have permission to update \'directProjectId\' property'); } @@ -142,7 +142,7 @@ module.exports = [ } previousValue = _.clone(project.get({ plain: true })); // run additional validations - const validationErrors = validateUpdates(previousValue, updatedProps, req.authUser); + const validationErrors = validateUpdates(previousValue, updatedProps, req); if (validationErrors.length > 0) { const err = new Error('Unable to update project'); _.assign(err, { @@ -160,7 +160,7 @@ module.exports = [ ].map(x => x.toLowerCase()); const matchRole = role => _.indexOf(validRoles, role.toLowerCase()) >= 0; if (updatedProps.status === PROJECT_STATUS.ACTIVE && - (!util.hasRole(req, USER_ROLE.TOPCODER_ADMIN) || !util.hasRole(req, USER_ROLE.CONNECT_ADMIN)) && + !util.hasAdminRole(req) && _.isUndefined(_.find(members, m => m.userId === req.authUser.userId && matchRole(m.role))) ) { diff --git a/src/util.js b/src/util.js index b67c7041..6bc3d0ce 100644 --- a/src/util.js +++ b/src/util.js @@ -17,6 +17,7 @@ import urlencode from 'urlencode'; import elasticsearch from 'elasticsearch'; import Promise from 'bluebird'; import AWS from 'aws-sdk'; +import { ADMIN_ROLES } from './constants'; const exec = require('child_process').exec; const models = require('./models').default; @@ -73,6 +74,27 @@ _.assignIn(util, { roles = roles.map(s => s.toLowerCase()); return _.indexOf(roles, role.toLowerCase()) >= 0; }, + /** + * Helper funtion to verify if user has specified roles + * @param {object} req Request object that should contain authUser + * @param {Array} roles specified roles + * @return {boolean} true/false + */ + hasRoles: (req, roles) => { + let authRoles = _.get(req, 'authUser.roles', []); + authRoles = authRoles.map(s => s.toLowerCase()); + return _.intersection(authRoles, roles.map(r => r.toLowerCase())).length > 0; + }, + /** + * Helper funtion to verify if user has admin roles + * @param {object} req Request object that should contain authUser + * @return {boolean} true/false + */ + hasAdminRole: (req) => { + let roles = _.get(req, 'authUser.roles', []); + roles = roles.map(s => s.toLowerCase()); + return _.intersection(roles, ADMIN_ROLES.map(r => r.toLowerCase())).length > 0; + }, /** * Parses query fields and groups them per table @@ -206,6 +228,7 @@ _.assignIn(util, { getSystemUserToken: (logger, id = 'system') => { const httpClient = util.getHttpClient({ id, log: logger }); const url = `${config.get('identityServiceEndpoint')}authorizations`; + console.log(url, 'url'); const formData = `clientId=${config.get('systemUserClientId')}&` + `secret=${encodeURIComponent(config.get('systemUserClientSecret'))}`; return httpClient.post(url, formData, @@ -269,8 +292,11 @@ _.assignIn(util, { */ getMemberDetailsByUserIds: Promise.coroutine(function* (userIds, logger, requestId) { // eslint-disable-line func-names try { - const token = yield this.getSystemUserToken(logger); + console.log('getMemberDetailsByUserIds'); + const token = yield 'farzi';// this.getSystemUserToken(logger); + console.log('token', token); const httpClient = this.getHttpClient({ id: requestId, log: logger }); + console.log(config.memberServiceEndpoint, 'config.memberServiceEndpoint'); return httpClient.get(`${config.memberServiceEndpoint}/_search`, { params: { query: `${userIds.join(urlencode(' OR ', 'utf8'))}`, From 2cebeca3960c0c4026aa362ead10023ba9f18bb4 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 6 Dec 2017 16:03:55 +0530 Subject: [PATCH 05/10] =?UTF-8?q?Github=20issue#1243,=20Connect=20Admin=20?= =?UTF-8?q?role=20=E2=80=94=20Making=20connect=20admin=20a=20project=20man?= =?UTF-8?q?ager=20when=20he/she=20joins=20a=20project?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/projects/create.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/projects/create.js b/src/routes/projects/create.js index 1b4f1ead..9e84bf2c 100644 --- a/src/routes/projects/create.js +++ b/src/routes/projects/create.js @@ -66,7 +66,8 @@ module.exports = [ */ (req, res, next) => { const project = req.body.param; - const userRole = util.hasRole(req, USER_ROLE.MANAGER) + // by default connect admin and managers joins projects as manager + const userRole = util.hasRoles(req, [USER_ROLE.CONNECT_ADMIN, USER_ROLE.MANAGER]) ? PROJECT_MEMBER_ROLE.MANAGER : PROJECT_MEMBER_ROLE.CUSTOMER; // set defaults @@ -136,6 +137,7 @@ module.exports = [ req.log.error(err); return Promise.resolve(); }); + // return Promise.resolve(); }) .then(() => { From 3687a4a247d16c58a7bc4150fd29fcbd7d06315e Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 6 Dec 2017 16:05:15 +0530 Subject: [PATCH 06/10] =?UTF-8?q?Github=20issue#1243,=20Connect=20Admin=20?= =?UTF-8?q?role=20=E2=80=94=20added=20unit=20tests=20for=20connect=20admin?= =?UTF-8?q?=20role,=20tested=20that=20it=20should=20have=20all=20access=20?= =?UTF-8?q?similar=20to=20topcoder=20admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/projects/delete.spec.js | 34 +++++++++ src/routes/projects/get.spec.js | 21 +++++- src/routes/projects/list-db.spec.js | 104 ++++++++++++++++++++++++++++ src/routes/projects/list.spec.js | 104 ++++++++++++++++++++++++++++ src/routes/projects/update.spec.js | 59 +++++++++++++++- src/tests/util.js | 2 + 6 files changed, 321 insertions(+), 3 deletions(-) diff --git a/src/routes/projects/delete.spec.js b/src/routes/projects/delete.spec.js index bf9ffd81..8fee3330 100644 --- a/src/routes/projects/delete.spec.js +++ b/src/routes/projects/delete.spec.js @@ -100,5 +100,39 @@ describe('Project delete test', () => { } }); }); + + it('should return 204, for connect admin, if project was successfully removed', (done) => { + request(server) + .delete(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + server.services.pubsub.publish.calledWith('project.deleted').should.be.true; + done(); + } + }); + }); + + it('should return 204, for connect admin, if project was successfully removed', (done) => { + request(server) + .delete(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + server.services.pubsub.publish.calledWith('project.deleted').should.be.true; + done(); + } + }); + }); }); }); diff --git a/src/routes/projects/get.spec.js b/src/routes/projects/get.spec.js index e1251041..9fc165ee 100644 --- a/src/routes/projects/get.spec.js +++ b/src/routes/projects/get.spec.js @@ -136,6 +136,25 @@ describe('GET Project', () => { }); }); + it('should return the project for connect admin ', (done) => { + request(server) + .get(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + done(); + } + }); + }); + it('should return attachment with downloadUrl', (done) => { models.ProjectAttachment.create({ projectId: project1.id, @@ -172,6 +191,7 @@ describe('GET Project', () => { .expect('Content-Type', /json/) .expect(200) .end((err, res) => { + stub.restore(); if (err) { done(err); } else { @@ -181,7 +201,6 @@ describe('GET Project', () => { resJson.attachments.should.have.lengthOf(1); resJson.attachments[0].filePath.should.equal(attachment.filePath); resJson.attachments[0].downloadUrl.should.exist; - stub.restore(); done(); } }); diff --git a/src/routes/projects/list-db.spec.js b/src/routes/projects/list-db.spec.js index 5906d492..2c9560fb 100644 --- a/src/routes/projects/list-db.spec.js +++ b/src/routes/projects/list-db.spec.js @@ -300,5 +300,109 @@ describe('LIST Project db', () => { } }); }); + + describe('for connect admin ', () => { + it('should return the project ', (done) => { + request(server) + .get('/v4/projects/db/?fields=id%2Cmembers.id') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + done(); + } + }); + }); + + it('should return all projects that match when filtering by name', (done) => { + request(server) + .get('/v4/projects/db/?filter=keyword%3Dtest') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + done(); + } + }); + }); + + it('should return the project when filtering by keyword, which matches the name', (done) => { + request(server) + .get('/v4/projects/db/?filter=keyword%3D1') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(1); + resJson[0].name.should.equal('test1'); + done(); + } + }); + }); + + it('should return the project when filtering by keyword, which matches the description', (done) => { + request(server) + .get('/v4/projects/db/?filter=keyword%3Dproject') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + done(); + } + }); + }); + + it('should return the project when filtering by keyword, which matches the details', (done) => { + request(server) + .get('/v4/projects/db/?filter=keyword%3Dcode') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(1); + resJson[0].name.should.equal('test1'); + done(); + } + }); + }); + }); }); }); diff --git a/src/routes/projects/list.spec.js b/src/routes/projects/list.spec.js index 4ce4acbc..cdcfa1fd 100644 --- a/src/routes/projects/list.spec.js +++ b/src/routes/projects/list.spec.js @@ -387,5 +387,109 @@ describe('LIST Project', () => { } }); }); + + describe('GET All /projects/ for Connect Admin, ', () => { + it('should return the project ', (done) => { + request(server) + .get('/v4/projects/?fields=id%2Cmembers.id') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + done(); + } + }); + }); + + it('should return all projects, that match when filtering by name', (done) => { + request(server) + .get('/v4/projects/?filter=keyword%3Dtest') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + done(); + } + }); + }); + + it('should return the project, when filtering by keyword, which matches the name', (done) => { + request(server) + .get('/v4/projects/?filter=keyword%3D1') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(1); + resJson[0].name.should.equal('test1'); + done(); + } + }); + }); + + it('should return the project, when filtering by keyword, which matches the description', (done) => { + request(server) + .get('/v4/projects/?filter=keyword%3Dproject') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + done(); + } + }); + }); + + it('should return the project, when filtering by keyword, which matches the member handle', (done) => { + request(server) + .get('/v4/projects/?filter=keyword%3Dtourist') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(1); + resJson[0].name.should.equal('test1'); + done(); + } + }); + }); + }); }); }); diff --git a/src/routes/projects/update.spec.js b/src/routes/projects/update.spec.js index 03e27cc1..520d93db 100644 --- a/src/routes/projects/update.spec.js +++ b/src/routes/projects/update.spec.js @@ -127,7 +127,7 @@ describe('Project', () => { }); }); - it('should return 403 if invalid user will launch a project', (done) => { + it('should return 403 if invalid user will update a project', (done) => { request(server) .patch(`/v4/projects/${project1.id}`) .set({ @@ -154,7 +154,7 @@ describe('Project', () => { }); }); - it('should return 200 if topcoder manager user will launch a project', (done) => { + it('should return 200 if topcoder manager user will update a project', (done) => { request(server) .patch(`/v4/projects/${project1.id}`) .set({ @@ -723,5 +723,60 @@ describe('Project', () => { } }); }); + + xdescribe('for connect admin, ', () => { + it('should return 200, connect admin is allowed to transition project out of cancel status', (done) => { + models.Project.update({ + status: PROJECT_STATUS.CANCELLED, + }, { + where: { + id: project1.id, + }, + }) + .then(() => { + const mbody = { + param: { + name: 'updatedProject name', + status: PROJECT_STATUS.ACTIVE, + }, + }; + request(server) + .patch(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send(mbody) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.name.should.equal('updatedProject name'); + resJson.updatedAt.should.not.equal('2016-06-30 00:33:07+00'); + resJson.updatedBy.should.equal(40051333); + server.services.pubsub.publish.calledWith('project.updated').should.be.true; + // validate that project history is updated + models.ProjectHistory.findAll({ + where: { + projectId: project1.id, + }, + }).then((histories) => { + should.exist(histories); + histories.length.should.equal(1); + const history = histories[0].get({ + plain: true, + }); + history.status.should.equal(PROJECT_STATUS.ACTIVE); + history.projectId.should.equal(project1.id); + done(); + }); + } + }); + }); + }); + }); }); }); diff --git a/src/tests/util.js b/src/tests/util.js index 77be2e78..ded1ff9f 100644 --- a/src/tests/util.js +++ b/src/tests/util.js @@ -22,5 +22,7 @@ export default { manager: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29ubmVjdCBNYW5hZ2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzQiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.J5VtOEQVph5jfe2Ji-NH7txEDcx_5gthhFeD-MzX9ck', // userId = 40051335, [ 'Topcoder User' ],handle: 'member2',email: 'test@topcoder.com' member2: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJtZW1iZXIyIiwiZXhwIjoyNTYzMDc2Njg5LCJ1c2VySWQiOiI0MDA1MTMzNSIsImlhdCI6MTQ2MzA3NjA4OSwiZW1haWwiOiJ0ZXN0QHRvcGNvZGVyLmNvbSIsImp0aSI6ImIzM2I3N2NkLWI1MmUtNDBmZS04MzdlLWJlYjhlMGFlNmE0YSJ9.Mh4bw3wm-cn5Kcf96gLFVlD0kySOqqk4xN3qnreAKL4', + // userId = 40051336, [ 'Connect Admin' ], handle: 'connect_admin1', email: 'connect_admin1@topcoder.com' + connectAdmin: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJDb25uZWN0IEFkbWluIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJjb25uZWN0X2FkbWluMSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzYiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoiY29ubmVjdF9hZG1pbjFAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.nSGfXMl02NZ90ZKLiEKPg75iAjU92mfteaY6xgqkM30', }, }; From c2c987dbdddf761511001dac524b56c060419571 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 6 Dec 2017 16:06:07 +0530 Subject: [PATCH 07/10] Fixing old unit test, it should not have worked in dev env as well, but strange enough it works there but not on local (which is correct) --- src/routes/projectMembers/update.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/projectMembers/update.spec.js b/src/routes/projectMembers/update.spec.js index e45026ad..9497c416 100644 --- a/src/routes/projectMembers/update.spec.js +++ b/src/routes/projectMembers/update.spec.js @@ -267,7 +267,7 @@ describe('Project members update', () => { resJson.isPrimary.should.be.false; resJson.updatedBy.should.equal(40051332); deleteSpy.should.have.been.calledOnce; - server.services.pubsub.publish.calledWith('project.member.removed').should.be.true; + server.services.pubsub.publish.calledWith('project.member.updated').should.be.true; done(); } }); From 91330b040524c25e7070e630eed6bd01a9d2306b Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 12 Dec 2017 14:02:28 +0530 Subject: [PATCH 08/10] Removed debug logs --- src/util.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/util.js b/src/util.js index 6bc3d0ce..56e6c8ba 100644 --- a/src/util.js +++ b/src/util.js @@ -228,7 +228,6 @@ _.assignIn(util, { getSystemUserToken: (logger, id = 'system') => { const httpClient = util.getHttpClient({ id, log: logger }); const url = `${config.get('identityServiceEndpoint')}authorizations`; - console.log(url, 'url'); const formData = `clientId=${config.get('systemUserClientId')}&` + `secret=${encodeURIComponent(config.get('systemUserClientSecret'))}`; return httpClient.post(url, formData, @@ -292,11 +291,8 @@ _.assignIn(util, { */ getMemberDetailsByUserIds: Promise.coroutine(function* (userIds, logger, requestId) { // eslint-disable-line func-names try { - console.log('getMemberDetailsByUserIds'); - const token = yield 'farzi';// this.getSystemUserToken(logger); - console.log('token', token); + const token = this.getSystemUserToken(logger); const httpClient = this.getHttpClient({ id: requestId, log: logger }); - console.log(config.memberServiceEndpoint, 'config.memberServiceEndpoint'); return httpClient.get(`${config.memberServiceEndpoint}/_search`, { params: { query: `${userIds.join(urlencode(' OR ', 'utf8'))}`, From e83f84ead6b4b27acab6818e00278969c2c3d592 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 12 Dec 2017 14:07:20 +0530 Subject: [PATCH 09/10] Fixed wrong cleanup in previous commit --- src/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.js b/src/util.js index 56e6c8ba..27df14ea 100644 --- a/src/util.js +++ b/src/util.js @@ -291,7 +291,7 @@ _.assignIn(util, { */ getMemberDetailsByUserIds: Promise.coroutine(function* (userIds, logger, requestId) { // eslint-disable-line func-names try { - const token = this.getSystemUserToken(logger); + const token = yield this.getSystemUserToken(logger); const httpClient = this.getHttpClient({ id: requestId, log: logger }); return httpClient.get(`${config.memberServiceEndpoint}/_search`, { params: { From cb962e9035e3e2919e0f5d55846db57221e6cdfe Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 20 Dec 2017 16:32:05 +0530 Subject: [PATCH 10/10] Removed feature branch from deployable branch list --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index ac34c5e4..f5993dff 100644 --- a/circle.yml +++ b/circle.yml @@ -24,7 +24,7 @@ dependencies: deployment: development: - branch: [dev, 'feature/connect-admin-role'] + branch: dev commands: - ./ebs_deploy.sh tc-project-service DEV $CIRCLE_BUILD_NUM