Skip to content

Commit 5357721

Browse files
Merge pull request #1077 from Real-Dev-Squad/develop
Added services for discord group roles assignment
2 parents c388a0b + 99c175c commit 5357721

File tree

13 files changed

+521
-6
lines changed

13 files changed

+521
-6
lines changed

config/custom-environment-variables.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ module.exports = {
4242
authRedirection: "SERVICES_RDSUI_ROUTES_AUTH_REDIRECTION",
4343
},
4444
},
45+
46+
discordBot: {
47+
baseUrl: "DISCORD_BASE_URL",
48+
},
4549
},
4650

4751
userToken: {
@@ -62,6 +66,11 @@ module.exports = {
6266
botPublicKey: "BOT_PUBLIC_KEY",
6367
},
6468

69+
rdsServerlessBot: {
70+
rdsServerLessPrivateKey: "RDS_SERVERLESS_PRIVATE_KEY",
71+
ttl: "RDS_SERVERLESS_TTL",
72+
},
73+
6574
cloudinary: {
6675
cloud_name: "CLOUDINARY_CLOUD_NAME",
6776
api_key: "CLOUDINARY_API_KEY",

config/default.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ module.exports = {
7878
CLOUDFLARE_X_AUTH_EMAIL: "Cloudflare_User_Email",
7979
},
8080

81+
rdsServerlessBot: {
82+
rdsServerLessPrivateKey: "RDS_SERVERLESS_PRIVATE_KEY",
83+
ttl: 60,
84+
},
85+
8186
integrations: {
8287
newrelic: {
8388
appName: "RDS_API_production",

config/test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ module.exports = {
3030
rdsApi: {
3131
baseUrl: `http://localhost:${port}`,
3232
},
33+
discordBot: {
34+
baseUrl: "DISCORD_BASE_URL",
35+
},
3336
},
3437

3538
cors: {
@@ -102,4 +105,10 @@ module.exports = {
102105
"Ba+XMCP64pXPC3r1llhKRwIl+6UFn+QlpbxtgQjhbULnSbc7fw==\n" +
103106
"-----END RSA PRIVATE KEY-----",
104107
},
108+
109+
rdsServerlessBot: {
110+
rdsServerLessPublicKey: "RSA PUBLIC KEY",
111+
rdsServerLessPrivateKey: "RSA PRIVATE KEY",
112+
ttl: 60,
113+
},
105114
};

controllers/discordactions.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");
2+
const admin = require("firebase-admin");
3+
const config = require("config");
4+
const jwt = require("jsonwebtoken");
5+
const discordRolesModel = require("../models/discordactions");
6+
7+
/**
8+
* Creates a role
9+
*
10+
* @param req {Object} - Express request object
11+
* @param res {Object} - Express response object
12+
*/
13+
14+
const DISCORD_BASE_URL = config.get("services.discordBot.baseUrl");
15+
16+
const createGroupRole = async (req, res) => {
17+
try {
18+
const rolename = `group-${req.body.rolename}`;
19+
20+
const { wasSuccess } = await discordRolesModel.isGroupRoleExists(rolename);
21+
22+
if (!wasSuccess) {
23+
return res.status(400).json({
24+
message: "Role already exists!",
25+
});
26+
}
27+
const dataForDiscord = {
28+
rolename,
29+
mentionable: true,
30+
};
31+
const groupRoleData = {
32+
rolename,
33+
createdBy: req.userData.id,
34+
date: admin.firestore.Timestamp.fromDate(new Date()),
35+
};
36+
const authToken = jwt.sign({}, config.get("rdsServerlessBot.rdsServerLessPrivateKey"), {
37+
algorithm: "RS256",
38+
expiresIn: config.get("rdsServerlessBot.ttl"),
39+
});
40+
41+
const responseForCreatedRole = await fetch(`${DISCORD_BASE_URL}/roles/create`, {
42+
method: "PUT",
43+
body: JSON.stringify(dataForDiscord),
44+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
45+
}).then((response) => response.json());
46+
47+
groupRoleData.roleid = responseForCreatedRole.id;
48+
49+
const { id } = await discordRolesModel.createNewRole(groupRoleData);
50+
return res.status(201).json({
51+
message: "Role created successfully!",
52+
id,
53+
});
54+
} catch (err) {
55+
logger.error(`Error while creating new Role: ${err}`);
56+
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
57+
}
58+
};
59+
60+
/**
61+
* Gets all group-roles
62+
*
63+
* @param res {Object} - Express response object
64+
*/
65+
66+
const getAllGroupRoles = async (req, res) => {
67+
try {
68+
const { groups } = await discordRolesModel.getAllGroupRoles();
69+
return res.json({
70+
message: "Roles fetched successfully!",
71+
groups,
72+
});
73+
} catch (err) {
74+
logger.error(`Error while getting roles: ${err}`);
75+
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
76+
}
77+
};
78+
79+
/**
80+
* Gets all group-roles
81+
* @param req {Object} - Express request object
82+
* @param res {Object} - Express response object
83+
*/
84+
85+
const addGroupRoleToMember = async (req, res) => {
86+
try {
87+
const memberGroupRole = {
88+
...req.body,
89+
date: admin.firestore.Timestamp.fromDate(new Date()),
90+
};
91+
92+
const { roleData, wasSuccess } = await discordRolesModel.addGroupRoleToMember(memberGroupRole);
93+
94+
if (!wasSuccess) {
95+
return res.status(400).json({
96+
message: "Role already exists!",
97+
data: {
98+
...roleData,
99+
},
100+
});
101+
}
102+
const dataForDiscord = {
103+
...req.body,
104+
};
105+
const authToken = jwt.sign({}, config.get("rdsServerlessBot.rdsServerLessPrivateKey"), {
106+
algorithm: "RS256",
107+
expiresIn: config.get("rdsServerlessBot.ttl"),
108+
});
109+
110+
await fetch(`${DISCORD_BASE_URL}/roles/add`, {
111+
method: "PUT",
112+
body: JSON.stringify(dataForDiscord),
113+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
114+
}).then((response) => response.json());
115+
116+
return res.status(201).json({
117+
message: "Role added successfully!",
118+
});
119+
} catch (err) {
120+
logger.error(`Error while adding new Role: ${err}`);
121+
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
122+
}
123+
};
124+
125+
module.exports = {
126+
createGroupRole,
127+
getAllGroupRoles,
128+
addGroupRoleToMember,
129+
};

controllers/users.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,13 @@ const getUsers = async (req, res) => {
7373
if (qualifiers?.filterBy) {
7474
const allPRs = await getFilteredPRsOrIssues(qualifiers);
7575

76-
const filteredUsernames = getUsernamesFromPRs(allPRs);
76+
const usernames = getUsernamesFromPRs(allPRs);
7777

78-
const { filterdUsersWithDetails } = await userQuery.fetchUsers(filteredUsernames);
78+
const { users } = await userQuery.fetchUsers(usernames);
7979

8080
return res.json({
8181
message: "Users returned successfully!",
82-
users: filterdUsersWithDetails,
82+
users,
8383
});
8484
}
8585

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const Joi = require("joi");
2+
3+
const validateGroupRoleBody = async (req, res, next) => {
4+
const schema = Joi.object({
5+
rolename: Joi.string().trim().required(),
6+
});
7+
8+
try {
9+
await schema.validateAsync(req.body);
10+
next();
11+
} catch (error) {
12+
logger.error(`Error validating createGroupRole payload : ${error}`);
13+
res.boom.badRequest(error.details[0].message);
14+
}
15+
};
16+
const validateMemberRoleBody = async (req, res, next) => {
17+
const schema = Joi.object({
18+
userid: Joi.string().trim().required(),
19+
roleid: Joi.string().trim().required(),
20+
});
21+
22+
try {
23+
await schema.validateAsync(req.body);
24+
next();
25+
} catch (error) {
26+
logger.error(`Error validating member role payload : ${error}`);
27+
res.boom.badRequest(error.details[0].message);
28+
}
29+
};
30+
31+
module.exports = {
32+
validateGroupRoleBody,
33+
validateMemberRoleBody,
34+
};

middlewares/verifydiscord.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Middleware to check if the user has verified themself with the discord bot.
3+
* If user has not verified, then no actions based on discord bot should be allowed.
4+
* Note: This requires that user is authenticated hence must be called after
5+
* the user authentication middleware. We are calling it from within the
6+
* `authenticate` middleware itself to avoid explicitly adding this middleware
7+
* while defining routes.
8+
*
9+
* @param {Object} req - Express request object
10+
* @param {Object} res - Express response object
11+
* @param {Function} next - Express middleware function
12+
* @returns {Object} - Returns unauthorized object if user has been restricted.
13+
*/
14+
const checkIsVerifiedDiscord = async (req, res, next) => {
15+
const { discordId } = req.userData;
16+
if (!discordId) {
17+
return res.boom.forbidden("You are restricted from performing this action");
18+
}
19+
return next();
20+
};
21+
22+
module.exports = checkIsVerifiedDiscord;

models/discordactions.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const firestore = require("../utils/firestore");
2+
const discordRoleModel = firestore.collection("discord-roles");
3+
const memberRoleModel = firestore.collection("member-group-roles");
4+
5+
/**
6+
*
7+
* @param roleData { Object }: Data of the new role
8+
* @returns {Promise<discordRoleModel|Object>}
9+
*/
10+
11+
const createNewRole = async (roleData) => {
12+
try {
13+
const { id } = await discordRoleModel.add(roleData);
14+
return { id, roleData };
15+
} catch (err) {
16+
logger.error("Error in creating role", err);
17+
throw err;
18+
}
19+
};
20+
21+
/**
22+
*
23+
* @param roleData { Object }: Data of the new role
24+
* @returns {Promise<discordRoleModel|Object>}
25+
*/
26+
const getAllGroupRoles = async () => {
27+
try {
28+
const data = await discordRoleModel.get();
29+
const groups = [];
30+
data.forEach((doc) => {
31+
const group = {
32+
id: doc.id,
33+
...doc.data(),
34+
};
35+
groups.push(group);
36+
});
37+
return { groups };
38+
} catch (err) {
39+
logger.error("Error in getting all group-roles", err);
40+
throw err;
41+
}
42+
};
43+
44+
/**
45+
*
46+
* @param roleData { Object }: Data of the new role
47+
* @returns {Promise<discordRoleModel|Object>}
48+
*/
49+
50+
const isGroupRoleExists = async (rolename) => {
51+
try {
52+
const alreadyIsRole = await discordRoleModel.where("rolename", "==", rolename).limit(1).get();
53+
if (!alreadyIsRole.empty) {
54+
const oldRole = [];
55+
alreadyIsRole.forEach((role) => oldRole.push(role.data()));
56+
return { wasSuccess: false };
57+
}
58+
return { wasSuccess: true };
59+
} catch (err) {
60+
logger.error("Error in getting all group-roles", err);
61+
throw err;
62+
}
63+
};
64+
65+
/**
66+
*
67+
* @param roleData { Object }: Data of the new role
68+
* @returns {Promise<discordRoleModel|Object>}
69+
*/
70+
const addGroupRoleToMember = async (roleData) => {
71+
try {
72+
const alreadyHasRole = await memberRoleModel
73+
.where("roleid", "==", roleData.roleid)
74+
.where("userid", "==", roleData.userid)
75+
.limit(1)
76+
.get();
77+
if (!alreadyHasRole.empty) {
78+
const oldRole = [];
79+
alreadyHasRole.forEach((role) => oldRole.push(role.data()));
80+
return { id: oldRole[0].id, roleData: oldRole[0], wasSuccess: false };
81+
}
82+
const { id } = await memberRoleModel.add(roleData);
83+
return { id, roleData, wasSuccess: true };
84+
} catch (err) {
85+
logger.error("Error in adding role", err);
86+
throw err;
87+
}
88+
};
89+
90+
module.exports = {
91+
createNewRole,
92+
getAllGroupRoles,
93+
addGroupRoleToMember,
94+
isGroupRoleExists,
95+
};

models/users.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ const fetchPaginatedUsers = async (query) => {
182182
const fetchUsers = async (usernames = []) => {
183183
try {
184184
const dbQuery = userModel;
185-
const filterdUsersWithDetails = [];
185+
const users = [];
186186

187187
const groups = [];
188188
for (let i = 0; i < usernames.length; i += BATCH_SIZE_IN_CLAUSE) {
@@ -198,7 +198,7 @@ const fetchUsers = async (usernames = []) => {
198198

199199
snapshots.forEach((snapshot) => {
200200
snapshot.forEach((doc) => {
201-
filterdUsersWithDetails.push({
201+
users.push({
202202
id: doc.id,
203203
...doc.data(),
204204
phone: undefined,
@@ -210,7 +210,7 @@ const fetchUsers = async (usernames = []) => {
210210
});
211211

212212
return {
213-
filterdUsersWithDetails,
213+
users,
214214
};
215215
} catch (err) {
216216
logger.error("Error retrieving user data", err);

routes/discordactions.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const express = require("express");
2+
const authenticate = require("../middlewares/authenticate");
3+
const { createGroupRole, getAllGroupRoles, addGroupRoleToMember } = require("../controllers/discordactions");
4+
const { validateGroupRoleBody, validateMemberRoleBody } = require("../middlewares/validators/discordactions");
5+
const checkIsVerifiedDiscord = require("../middlewares/verifydiscord");
6+
7+
const router = express.Router();
8+
9+
router.post("/groups", authenticate, checkIsVerifiedDiscord, validateGroupRoleBody, createGroupRole);
10+
router.get("/groups", authenticate, checkIsVerifiedDiscord, getAllGroupRoles);
11+
router.post("/roles", authenticate, checkIsVerifiedDiscord, validateMemberRoleBody, addGroupRoleToMember);
12+
13+
module.exports = router;

0 commit comments

Comments
 (0)