From e08bbcc5435cbabaee40a41a7fb667d4a1f078e6 Mon Sep 17 00:00:00 2001 From: Sabir Hassan Date: Wed, 15 Jun 2022 15:18:42 +0500 Subject: [PATCH] fix: add/remove group to User when adding/removing user from group and return group membership on getting user --- api/public/swagger.yaml | 34 ++++++++++++++++------------ api/src/controllers/group.ts | 5 +++- api/src/controllers/user.ts | 13 +++++++++-- api/src/model/User.ts | 24 ++++++++++++++++++++ api/src/routes/api/spec/user.spec.ts | 32 +++++++++++++++++++++++++- 5 files changed, 89 insertions(+), 19 deletions(-) diff --git a/api/public/swagger.yaml b/api/public/swagger.yaml index 7e761fe..dd34dd8 100644 --- a/api/public/swagger.yaml +++ b/api/public/swagger.yaml @@ -310,6 +310,21 @@ components: - displayName type: object additionalProperties: false + GroupResponse: + properties: + groupId: + type: number + format: double + name: + type: string + description: + type: string + required: + - groupId + - name + - description + type: object + additionalProperties: false UserDetailsResponse: properties: id: @@ -325,6 +340,10 @@ components: type: boolean autoExec: type: string + groups: + items: + $ref: '#/components/schemas/GroupResponse' + type: array required: - id - displayName @@ -364,21 +383,6 @@ components: - password type: object additionalProperties: false - GroupResponse: - properties: - groupId: - type: number - format: double - name: - type: string - description: - type: string - required: - - groupId - - name - - description - type: object - additionalProperties: false GroupDetailsResponse: properties: groupId: diff --git a/api/src/controllers/group.ts b/api/src/controllers/group.ts index 44adef2..b3cbb0f 100644 --- a/api/src/controllers/group.ts +++ b/api/src/controllers/group.ts @@ -14,7 +14,7 @@ import Group, { GroupPayload } from '../model/Group' import User from '../model/User' import { UserResponse } from './user' -interface GroupResponse { +export interface GroupResponse { groupId: number name: string description: string @@ -210,6 +210,9 @@ const updateUsersListInGroup = async ( if (!updatedGroup) throw new Error('Unable to update group') + if (action === 'addUser') user.addGroup(group._id) + else user.removeGroup(group._id) + return { groupId: updatedGroup.groupId, name: updatedGroup.name, diff --git a/api/src/controllers/user.ts b/api/src/controllers/user.ts index 99a8f9b..cbb3d81 100644 --- a/api/src/controllers/user.ts +++ b/api/src/controllers/user.ts @@ -18,6 +18,7 @@ import { desktopUser } from '../middlewares' import User, { UserPayload } from '../model/User' import { getUserAutoExec, updateUserAutoExec, ModeType } from '../utils' +import { GroupResponse } from './group' export interface UserResponse { id: number @@ -32,6 +33,7 @@ interface UserDetailsResponse { isActive: boolean isAdmin: boolean autoExec?: string + groups?: GroupResponse[] } @Security('bearerAuth') @@ -242,7 +244,13 @@ const getUser = async ( findBy: GetUserBy, getAutoExec: boolean ): Promise => { - const user = await User.findOne(findBy) + const user = (await User.findOne( + findBy, + `id displayName username isActive isAdmin autoExec -_id` + ).populate( + 'groups', + 'groupId name description -_id' + )) as unknown as UserDetailsResponse if (!user) throw new Error('User is not found.') @@ -252,7 +260,8 @@ const getUser = async ( username: user.username, isActive: user.isActive, isAdmin: user.isAdmin, - autoExec: getAutoExec ? user.autoExec ?? '' : undefined + autoExec: getAutoExec ? user.autoExec ?? '' : undefined, + groups: user.groups } } diff --git a/api/src/model/User.ts b/api/src/model/User.ts index 04d550e..b70807d 100644 --- a/api/src/model/User.ts +++ b/api/src/model/User.ts @@ -45,6 +45,8 @@ interface IUserDocument extends UserPayload, Document { interface IUser extends IUserDocument { comparePassword(password: string): boolean + addGroup(groupObjectId: Schema.Types.ObjectId): Promise + removeGroup(groupObjectId: Schema.Types.ObjectId): Promise } interface IUserModel extends Model { hashPassword(password: string): string @@ -106,6 +108,28 @@ userSchema.method('comparePassword', function (password: string): boolean { if (bcrypt.compareSync(password, this.password)) return true return false }) +userSchema.method( + 'addGroup', + async function (groupObjectId: Schema.Types.ObjectId) { + const groupIdIndex = this.groups.indexOf(groupObjectId) + if (groupIdIndex === -1) { + this.groups.push(groupObjectId) + } + this.markModified('groups') + return this.save() + } +) +userSchema.method( + 'removeGroup', + async function (groupObjectId: Schema.Types.ObjectId) { + const groupIdIndex = this.groups.indexOf(groupObjectId) + if (groupIdIndex > -1) { + this.groups.splice(groupIdIndex, 1) + } + this.markModified('groups') + return this.save() + } +) export const User: IUserModel = model('User', userSchema) diff --git a/api/src/routes/api/spec/user.spec.ts b/api/src/routes/api/spec/user.spec.ts index 36913a3..1c4fd99 100644 --- a/api/src/routes/api/spec/user.spec.ts +++ b/api/src/routes/api/spec/user.spec.ts @@ -3,7 +3,7 @@ import mongoose, { Mongoose } from 'mongoose' import { MongoMemoryServer } from 'mongodb-memory-server' import request from 'supertest' import appPromise from '../../../app' -import { UserController } from '../../../controllers/' +import { UserController, GroupController } from '../../../controllers/' import { generateAccessToken, saveTokensInDB } from '../../../utils' const clientId = 'someclientID' @@ -571,6 +571,7 @@ describe('user', () => { expect(res.body.isAdmin).toEqual(user.isAdmin) expect(res.body.isActive).toEqual(user.isActive) expect(res.body.autoExec).toEqual(user.autoExec) + expect(res.body.groups).toEqual([]) }) it('should respond with user autoExec when admin user requests', async () => { @@ -588,6 +589,7 @@ describe('user', () => { expect(res.body.isAdmin).toEqual(user.isAdmin) expect(res.body.isActive).toEqual(user.isActive) expect(res.body.autoExec).toEqual(user.autoExec) + expect(res.body.groups).toEqual([]) }) it('should respond with user when access token is not of an admin account', async () => { @@ -610,6 +612,34 @@ describe('user', () => { expect(res.body.isAdmin).toEqual(user.isAdmin) expect(res.body.isActive).toEqual(user.isActive) expect(res.body.autoExec).toBeUndefined() + expect(res.body.groups).toEqual([]) + }) + + it('should respond with user along with associated groups', async () => { + const dbUser = await controller.createUser(user) + const userId = dbUser.id + const accessToken = await generateAndSaveToken(userId) + + const group = { + name: 'DCGroup1', + description: 'DC group for testing purposes.' + } + const groupController = new GroupController() + const dbGroup = await groupController.createGroup(group) + await groupController.addUserToGroup(dbGroup.groupId, dbUser.id) + + const res = await request(app) + .get(`/SASjsApi/user/${userId}`) + .auth(accessToken, { type: 'bearer' }) + .send() + .expect(200) + + expect(res.body.username).toEqual(user.username) + expect(res.body.displayName).toEqual(user.displayName) + expect(res.body.isAdmin).toEqual(user.isAdmin) + expect(res.body.isActive).toEqual(user.isActive) + expect(res.body.autoExec).toEqual(user.autoExec) + expect(res.body.groups.length).toBeGreaterThan(0) }) it('should respond with Unauthorized if access token is not present', async () => {