Skip to content

Commit 9f2d7c4

Browse files
remove unverified role on /verify (#2192)
* remove unverified role on /verify * role actually got removed check added * handled edge cases * Update logger error utils/removeDiscordRole.js Co-authored-by: Achintya Chatterjee <[email protected]> * modified changes and edge cases * added detailed messages * fix: requested changes * exported function directly * fix: modified tests --------- Co-authored-by: Achintya Chatterjee <[email protected]>
1 parent c5a70f3 commit 9f2d7c4

File tree

5 files changed

+224
-0
lines changed

5 files changed

+224
-0
lines changed

constants/discordRoles.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const DISCORD_ROLES = {
2+
UNVERIFIED: "unverified",
3+
};

controllers/external-accounts.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ const { getDiscordMembers } = require("../services/discordService");
44
const { addOrUpdate, getUsersByRole, updateUsersInBatch } = require("../models/users");
55
const { retrieveDiscordUsers, fetchUsersForKeyValues } = require("../services/dataAccessLayer");
66
const { EXTERNAL_ACCOUNTS_POST_ACTIONS } = require("../constants/external-accounts");
7+
const { DISCORD_ROLES } = require("../constants/discordRoles");
78
const logger = require("../utils/logger");
89
const { markUnDoneTasksOfArchivedUsersBacklog } = require("../models/tasks");
10+
const removeDiscordRoleUtils = require("../utils/removeDiscordRole");
911

1012
const addExternalAccountData = async (req, res) => {
1113
const createdOn = Date.now();
@@ -46,6 +48,7 @@ const getExternalAccountData = async (req, res) => {
4648
return res.boom.serverUnavailable(SOMETHING_WENT_WRONG);
4749
}
4850
};
51+
4952
const linkExternalAccount = async (req, res) => {
5053
try {
5154
const { id: userId, roles } = req.userData;
@@ -69,6 +72,19 @@ const linkExternalAccount = async (req, res) => {
6972
userId
7073
);
7174

75+
const unverifiedRoleRemovalResponse = await removeDiscordRoleUtils.removeDiscordRole(
76+
req.userData,
77+
attributes.discordId,
78+
undefined,
79+
DISCORD_ROLES.UNVERIFIED
80+
);
81+
82+
if (!unverifiedRoleRemovalResponse.success) {
83+
return res.status(500).json({
84+
message: `User details updated but ${unverifiedRoleRemovalResponse.message}. Please contact admin`,
85+
});
86+
}
87+
7288
return res.status(204).json({ message: "Your discord profile has been linked successfully" });
7389
} catch (error) {
7490
logger.error(`Error getting external account data: ${error}`);

test/integration/external-accounts.test.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const userData = require("../fixtures/user/user")();
1717
const userModel = firestore.collection("users");
1818
const tasksModel = firestore.collection("tasks");
1919
const { EXTERNAL_ACCOUNTS_POST_ACTIONS } = require("../../constants/external-accounts");
20+
const removeDiscordRoleUtils = require("../../utils/removeDiscordRole");
2021
chai.use(chaiHttp);
2122
const cookieName = config.get("userToken.cookieName");
2223

@@ -537,6 +538,11 @@ describe("External Accounts", function () {
537538
expect(getUserResponseBeforeUpdate.body).to.not.have.property("discordId");
538539
expect(getUserResponseBeforeUpdate.body).to.not.have.property("discordJoinedAt");
539540

541+
const removeDiscordRoleStub = Sinon.stub(removeDiscordRoleUtils, "removeDiscordRole").resolves({
542+
success: true,
543+
message: "Role deleted successfully",
544+
});
545+
540546
const response = await chai
541547
.request(app)
542548
.patch(`/external-accounts/link/${externalAccountData[2].token}`)
@@ -553,6 +559,60 @@ describe("External Accounts", function () {
553559
expect(updatedUserDetails.body.roles.in_discord).to.equal(true);
554560
expect(updatedUserDetails.body).to.have.property("discordId");
555561
expect(updatedUserDetails.body).to.have.property("discordJoinedAt");
562+
563+
removeDiscordRoleStub.restore();
564+
});
565+
566+
it("Should return 500 when removeDiscordRole fails because role doesn't exist", async function () {
567+
await externalAccountsModel.addExternalAccountData(externalAccountData[2]);
568+
569+
const removeDiscordRoleStub = Sinon.stub(removeDiscordRoleUtils, "removeDiscordRole").resolves({
570+
success: false,
571+
message: "Role doesn't exist",
572+
});
573+
574+
const response = await chai
575+
.request(app)
576+
.patch(`/external-accounts/link/${externalAccountData[2].token}`)
577+
.query({ action: EXTERNAL_ACCOUNTS_POST_ACTIONS.DISCORD_USERS_SYNC })
578+
.set("Cookie", `${cookieName}=${newUserJWT}`);
579+
580+
const unverifiedRoleRemovalResponse = await removeDiscordRoleStub();
581+
582+
expect(response).to.have.status(500);
583+
expect(response.body).to.be.an("object");
584+
expect(response.body).to.have.property("message");
585+
expect(response.body.message).to.equal(
586+
`User details updated but ${unverifiedRoleRemovalResponse.message}. Please contact admin`
587+
);
588+
589+
removeDiscordRoleStub.restore();
590+
});
591+
592+
it("Should return 500 when removeDiscordRole fails because role deletion failed", async function () {
593+
await externalAccountsModel.addExternalAccountData(externalAccountData[2]);
594+
595+
const removeDiscordRoleStub = Sinon.stub(removeDiscordRoleUtils, "removeDiscordRole").resolves({
596+
success: false,
597+
message: "Role deletion failed",
598+
});
599+
600+
const response = await chai
601+
.request(app)
602+
.patch(`/external-accounts/link/${externalAccountData[2].token}`)
603+
.query({ action: EXTERNAL_ACCOUNTS_POST_ACTIONS.DISCORD_USERS_SYNC })
604+
.set("Cookie", `${cookieName}=${newUserJWT}`);
605+
606+
const unverifiedRoleRemovalResponse = await removeDiscordRoleStub();
607+
608+
expect(response).to.have.status(500);
609+
expect(response.body).to.be.an("object");
610+
expect(response.body).to.have.property("message");
611+
expect(response.body.message).to.equal(
612+
`User details updated but ${unverifiedRoleRemovalResponse.message}. Please contact admin`
613+
);
614+
615+
removeDiscordRoleStub.restore();
556616
});
557617
});
558618
});
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
const chai = require("chai");
2+
const Sinon = require("sinon");
3+
const { expect } = chai;
4+
const { removeDiscordRole } = require("../../../utils/removeDiscordRole");
5+
const addUser = require("../../utils/addUser");
6+
const cleanDb = require("../../utils/cleanDb");
7+
const firestore = require("../../../utils/firestore");
8+
const discordRolesModel = firestore.collection("discord-roles");
9+
const memberRoleModel = firestore.collection("member-group-roles");
10+
const userData = require("../../fixtures/user/user")();
11+
const { groupData, memberGroupData } = require("../../fixtures/discordactions/discordactions");
12+
13+
describe("removeDiscordRole", function () {
14+
let userId;
15+
let discordId;
16+
let roleid;
17+
let rolename;
18+
let fetchStub;
19+
20+
beforeEach(async function () {
21+
userData[0].roles = { archived: false, in_discord: true };
22+
userId = await addUser(userData[0]);
23+
discordId = userData[0].discordId;
24+
userData[0] = { ...userData[0], id: userId };
25+
26+
const addRolePromises = memberGroupData.map(async (data) => {
27+
await memberRoleModel.add(data);
28+
});
29+
const discordRolesModelPromise = [discordRolesModel.add(groupData[0]), discordRolesModel.add(groupData[1])];
30+
await Promise.all(discordRolesModelPromise);
31+
roleid = groupData[0].roleid;
32+
rolename = groupData[0].rolename;
33+
await memberRoleModel.add({ roleid, userid: discordId });
34+
await Promise.all(addRolePromises);
35+
36+
fetchStub = Sinon.stub(global, "fetch");
37+
});
38+
39+
afterEach(async function () {
40+
await cleanDb();
41+
fetchStub.restore();
42+
});
43+
44+
it("should remove discord role successfully", async function () {
45+
fetchStub.returns(
46+
Promise.resolve({ json: () => Promise.resolve({ success: true, message: "Role deleted successfully" }) })
47+
);
48+
49+
const isDiscordRoleRemoved = await removeDiscordRole(userData[0], discordId, roleid, rolename);
50+
51+
expect(isDiscordRoleRemoved.success).to.be.equal(true);
52+
expect(isDiscordRoleRemoved.message).to.be.equal("Role deleted successfully");
53+
});
54+
55+
it("should throw an error if roleid and rolename doesn't exist in database when attempting to remove", async function () {
56+
roleid = "randomRoleId";
57+
rolename = "randomRoleName";
58+
59+
try {
60+
await removeDiscordRole(userData[0], discordId, roleid, rolename);
61+
} catch (error) {
62+
expect(error.message).to.equal("Role doesn't exist");
63+
}
64+
});
65+
66+
it("should throw an error if roleid doesn't exist in database when attempting to remove", async function () {
67+
roleid = "randomRoleId";
68+
rolename = undefined;
69+
70+
try {
71+
await removeDiscordRole(userData[0], discordId, roleid, rolename);
72+
} catch (error) {
73+
expect(error.message).to.equal("Role doesn't exist");
74+
}
75+
});
76+
77+
it("should throw an error if rolename doesn't exist in database when attempting to remove", async function () {
78+
roleid = undefined;
79+
rolename = "randomRoleName";
80+
81+
try {
82+
await removeDiscordRole(userData[0], discordId, roleid, rolename);
83+
} catch (error) {
84+
expect(error.message).to.equal("Role doesn't exist");
85+
}
86+
});
87+
88+
it("should throw an error if rolename and roleid both are undefined", async function () {
89+
roleid = undefined;
90+
rolename = undefined;
91+
92+
try {
93+
await removeDiscordRole(userData[0], discordId, roleid, rolename);
94+
} catch (error) {
95+
expect(error.message).to.equal("Role doesn't exist");
96+
}
97+
});
98+
99+
it("should throw an error if role deletion failed", async function () {
100+
fetchStub.rejects(new Error("Role deletion failed"));
101+
102+
try {
103+
await removeDiscordRole(userData[0], discordId, roleid, rolename);
104+
} catch (error) {
105+
expect(error.message).to.equal("Role deletion failed");
106+
}
107+
});
108+
});

utils/removeDiscordRole.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const discordRolesModel = require("../models/discordactions");
2+
const discordServices = require("../services/discordService");
3+
4+
/**
5+
* Removes a Discord role from a user, handling cases where either "roleid", "rolename", or both are provided.
6+
*
7+
* @param {Object} userData - User data.
8+
* @param {string} discordId - User's Discord ID.
9+
* @param {string} roleid - Role ID. If not provided, defaults to "undefined".
10+
* @param {string} rolename - Role name. If not provided, defaults to "undefined".
11+
*
12+
* @returns {Promise<Object>} - Result with success status and message.
13+
*/
14+
export const removeDiscordRole = async (userData, discordId, roleid, rolename) => {
15+
try {
16+
const role = await discordRolesModel.isGroupRoleExists({ roleid, rolename });
17+
18+
if (!role.roleExists) {
19+
throw new Error("Role doesn't exist");
20+
}
21+
22+
const roleData = role.existingRoles.docs[0].data();
23+
24+
await discordServices.removeRoleFromUser(roleData.roleid, discordId, userData);
25+
26+
const { wasSuccess } = await discordRolesModel.removeMemberGroup(roleData.roleid, discordId);
27+
if (!wasSuccess) {
28+
throw new Error("Role deletion failed");
29+
}
30+
31+
return { success: true, message: "Role deleted successfully" };
32+
} catch (error) {
33+
logger.error(`Error removing role ${rolename || roleid} for user ${discordId}: ${error.message}`);
34+
35+
return { success: false, message: error.message };
36+
}
37+
};

0 commit comments

Comments
 (0)