Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
44328ad
feat: add purpose field in generate discord invite for user with feat…
devdeadviz Oct 18, 2024
ddfc548
feat: add functionality to be able to create n times discord invites …
devdeadviz Oct 22, 2024
2eab530
Merge branch 'develop' into feat/add-purpose-in-discord-api
devdeadviz Oct 22, 2024
becf40e
feat: add unit tests for generateInviteForUser
devdeadviz Oct 25, 2024
9f7dc73
feat: add joi validation for generateInviteForUser function body
devdeadviz Oct 25, 2024
9545994
Merge branch 'develop' into feat/add-purpose-in-discord-api
Achintya-Chatterjee Oct 25, 2024
bdd12a3
Merge branch 'develop' into feat/add-purpose-in-discord-api
devdeadviz Oct 28, 2024
99a8364
Merge branch 'develop' into feat/add-purpose-in-discord-api
Achintya-Chatterjee Oct 29, 2024
ef00ad8
fix: add suggested changes
devdeadviz Oct 30, 2024
a3b15c4
feat(e2e): add integrations test for discord-actions invite api
devdeadviz Oct 30, 2024
84d0960
chore: remove creation of random id while storing discord invite to d…
devdeadviz Oct 30, 2024
3cc2444
refactor: getUserDiscordInvite query to fetch purpose when dev is true
devdeadviz Oct 30, 2024
5734b0c
Merge branch 'develop' into feat/add-purpose-in-discord-api
devdeadviz Nov 22, 2024
470399b
refactor: remove non needed else block
devdeadviz Nov 25, 2024
13f5a73
refactor: add status code in fetch invite api
devdeadviz Nov 26, 2024
f63f564
fix: dev condition to only work in truthy case
devdeadviz Nov 26, 2024
3c0a55f
Merge branch 'develop' into feat/add-purpose-in-discord-api
devdeadviz Nov 26, 2024
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
57 changes: 39 additions & 18 deletions controllers/discordactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
const nickNameUpdatedUsers = [];
let counter = 0;
for (let i = 0; i < usersToBeEffected.length; i++) {
const { discordId, username, first_name: firstName } = usersToBeEffected[i];

Check warning on line 289 in controllers/discordactions.js

View workflow job for this annotation

GitHub Actions / build (20.11.x)

Variable Assigned to Object Injection Sink
try {
if (counter % 10 === 0 && counter !== 0) {
await new Promise((resolve) => setTimeout(resolve, 5500));
Expand All @@ -302,7 +302,7 @@
if (message) {
counter++;
totalNicknamesUpdated.count++;
nickNameUpdatedUsers.push(usersToBeEffected[i].id);

Check warning on line 305 in controllers/discordactions.js

View workflow job for this annotation

GitHub Actions / build (20.11.x)

Generic Object Injection Sink
}
}
} catch (error) {
Expand Down Expand Up @@ -409,15 +409,17 @@

const generateInviteForUser = async (req, res) => {
try {
const { userId } = req.query;
const { userId, dev } = req.query;
const userIdForInvite = userId || req.userData.id;
let inviteLink = "";

const modelResponse = await discordRolesModel.getUserDiscordInvite(userIdForInvite);

if (!modelResponse.notFound) {
return res.status(409).json({
message: "User invite is already present!",
});
if (!dev) {
const modelResponse = await discordRolesModel.getUserDiscordInvite(userIdForInvite);
if (!modelResponse.notFound) {
return res.status(409).json({
message: "User invite is already present!",
});
}
}

const channelId = config.get("discordNewComersChannelId");
Expand All @@ -437,14 +439,25 @@
const discordInviteResponse = await response.json();

const inviteCode = discordInviteResponse.data.code;
const inviteLink = `discord.gg/${inviteCode}`;
inviteLink = `discord.gg/${inviteCode}`;

await discordRolesModel.addInviteToInviteModel({ userId: userIdForInvite, inviteLink });
if (dev) {
const purpose = req.body.purpose;
await discordRolesModel.addInviteToInviteModel({ userId: userIdForInvite, inviteLink, purpose });

return res.status(201).json({
message: "invite generated successfully",
inviteLink,
});
return res.status(201).json({
message: "invite generated successfully",
inviteLink,
purpose,
});
} else {
await discordRolesModel.addInviteToInviteModel({ userId: userIdForInvite, inviteLink });

return res.status(201).json({
message: "invite generated successfully",
inviteLink,
});
}
} catch (err) {
logger.error(`Error in generating invite for user: ${err}`);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
Expand All @@ -453,7 +466,7 @@

const getUserDiscordInvite = async (req, res) => {
try {
const { userId } = req.query;
const { userId, dev } = req.query;
const isSuperUser = req.userData.roles.super_user;

if (userId && !isSuperUser) return res.boom.forbidden("User should be super user to get link for other users");
Expand All @@ -466,10 +479,18 @@
return res.boom.notFound("User invite doesn't exist");
}

return res.json({
message: "Invite returned successfully",
inviteLink: invite?.inviteLink,
});
if (dev) {
return res.json({
message: "Invite returned successfully",
inviteLink: invite?.inviteLink,
purpose: invite?.purpose,
});
} else {
return res.json({
message: "Invite returned successfully",
inviteLink: invite?.inviteLink,
});
}
} catch (err) {
logger.error(`Error in fetching user invite: ${err}`);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
Expand Down
16 changes: 16 additions & 0 deletions middlewares/validators/discordactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,24 @@ const validateUpdateUsersNicknameStatusBody = async (req, res, next) => {
}
};

export const validateGenerateInviteForUserBody = async (req, res, next) => {
const schema = Joi.object().strict().keys({
purpose: Joi.string().trim(),
});

try {
await schema.validateAsync(req.body);
next();
} catch (error) {
const errorMessages = error.details.map((detail) => detail.message);
logger.error(`Error while validating invite creation payload: ${errorMessages}`);
res.boom.badRequest(errorMessages);
}
};

module.exports = {
validateGroupRoleBody,
validateMemberRoleBody,
validateUpdateUsersNicknameStatusBody,
validateGenerateInviteForUserBody,
};
9 changes: 8 additions & 1 deletion routes/discordactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
validateGroupRoleBody,
validateMemberRoleBody,
validateUpdateUsersNicknameStatusBody,
validateGenerateInviteForUserBody,
} = require("../middlewares/validators/discordactions");
const checkIsVerifiedDiscord = require("../middlewares/verifydiscord");
const checkCanGenerateDiscordLink = require("../middlewares/checkCanGenerateDiscordLink");
Expand All @@ -36,7 +37,13 @@
router.get("/groups", authenticate, checkIsVerifiedDiscord, getAllGroupRoles);
router.post("/roles", authenticate, checkIsVerifiedDiscord, validateMemberRoleBody, addGroupRoleToMember);
router.get("/invite", authenticate, getUserDiscordInvite);
router.post("/invite", authenticate, checkCanGenerateDiscordLink, generateInviteForUser);
router.post(
"/invite",
authenticate,
checkCanGenerateDiscordLink,
validateGenerateInviteForUserBody,
generateInviteForUser
);
router.delete("/roles", authenticate, checkIsVerifiedDiscord, deleteRole);
router.get("/roles", authenticate, checkIsVerifiedDiscord, getGroupsRoleId);
router.patch(
Expand Down
21 changes: 21 additions & 0 deletions test/integration/discordactions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,27 @@ describe("Discord actions", function () {
expect(res.body.message).to.be.equal("User should be super user to generate link for other users");
});

it("Should return the invite and the purpose for other user if the dev=true is provided in the query and the user is super user", async function () {
fetchStub.returns(
Promise.resolve({
status: 201,
json: () => Promise.resolve({ data: { code: "xyz" } }),
})
);

const res = await chai
.request(app)
.post(`/discord-actions/invite?dev=true`)
.set("cookie", `${cookieName}=${superUserAuthToken}`)
.send({
purpose: "testing",
});
expect(res).to.have.status(201);
expect(res.body.message).to.be.equal("invite generated successfully");
expect(res.body.inviteLink).to.be.equal("discord.gg/xyz");
expect(res.body.purpose).to.be.equal("testing");
});

// eslint-disable-next-line mocha/no-skipped-tests
it.skip("should return 403 if the user has discord id in their user object, which means user is already in discord", async function () {
const res = await chai
Expand Down
31 changes: 31 additions & 0 deletions test/unit/middlewares/discordactions-validators.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const {
validateGroupRoleBody,
validateMemberRoleBody,
validateUpdateUsersNicknameStatusBody,
validateGenerateInviteForUserBody,
} = require("../../../middlewares/validators/discordactions");
const { expect } = require("chai");

Expand Down Expand Up @@ -127,4 +128,34 @@ describe("Middleware | Validators | discord actions", function () {
expect(nextSpy.callCount).to.be.equal(0);
});
});

describe("validateGenerateInviteForUserBody", function () {
it("lets the request pass to the next function", async function () {
const res = {};
const req = {
body: {
purpose: "testing",
},
};
const nextSpy = Sinon.spy();
await validateGenerateInviteForUserBody(req, res, nextSpy);
expect(nextSpy.calledOnce).to.be.equal(true);
});

it("stops the propogation to the next function", async function () {
const res = {
boom: {
badRequest: () => {},
},
};
const nextSpy = Sinon.spy();
const req = {
body: {},
};
await validateMemberRoleBody(req, res, nextSpy).catch((err) => {
expect(err).to.be.an.instanceOf(Error);
});
expect(nextSpy.callCount).to.be.equal(0);
});
});
});
49 changes: 49 additions & 0 deletions test/unit/models/discordactions.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { generateDiscordInviteLink } from "../../../utils/discord-actions";
const chai = require("chai");
const expect = chai.expect;
const sinon = require("sinon");
Expand Down Expand Up @@ -802,6 +803,54 @@ describe("discordactions", function () {
});
});

describe("generateInviteForUser", function () {
let fetchStub;

beforeEach(async function () {
fetchStub = sinon.stub(global, "fetch");
});

afterEach(function () {
fetchStub.restore();
});

it("should return the invite link and purpose", async function () {
const inviteLink = "discord.gg/xyz";
const discordInviteLink = {
data: {
code: "xyz",
},
};
fetchStub.resolves({
ok: true,
json: () => discordInviteLink,
});

const result = await generateDiscordInviteLink();
expect(result).to.be.equal(inviteLink);

const inviteObject = { userId: "123456", inviteLink: "discord.gg/xyz", purpose: "testing" };
await addInviteToInviteModel(inviteObject);

const invite = await getUserDiscordInvite("123456");
expect(invite).to.have.property("id");
expect(invite.notFound).to.be.equal(false);
expect(invite.userId).to.be.equal("123456");
expect(invite.inviteLink).to.be.equal("discord.gg/xyz");
expect(invite.purpose).to.be.equal("testing");
});

it("should resolve with an error", async function () {
const error = new Error("Error");
fetchStub.rejects(error);
try {
await generateDiscordInviteLink();
} catch (err) {
expect(err).to.be.equal(error);
}
});
});

describe("groupUpdateLastJoinDate", function () {
beforeEach(function () {
sinon.stub(discordRoleModel, "doc").returns({
Expand Down
Loading