Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/permissions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = () => {
Authorizer.setPolicy('project.view', projectView);
Authorizer.setPolicy('project.edit', projectEdit);
Authorizer.setPolicy('project.delete', projectDelete);
Authorizer.setPolicy('project.getMember', projectView);
Authorizer.setPolicy('project.addMember', projectView);
Authorizer.setPolicy('project.listMembers', projectView);
Authorizer.setPolicy('project.removeMember', projectMemberDelete);
Expand Down Expand Up @@ -86,6 +87,7 @@ module.exports = () => {
Authorizer.setPolicy('projectMemberInvite.create', projectView);
Authorizer.setPolicy('projectMemberInvite.put', true);
Authorizer.setPolicy('projectMemberInvite.get', true);
Authorizer.setPolicy('projectMemberInvite.list', projectView);

Authorizer.setPolicy('form.create', projectAdmin);
Authorizer.setPolicy('form.edit', projectAdmin);
Expand Down
4 changes: 4 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ router.route('/v4/projects/:projectId(\\d+)/members')
.post(require('./projectMembers/create'));

router.route('/v4/projects/:projectId(\\d+)/members/:id(\\d+)')
.get(require('./projectMembers/get'))
.delete(require('./projectMembers/delete'))
.patch(require('./projectMembers/update'));

Expand Down Expand Up @@ -230,6 +231,9 @@ router.route('/v4/timelines/metadata/milestoneTemplates/:milestoneTemplateId(\\d
.patch(require('./milestoneTemplates/update'))
.delete(require('./milestoneTemplates/delete'));

router.route('/v4/projects/:projectId(\\d+)/members/invites')
.get(require('./projectMemberInvites/list'));

router.route('/v4/projects/:projectId(\\d+)/members/invite')
.post(require('./projectMemberInvites/create'))
.put(require('./projectMemberInvites/update'))
Expand Down
62 changes: 42 additions & 20 deletions src/routes/projectMemberInvites/get.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@


import _ from 'lodash';
import Joi from 'joi';
import validate from 'express-validation';
import { middleware as tcMiddleware } from 'tc-core-library-js';
import models from '../../models';
import util from '../../util';
Expand All @@ -9,27 +9,49 @@ import util from '../../util';
* API to update invite member to project.
*
*/
const schema = {
query: {
fields: Joi.string().optional(),
},
};
const permissions = tcMiddleware.permissions;

module.exports = [
// handles request validations
validate(schema),
permissions('projectMemberInvite.get'),
(req, res, next) => {
const projectId = _.parseInt(req.params.projectId);
const currentUserId = req.authUser.userId;
let invite;
return models.ProjectMemberInvite.getPendingInviteByEmailOrUserId(projectId, req.authUser.email, currentUserId)
.then((_invite) => {
invite = _invite;
if (!invite) {
// check there is an existing invite for the user with status PENDING
// handle 404
const err = new Error('invite not found for project id ' +
`${projectId}, userId ${currentUserId}, email ${req.authUser.email}`);
err.status = 404;
return next(err);
}
return res.json(util.wrapResponse(req.id, invite));
});
async (req, res, next) => {
try {
const projectId = _.parseInt(req.params.projectId);
const currentUserId = req.authUser.userId;
const memberFields = _.keys(models.ProjectMemberInvite.attributes);
const invite = await models.ProjectMemberInvite.getPendingInviteByEmailOrUserId(
projectId, req.authUser.email, currentUserId,
);
if (!invite) {
// check there is an existing invite for the user with status PENDING
// handle 404
const err = new Error(
'invite not found for project id ' +
`${projectId}, userId ${currentUserId}, email ${req.authUser.email}`,
);
err.status = 404;
throw err;
}

let fields = null;
if (req.query.fields) {
fields = req.query.fields.split(',');
}
const opts = {
logger: req.log,
requestId: req.id,
memberFields,
};
const inviteWithDetails = await util.getObjectsWithMemberDetails(invite, fields, opts);

return res.json(util.wrapResponse(req.id, inviteWithDetails));
} catch (err) {
return next(err);
}
},
];
46 changes: 46 additions & 0 deletions src/routes/projectMemberInvites/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import _ from 'lodash';
import Joi from 'joi';
import validate from 'express-validation';
import { middleware as tcMiddleware } from 'tc-core-library-js';
import models from '../../models';
import util from '../../util';

/**
* API to list all project member invites.
*
*/
const permissions = tcMiddleware.permissions;

const schema = {
query: {
fields: Joi.string().optional(),
},
};

module.exports = [
validate(schema),
permissions('projectMemberInvite.list'),
async (req, res, next) => {
try {
let fields = null;
if (req.query.fields) {
fields = req.query.fields.split(',');
}
const memberFields = _.keys(models.ProjectMemberInvite.attributes);
const projectId = _.parseInt(req.params.projectId);
const invites = await models.ProjectMemberInvite.findAll({
where: { projectId },
raw: true,
});
const opts = {
logger: req.log,
requestId: req.id,
memberFields,
};
const invitesWithDetails = await util.getObjectsWithMemberDetails(invites, fields, opts);
return res.json(util.wrapResponse(req.id, invitesWithDetails));
} catch (err) {
return next(err);
}
},
];
45 changes: 45 additions & 0 deletions src/routes/projectMembers/get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@


import _ from 'lodash';
import Joi from 'joi';
import validate from 'express-validation';
import { middleware as tcMiddleware } from 'tc-core-library-js';
import models from '../../models';
import util from '../../util';

/**
* API to get a project member in a project.
*/
const permissions = tcMiddleware.permissions;

const schema = {
query: {
fields: Joi.string().optional(),
},
};

module.exports = [
validate(schema),
permissions('project.getMember'),
async (req, res, next) => {
try {
let fields = null;
if (req.query.fields) {
fields = req.query.fields.split(',');
}
const memberFields = _.keys(models.ProjectMember.attributes);
const memberId = _.parseInt(req.params.id);
const members = [_.find(req.context.currentProjectMembers, user => user.id === memberId)];
const [member] = await util.getObjectsWithMemberDetails(
members, fields, {
logger: req.log,
requestId: req.id,
memberFields,
},
);
return res.json(util.wrapResponse(req.id, member));
} catch (err) {
return next(err);
}
},
];
142 changes: 30 additions & 112 deletions src/routes/projectMembers/list.js
Original file line number Diff line number Diff line change
@@ -1,124 +1,42 @@
/**
* Endpoint to list project members.
*/
import _ from 'lodash';
import Joi from 'joi';
import validate from 'express-validation';
import { middleware as tcMiddleware } from 'tc-core-library-js';
import models from '../../models';
import util from '../../util';

/**
* API to list all project members.
*
*/
const permissions = tcMiddleware.permissions;

const schema = {
query: {
fields: Joi.string().optional(),
},
};

module.exports = [
validate(schema),
permissions('project.listMembers'),
async (req, res) => {
let members = req.context.currentProjectMembers;


if (members.length && _.get(req, 'query.fields')) {
const fields = req.query.fields.split(',');

const ModelFields = [
'id',
'userId',
'role',
'isPrimary',
'deletedAt',
'createdAt',
'updatedAt',
'deletedBy',
'createdBy',
'updatedBy',
];

const modelFields = _.intersection(ModelFields, fields);
const hasUserIdField = _.indexOf(fields, 'userId') !== -1;
if (hasUserIdField === false) {
modelFields.push('userId');
}
// merge model fields
members = _.map(members, m => _.pick(m, modelFields));

const MemberDetailFields = ['handle', 'firstName', 'lastName'];
const memberDetailFields = _.intersection(MemberDetailFields, fields);

// has handleField
const hasHandleField = _.indexOf(memberDetailFields, 'handle') !== -1;

if (hasHandleField === false) {
memberDetailFields.push('handle');
}

if (memberDetailFields.length) {
const userIds = _.map(members, m => `userId:${m.userId}`) || [];

const logger = req.log;
try {
const memberDetails = await util.getMemberDetailsByUserIds(userIds, logger, req.id);

// merge detail fields
_.forEach(members, (m) => {
const detail = _.find(memberDetails, mt => mt.userId === m.userId);
if (detail) {
_.assign(m, _.pick(detail, memberDetailFields));
}
});


const TraitFields = ['photoURL', 'workingHourStart', 'workingHourEnd', 'timeZone'];
const traitFields = _.intersection(TraitFields, fields);
if (traitFields.length) {
const promises = [];
_.forEach(members, (m) => {
if (m.handle) {
promises.push(util.getMemberTratisByHandle(m.handle, req.log, req.id));
}
});

const traits = await Promise.all(promises);

// remove photoURL, because connect_info also has photoURL
const ConnectInfoFields = ['workingHourStart', 'workingHourEnd', 'timeZone'];
const connectInfoFields = _.intersection(ConnectInfoFields, fields);

// merge traits
_.forEach(members, (m) => {
const traitsArr = _.find(traits, t => t[0].userId === m.userId);
if (traitsArr) {
if (traitFields[0] === 'photoURL') {
_.assign(m, {
photoURL: _.get(_.find(traitsArr, { traitId: 'basic_info' }), 'traits.data[0].photoURL'),
});
if (traitFields.length > 1) {
const traitInfo = _.get(_.find(traitsArr, { traitId: 'connect_info' }), 'traits.data[0]', {});
_.assign(m, _.pick(traitInfo, connectInfoFields));
}
} else {
const traitInfo = _.get(_.find(traitsArr, { traitId: 'connect_info' }), 'traits.data[0]', {});
_.assign(m, _.pick(traitInfo, connectInfoFields));
}
}
});
}
} catch (e) {
logger.error('Error getting member details', e);
if (hasUserIdField === false) {
members = _.map(members, m => _.omit(m, ['userId']));
}
if (hasHandleField === false) {
members = _.map(members, m => _.omit(m, ['handle']));
}
return res.json(util.wrapResponse(req.id, members));
}
}

if (hasUserIdField === false) {
members = _.map(members, m => _.omit(m, ['userId']));
}
if (hasHandleField === false) {
members = _.map(members, m => _.omit(m, ['handle']));
}
async (req, res, next) => {
let fields = null;
if (req.query.fields) {
fields = req.query.fields.split(',');
}
try {
const memberFields = _.keys(models.ProjectMember.attributes);
const members = await util.getObjectsWithMemberDetails(
req.context.currentProjectMembers, fields, {
logger: req.log,
requestId: req.id,
memberFields,
},
);
return res.json(util.wrapResponse(req.id, members));
} catch (err) {
return next(err);
}

return res.json(util.wrapResponse(req.id, req.context.currentProjectMembers));
},
];
Loading