Skip to content

chenges to add universal member to groups #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 13, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
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 config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ module.exports = {
KAFKA_GROUP_DELETE_TOPIC: process.env.KAFKA_GROUP_DELETE_TOPIC || 'groups.notification.delete',
KAFKA_GROUP_MEMBER_ADD_TOPIC: process.env.KAFKA_GROUP_MEMBER_ADD_TOPIC || 'groups.notification.member.add',
KAFKA_GROUP_MEMBER_DELETE_TOPIC: process.env.KAFKA_GROUP_MEMBER_DELETE_TOPIC || 'groups.notification.member.delete',
KAFKA_GROUP_UNIVERSAL_MEMBER_ADD_TOPIC: process.env.KAFKA_GROUP_UNIVERSAL_MEMBER_ADD_TOPIC || 'groups.notification.universalmember.add',
KAFKA_GROUP_UNIVERSAL_MEMBER_DELETE_TOPIC: process.env.KAFKA_GROUP_UNIVERSAL_MEMBER_DELETE_TOPIC || 'groups.notification.universalmember.delete',

USER_ROLES: {
Admin: 'Administrator',
Expand Down
36 changes: 18 additions & 18 deletions src/common/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,36 +74,36 @@ async function ensureExists(tx, model, id, isAdmin = false) {
if (model === 'Group') {
if (validate(id, 4)) {
if (!isAdmin) {
res = await tx.run(`MATCH (e:${model} {id: {id}, status: '${constants.GroupStatus.Active}'}) RETURN e`, {
id
})
res = await tx.run(`MATCH (e:${model} {id: {id}, status: '${constants.GroupStatus.Active}'}) RETURN e`, {id})
} else {
res = await tx.run(`MATCH (e:${model} {id: {id}}) RETURN e`, {
id
})
res = await tx.run(`MATCH (e:${model} {id: {id}}) RETURN e`, {id})
}
} else {
if (!isAdmin) {
res = await tx.run(`MATCH (e:${model} {oldId: {id}, status: '${constants.GroupStatus.Active}'}) RETURN e`, {
id
})
} else {
res = await tx.run(`MATCH (e:${model} {oldId: {id}}) RETURN e`, {
id
})
res = await tx.run(`MATCH (e:${model} {oldId: {id}}) RETURN e`, {id})
}
}

if (res && res.records.length === 0) {
throw new errors.NotFoundError(`Not found ${model} of id ${id}`)
}
} else if (model === 'User') {
res = await tx.run(`MATCH (e:${model} {id: {id}}) RETURN e`, {
id
})
if (validate(id, 4)) {
res = await tx.run(`MATCH (e:${model} {universalUID: {id}}) RETURN e`, {id})

if (res && res.records.length === 0) {
res = await tx.run(`CREATE (user:User {id: {id}}) RETURN user`, { id })
if (res && res.records.length === 0) {
res = await tx.run(`CREATE (user:User {id: '00000000', universalUID: {id}}) RETURN user`, {id})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the Id field not allowed to be null ?

}
} else {
res = await tx.run(`MATCH (e:${model} {id: {id}}) RETURN e`, {id})

if (res && res.records.length === 0) {
res = await tx.run(`CREATE (user:User {id: {id}, universalUID: '00000000'}) RETURN user`, {id})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we put a null in instead of zeros?

}
}
}

Expand All @@ -123,7 +123,7 @@ async function ensureGroupMember(session, groupId, userId) {
try {
const memberCheckRes = await session.run(
'MATCH (g:Group {id: {groupId}})-[r:GroupContains {type: {membershipType}}]->(u:User {id: {userId}}) RETURN r',
{ groupId, membershipType: config.MEMBERSHIP_TYPES.User, userId }
{groupId, membershipType: config.MEMBERSHIP_TYPES.User, userId}
)
if (memberCheckRes.records.length === 0) {
throw new errors.ForbiddenError(`User is not member of the group`)
Expand All @@ -143,7 +143,7 @@ async function getChildGroups(session, groupId) {
try {
const res = await session.run(
'MATCH (g:Group {id: {groupId}})-[r:GroupContains]->(c:Group) RETURN c ORDER BY c.oldId',
{ groupId }
{groupId}
)
return _.map(res.records, (record) => record.get(0).properties)
} catch (error) {
Expand All @@ -161,7 +161,7 @@ async function getParentGroups(session, groupId) {
try {
const res = await session.run(
'MATCH (g:Group)-[r:GroupContains]->(c:Group {id: {groupId}}) RETURN g ORDER BY g.oldId',
{ groupId }
{groupId}
)
return _.map(res.records, (record) => record.get(0).properties)
} catch (error) {
Expand All @@ -176,7 +176,7 @@ async function getParentGroups(session, groupId) {
* @returns {String} link for the page
*/
function getPageLink(req, page) {
const q = _.assignIn({}, req.query, { page })
const q = _.assignIn({}, req.query, {page})
return `${req.protocol}://${req.get('Host')}${req.baseUrl}${req.path}?${querystring.stringify(q)}`
}

Expand Down
21 changes: 15 additions & 6 deletions src/controllers/GroupMembershipController.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,21 @@ async function getGroupMembers(req, res) {
* @param res the response
*/
async function addGroupMember(req, res) {
const result = await service.addGroupMember(
req.authUser.isMachine ? 'M2M' : req.authUser,
req.params.groupId,
req.body
)
res.send(result)
if(req.body.universalUID) {
const result = await service.addUniversalMember(
req.authUser.isMachine ? 'M2M' : req.authUser,
req.params.groupId,
req.body
)
res.send(result)
} else {
const result = await service.addGroupMember(
req.authUser.isMachine ? 'M2M' : req.authUser,
req.params.groupId,
req.body
)
res.send(result)
}
}

/**
Expand Down
115 changes: 106 additions & 9 deletions src/services/GroupMembershipService.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ async function addGroupMember(currentUser, groupId, data) {
const targetObjectType = data.membershipType === config.MEMBERSHIP_TYPES.Group ? 'Group' : 'User'
const memberCheckRes = await tx.run(
`MATCH (g:Group {id: {groupId}})-[r:GroupContains]->(o:${targetObjectType} {id: {memberId}}) RETURN o`,
{ groupId, memberId: data.memberId }
{groupId, memberId: data.memberId}
)
if (memberCheckRes.records.length > 0) {
throw new errors.ConflictError('The member is already in the group')
Expand All @@ -77,7 +77,7 @@ async function addGroupMember(currentUser, groupId, data) {
if (data.membershipType === config.MEMBERSHIP_TYPES.Group) {
const pathRes = await tx.run(
'MATCH p=shortestPath( (g1:Group {id: {fromId}})-[*]->(g2:Group {id: {toId}}) ) RETURN p',
{ fromId: data.memberId, toId: groupId }
{fromId: data.memberId, toId: groupId}
)
if (pathRes.records.length > 0) {
throw new errors.ConflictError('There is cyclical group reference')
Expand Down Expand Up @@ -107,9 +107,9 @@ async function addGroupMember(currentUser, groupId, data) {
oldId: data.oldId,
name: group.name,
createdAt,
...(currentUser === 'M2M' ? {} : { createdBy: currentUser.userId }),
...(currentUser === 'M2M' ? {} : {createdBy: currentUser.userId}),
memberId: data.memberId,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we not using the M2M clientId for the createdUser/updatedUser values ?

...(data.memberOldId ? { memberOldId: data.memberOldId } : {}),
...(data.memberOldId ? {memberOldId: data.memberOldId} : {}),
membershipType: data.membershipType
}

Expand Down Expand Up @@ -174,7 +174,7 @@ async function deleteGroupMember(currentUser, groupId, memberId) {

// delete membership
const query = 'MATCH (g:Group {id: {groupId}})-[r:GroupContains]->(o {id: {memberId}}) DELETE r'
await tx.run(query, { groupId, memberId })
await tx.run(query, {groupId, memberId})

if (validate(memberId, 4)) {
const getMember = await helper.ensureExists(tx, 'Group', memberId)
Expand Down Expand Up @@ -210,6 +210,102 @@ deleteGroupMember.schema = {
memberId: Joi.id()
}

/**
* Add universal member.
* @param {Object} currentUser the current user
* @param {String} groupId the id of group to add member
* @param {Object} data the data to add member
* @returns {Object} the added group membership
*/
async function addUniversalMember(currentUser, groupId, data) {
logger.debug(`Enter in addGroupMember - Group = ${groupId} Criteria = ${data}`)
let session = helper.createDBSession()
let tx = session.beginTransaction()

try {
logger.debug(`Check for groupId ${groupId} exist or not`)
const group = await helper.ensureExists(
tx,
'Group',
groupId,
currentUser !== 'M2M' && helper.hasAdminRole(currentUser)
)
data.oldId = group.oldId
groupId = group.id

if (currentUser !== 'M2M' && !helper.hasAdminRole(currentUser)) {
throw new errors.ForbiddenError('You are not allowed to perform this action!')
}

logger.debug(`Check for memberId ${data.universalUID} exist or not`)
await helper.ensureExists(tx, 'User', data.universalUID)

logger.debug(`check member ${data.universalUID} is part of group ${groupId}`)
const memberCheckRes = await tx.run(
`MATCH (g:Group {id: {groupId}})-[r:GroupContains]->(o:User {universalUID: {universalUID}}) RETURN o`,
{groupId, universalUID: data.universalUID}
)
if (memberCheckRes.records.length > 0) {
throw new errors.ConflictError('The member is already in the group')
}

// add membership
const membershipId = uuid()
const createdAt = new Date().toISOString()
const query = `MATCH (g:Group {id: {groupId}}) MATCH (o:User {universalUID: {universalUID}}) CREATE (g)-[r:GroupContains {universalUID: {universalUID}, type: {membershipType}, createdAt: {createdAt}, createdBy: {createdBy}}]->(o) RETURN r`

const params = {
groupId,
universalUID: data.universalUID,
membershipId,
membershipType: data.membershipType,
createdAt,
createdBy: currentUser === 'M2M' ? '00000000' : currentUser.userId
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use null instead of zeros?

}

logger.debug(`quey for adding membership ${query} with params ${JSON.stringify(params)}`)
await tx.run(query, params)

const result = {
id: membershipId,
groupId,
oldId: data.oldId,
name: group.name,
createdAt,
...(currentUser === 'M2M' ? {} : {createdBy: currentUser.userId}),
memberId: '00000000',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullable?

universalUID: data.universalUID,
handle: data.handle,
...(data.memberOldId ? {memberOldId: data.memberOldId} : {}),
membershipType: data.membershipType
}

logger.debug(`sending message ${JSON.stringify(result)} to kafka topic ${config.KAFKA_GROUP_MEMBER_ADD_TOPIC}`)
await helper.postBusEvent(config.KAFKA_GROUP_MEMBER_ADD_TOPIC, result)

await tx.commit()
return result
} catch (error) {
logger.error(error)
logger.debug('Transaction Rollback')
await tx.rollback()
throw error
} finally {
logger.debug('Session Close')
await session.close()
}
}

addUniversalMember.schema = {
currentUser: Joi.any(),
groupId: Joi.id(), // defined in app-bootstrap
data: Joi.object()
.keys({
universalUID: Joi.id(),
handle: Joi.string().required()
})
.required()
}
/**
* Get group members
* @param {Object} currentUser the current user
Expand All @@ -235,7 +331,7 @@ async function getGroupMembers(currentUser, groupId, criteria) {
}

const matchClause = 'MATCH (g:Group {id: {groupId}})-[r:GroupContains]->(o)'
const params = { groupId }
const params = {groupId}

// query total record count
const totalRes = await session.run(`${matchClause} RETURN COUNT(o)`, params)
Expand Down Expand Up @@ -299,7 +395,7 @@ async function getGroupMemberWithSession(session, groupId, memberId) {
groupId = group.id

const query = 'MATCH (g:Group {id: {groupId}})-[r:GroupContains]->(o {id: {memberId}}) RETURN r'
const membershipRes = await session.run(query, { groupId, memberId })
const membershipRes = await session.run(query, {groupId, memberId})
if (membershipRes.records.length === 0) {
throw new errors.NotFoundError('The member is not in the group')
}
Expand Down Expand Up @@ -375,9 +471,9 @@ async function getGroupMembersCount(groupId, query) {
queryToExecute = 'MATCH (g:Group {id: {groupId}})-[r:GroupContains]->(o:User) RETURN COUNT(o) AS count'
}

const res = await session.run(queryToExecute, { groupId })
const res = await session.run(queryToExecute, {groupId})

return { count: res.records[0]._fields[0].low }
return {count: res.records[0]._fields[0].low}
} catch (error) {
logger.error(error)
throw error
Expand Down Expand Up @@ -426,6 +522,7 @@ getMemberGroups.schema = {
module.exports = {
getGroupMembers,
addGroupMember,
addUniversalMember,
getGroupMember,
deleteGroupMember,
getGroupMembersCount,
Expand Down