Skip to content

Commit 7129299

Browse files
feat: archive users/members from the members page (#427)
* feat: archive members * generated api schema * added: check if user is already archived or not * bug fix * added: optional chaining * added: move to member model. * added: integration test for testing archive member feature * key changed to camelCase * Added: requested changes * added: requested changes * added: requested changes * bug fix * fix: typo * removed unwanted code. * removed unwanted code * Fixed schema * added: requested changes * added: requested changes
1 parent 5ec05e8 commit 7129299

File tree

8 files changed

+218
-22
lines changed

8 files changed

+218
-22
lines changed

controllers/members.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { ROLES } = require('../constants/users')
2-
const { fetchUsers, migrateUsers, deleteIsMemberProperty, fetchUsersWithRole, moveToMembers: updateToMemberRole } = require('../models/members')
2+
const members = require('../models/members')
33
const tasks = require('../models/tasks')
44
const { fetchUser } = require('../models/users')
55

@@ -14,7 +14,7 @@ const ERROR_MESSAGE = 'Something went wrong. Please try again or contact admin'
1414

1515
const getMembers = async (req, res) => {
1616
try {
17-
const allUsers = await fetchUsers()
17+
const allUsers = await members.fetchUsers()
1818

1919
return res.json({
2020
message: allUsers.length ? 'Members returned successfully!' : 'No member found',
@@ -35,7 +35,7 @@ const getMembers = async (req, res) => {
3535

3636
const getIdleMembers = async (req, res) => {
3737
try {
38-
const onlyMembers = await fetchUsersWithRole(ROLES.MEMBER)
38+
const onlyMembers = await members.fetchUsersWithRole(ROLES.MEMBER)
3939
const taskParticipants = await tasks.fetchActiveTaskMembers()
4040
const idleMembers = onlyMembers?.filter(({ id }) => !taskParticipants.has(id))
4141
const idleMemberUserNames = idleMembers?.map((member) => member.username)
@@ -62,7 +62,7 @@ const moveToMembers = async (req, res) => {
6262
const { username } = req.params
6363
const result = await fetchUser({ username })
6464
if (result.userExists) {
65-
const successObject = await updateToMemberRole(result.user.id)
65+
const successObject = await members.moveToMembers(result.user.id)
6666
if (successObject.isAlreadyMember) {
6767
return res.boom.badRequest('User is already a member')
6868
}
@@ -84,7 +84,7 @@ const moveToMembers = async (req, res) => {
8484

8585
const migrateUserRoles = async (req, res) => {
8686
try {
87-
const migratedUserData = await migrateUsers()
87+
const migratedUserData = await members.migrateUsers()
8888
return res.json({
8989
message: 'Users migrated successfully',
9090
...migratedUserData
@@ -103,7 +103,7 @@ const migrateUserRoles = async (req, res) => {
103103
*/
104104
const deleteIsMember = async (req, res) => {
105105
try {
106-
const deletedIsMemberData = await deleteIsMemberProperty()
106+
const deletedIsMemberData = await members.deleteIsMemberProperty()
107107
return res.json({
108108
message: 'Users isMember deleted successfully',
109109
...deletedIsMemberData
@@ -114,7 +114,33 @@ const deleteIsMember = async (req, res) => {
114114
}
115115
}
116116

117+
/**
118+
* Archives old member from new members list.
119+
*
120+
* @param req {Object} - Express request object
121+
* @param res {Object} - Express response object
122+
*/
123+
124+
const archiveMembers = async (req, res) => {
125+
try {
126+
const { username } = req.params
127+
const user = await fetchUser({ username })
128+
if (user?.userExists) {
129+
const successObject = await members.addArchiveRoleToMembers(user.user.id)
130+
if (successObject.isArchived) {
131+
return res.boom.badRequest('User is already archived')
132+
}
133+
return res.status(204).send()
134+
}
135+
return res.boom.notFound("User doesn't exist")
136+
} catch (err) {
137+
logger.error(`Error while retriving contributions ${err}`)
138+
return res.boom.badImplementation(ERROR_MESSAGE)
139+
}
140+
}
141+
117142
module.exports = {
143+
archiveMembers,
118144
getMembers,
119145
getIdleMembers,
120146
moveToMembers,

models/members.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,31 @@ const fetchUsersWithRole = async (role) => {
148148
}
149149
}
150150

151+
/**
152+
* changes the role of a new user to member
153+
* @param userId { String }: User id of user to be modified
154+
* @return { Object }: whether moveToMember was successful or not and whether user is already a member or not
155+
*/
156+
157+
const addArchiveRoleToMembers = async (userId) => {
158+
try {
159+
const userDoc = await userModel.doc(userId).get()
160+
const user = userDoc.data()
161+
if (user?.roles?.archivedMember) return { isArchived: true }
162+
const roles = { ...user.roles, archivedMember: true }
163+
await userModel.doc(userId).update({
164+
roles
165+
})
166+
return { isArchived: false }
167+
} catch (err) {
168+
logger.error('Error updating user', err)
169+
throw err
170+
}
171+
}
172+
151173
module.exports = {
152174
moveToMembers,
175+
addArchiveRoleToMembers,
153176
fetchUsers,
154177
migrateUsers,
155178
deleteIsMemberProperty,

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"generate-api-schema": "node utils/generateAPISchema.js"
1515
},
1616
"dependencies": {
17-
"axios": "^0.21.2",
17+
"axios": "^0.21.1",
1818
"cloudinary": "^1.25.1",
1919
"config": "^3.3.6",
2020
"cookie-parser": "~1.4.5",

public/apiSchema.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

routes/members.js

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const express = require('express')
22
const router = express.Router()
3-
const { getMembers, getIdleMembers, migrateUserRoles, deleteIsMember, moveToMembers } = require('../controllers/members')
3+
const members = require('../controllers/members')
44
const { authorizeUser } = require('../middlewares/authorization')
55
const authenticate = require('../middlewares/authenticate')
66
const { addRecruiter } = require('../controllers/recruiters')
@@ -29,7 +29,7 @@ const { SUPER_USER } = require('../constants/roles')
2929
* $ref: '#/components/schemas/errors/badImplementation'
3030
*/
3131

32-
router.get('/', getMembers)
32+
router.get('/', members.getMembers)
3333

3434
/**
3535
* @swagger
@@ -53,7 +53,7 @@ router.get('/', getMembers)
5353
* $ref: '#/components/schemas/errors/badImplementation'
5454
*/
5555

56-
router.get('/idle', getIdleMembers)
56+
router.get('/idle', members.getIdleMembers)
5757

5858
/**
5959
* @swagger
@@ -131,7 +131,7 @@ router.post('/intro/:username', validateRecruiter, addRecruiter)
131131
* $ref: '#/components/schemas/errors/serverUnavailable'
132132
*/
133133

134-
router.patch('/moveToMembers/:username', authenticate, authorizeUser(SUPER_USER), moveToMembers)
134+
router.patch('/moveToMembers/:username', authenticate, authorizeUser(SUPER_USER), members.moveToMembers)
135135
/**
136136
* @swagger
137137
* /members/member-to-role-migration:
@@ -165,7 +165,7 @@ router.patch('/moveToMembers/:username', authenticate, authorizeUser(SUPER_USER)
165165
* schema:
166166
* $ref: '#/components/schemas/errors/badImplementation'
167167
*/
168-
router.patch('/member-to-role-migration', authenticate, authorizeUser('superUser'), migrateUserRoles)
168+
router.patch('/member-to-role-migration', authenticate, authorizeUser('superUser'), members.migrateUserRoles)
169169

170170
/**
171171
* @swagger
@@ -200,6 +200,52 @@ router.patch('/member-to-role-migration', authenticate, authorizeUser('superUser
200200
* schema:
201201
* $ref: '#/components/schemas/errors/badImplementation'
202202
*/
203-
router.patch('/delete-isMember', authenticate, authorizeUser('superUser'), deleteIsMember)
203+
router.patch('/delete-isMember', authenticate, authorizeUser('superUser'), members.deleteIsMember)
204+
205+
/**
206+
* @swagger
207+
* /archiveMembers/:username:
208+
* patch:
209+
* summary: Changes the role of a old member(the username provided in params) in new members list to archive_member
210+
* tags:
211+
* - Members
212+
* security:
213+
* - bearerAuth: []
214+
* responses:
215+
* 204:
216+
* description: no content
217+
* 400:
218+
* description: badRequest
219+
* content:
220+
* application/json:
221+
* schema:
222+
* type: object
223+
* properties:
224+
* message:
225+
* type: string
226+
* example: User Already is a member
227+
*
228+
* 401:
229+
* description: unAuthorized
230+
* content:
231+
* application/json:
232+
* schema:
233+
* $ref: '#/components/schemas/errors/unAuthorized'
234+
* 404:
235+
* description: notFound
236+
* content:
237+
* application/json:
238+
* schema:
239+
* $ref: '#/components/schemas/errors/notFound'
240+
*
241+
* 500:
242+
* description: serverUnavailable
243+
* content:
244+
* application/json:
245+
* schema:
246+
* $ref: '#/components/schemas/errors/serverUnavailable'
247+
*/
248+
249+
router.patch('/archiveMembers/:username', authenticate, authorizeUser(SUPER_USER), members.archiveMembers)
204250

205251
module.exports = router

test/fixtures/user/user.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,28 @@ module.exports = () => {
110110
},
111111
status: 'active',
112112
roles: {
113-
app_owner: true
113+
app_owner: true,
114+
archivedMember: true
115+
}
116+
},
117+
{
118+
username: 'mehul',
119+
first_name: 'Mehul',
120+
last_name: 'Chaudhari',
121+
yoe: 0,
122+
img: './img.png',
123+
github_id: 'mehulkchaudhari',
124+
linkedin_id: 'mehulkchaudhari',
125+
twitter_id: 'mehulkchaudhari',
126+
phone: '1234567891',
127+
128+
tokens: {
129+
githubAccessToken: 'githubAccessToken'
130+
},
131+
status: 'active',
132+
roles: {
133+
member: true,
134+
archivedMember: true
114135
}
115136
}
116137
]

test/integration/members.test.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ const superUser = userData[4]
1919
const userAlreadyMember = userData[0]
2020
const userToBeMadeMember = userData[1]
2121
const nonSuperUser = userData[2]
22+
const userDoesNotExists = userData[1]
23+
const userToBeArchived = userData[3]
24+
const userAlreadyArchived = userData[5]
2225

2326
describe('Members', function () {
2427
let jwt
@@ -185,4 +188,81 @@ describe('Members', function () {
185188
})
186189
})
187190
})
191+
192+
describe('PATCH /members/archiveMembers/:username', function () {
193+
before(async function () {
194+
await cleanDb()
195+
const userId = await addUser(superUser)
196+
jwt = authService.generateAuthToken({ userId })
197+
})
198+
199+
it("Should return 404 if user doesn't exist", function (done) {
200+
chai
201+
.request(app)
202+
.patch(`/members/archiveMembers/${userDoesNotExists.username}`)
203+
.set('cookie', `${cookieName}=${jwt}`)
204+
.end((err, res) => {
205+
if (err) { return done(err) }
206+
expect(res).to.have.status(404)
207+
expect(res.body).to.be.a('object')
208+
expect(res.body.message).to.equal("User doesn't exist")
209+
return done()
210+
})
211+
})
212+
213+
it('Should archive the user', function (done) {
214+
addUser(userToBeArchived).then(() => {
215+
chai
216+
.request(app)
217+
.patch(`/members/archiveMembers/${userToBeArchived.username}`)
218+
.set('cookie', `${cookieName}=${jwt}`)
219+
.end((err, res) => {
220+
if (err) { return done(err) }
221+
222+
expect(res).to.have.status(204)
223+
/* eslint-disable no-unused-expressions */
224+
expect(res.body).to.be.a('object').to.be.empty
225+
226+
return done()
227+
})
228+
})
229+
})
230+
231+
it('Should return 400 if user is already archived', function (done) {
232+
addUser(userAlreadyArchived).then(() => {
233+
chai
234+
.request(app)
235+
.patch(`/members/archiveMembers/${userAlreadyArchived.username}`)
236+
.set('cookie', `${cookieName}=${jwt}`)
237+
.end((err, res) => {
238+
if (err) { return done(err) }
239+
240+
expect(res).to.have.status(400)
241+
expect(res.body).to.be.a('object')
242+
expect(res.body.message).to.equal('User is already archived')
243+
244+
return done()
245+
})
246+
})
247+
})
248+
249+
it('Should return 401 if user is not a super user', function (done) {
250+
addUser(nonSuperUser).then(nonSuperUserId => {
251+
const nonSuperUserJwt = authService.generateAuthToken({ nonSuperUserId })
252+
chai
253+
.request(app)
254+
.patch(`/members/moveToMembers/${nonSuperUser.username}`)
255+
.set('cookie', `${cookieName}=${nonSuperUserJwt}`)
256+
.end((err, res) => {
257+
if (err) { return done(err) }
258+
259+
expect(res).to.have.status(401)
260+
expect(res.body).to.be.a('object')
261+
expect(res.body.message).to.equal('You are not authorized for this action.')
262+
263+
return done()
264+
})
265+
})
266+
})
267+
})
188268
})

0 commit comments

Comments
 (0)