Skip to content

Commit efbd6a9

Browse files
authored
Merge pull request #1329 from gauravsinhaweb/feat/logs-archive-details
2 parents 32e68b4 + 63c7396 commit efbd6a9

File tree

5 files changed

+241
-28
lines changed

5 files changed

+241
-28
lines changed

controllers/members.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
const ROLES = require("../constants/roles");
22
const members = require("../models/members");
33
const tasks = require("../models/tasks");
4-
const { SOMETHING_WENT_WRONG } = require("../constants/errorMessages");
4+
const { SOMETHING_WENT_WRONG, INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");
55
const dataAccess = require("../services/dataAccessLayer");
6+
const { addLog } = require("../models/logs");
67
/**
78
* Fetches the data about our members
89
*
@@ -83,17 +84,38 @@ const archiveMembers = async (req, res) => {
8384
try {
8485
const { username } = req.params;
8586
const user = await dataAccess.retrieveUsers({ username });
87+
const superUserId = req.userData.id;
88+
const { reason } = req.body;
89+
const roles = req?.userData?.roles;
90+
const isReasonNullOrUndefined = !reason;
91+
const isReasonEmptyOrWhitespace = /^\s*$/.test(reason);
92+
if (isReasonNullOrUndefined || isReasonEmptyOrWhitespace) {
93+
return res.boom.badRequest("Reason is required");
94+
}
8695
if (user?.userExists) {
8796
const successObject = await members.addArchiveRoleToMembers(user.user.id);
8897
if (successObject.isArchived) {
8998
return res.boom.badRequest("User is already archived");
9099
}
100+
const body = {
101+
reason: reason,
102+
archived_user: {
103+
user_id: user.user.id,
104+
username: user.user.username,
105+
},
106+
archived_by: {
107+
user_id: superUserId,
108+
roles: roles,
109+
},
110+
};
111+
112+
addLog("archived-details", {}, body);
91113
return res.status(204).send();
92114
}
93115
return res.boom.notFound("User doesn't exist");
94116
} catch (err) {
95117
logger.error(`Error while retriving contributions ${err}`);
96-
return res.boom.badImplementation(SOMETHING_WENT_WRONG);
118+
return res.status(500).json({ message: INTERNAL_SERVER_ERROR });
97119
}
98120
};
99121

models/logs.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const { getBeforeHourTime } = require("../utils/time");
33
const logsModel = firestore.collection("logs");
44
const admin = require("firebase-admin");
55
const { logType } = require("../constants/logs");
6+
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");
67

78
/**
89
* Adds log
@@ -22,7 +23,7 @@ const addLog = async (type, meta, body) => {
2223
return await logsModel.add(log);
2324
} catch (err) {
2425
logger.error("Error in adding log", err);
25-
throw err;
26+
throw new Error(INTERNAL_SERVER_ERROR);
2627
}
2728
};
2829

@@ -42,14 +43,27 @@ const fetchLogs = async (query, param) => {
4243
}
4344
});
4445

45-
const { limit, lastDocId } = query;
46+
const { limit, lastDocId, userId } = query;
4647
let lastDoc;
4748
const limitDocuments = Number(limit);
4849

4950
if (lastDocId) {
5051
lastDoc = await logsModel.doc(lastDocId).get();
5152
}
52-
53+
if (userId) {
54+
const logsSnapshot = await logsModel
55+
.where("type", "==", param)
56+
.where("body.archived_user.user_id", "==", userId)
57+
.orderBy("timestamp", "desc")
58+
.get();
59+
const logs = [];
60+
logsSnapshot.forEach((doc) => {
61+
logs.push({
62+
...doc.data(),
63+
});
64+
});
65+
return logs;
66+
}
5367
const logsSnapshotQuery = call.orderBy("timestamp", "desc").startAfter(lastDoc ?? "");
5468
const snapshot = limit
5569
? await logsSnapshotQuery.limit(limitDocuments).get()
@@ -64,7 +78,7 @@ const fetchLogs = async (query, param) => {
6478
return logs;
6579
} catch (err) {
6680
logger.error("Error in adding log", err);
67-
throw err;
81+
throw new Error(INTERNAL_SERVER_ERROR);
6882
}
6983
};
7084

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const archivedUserDetailsModal = [
2+
{
3+
type: "archived-details",
4+
meta: {},
5+
body: {
6+
reason: "test reason",
7+
archived_user: { user_id: "R5kljdsleH4Gr2t7tvr0Z", username: "testUser1" },
8+
archived_by: {
9+
user_id: "ReMyuklislajwooncVL",
10+
roles: {
11+
in_discord: true,
12+
super_user: false,
13+
member: true,
14+
archived: false,
15+
},
16+
},
17+
},
18+
timestamp: {
19+
_seconds: 1657193216,
20+
_nanoseconds: 912000000,
21+
},
22+
},
23+
{
24+
type: "archived-details",
25+
meta: {},
26+
body: {
27+
reason: "test reason",
28+
archived_user: { user_id: "R5kljdsleH4Gr2t7tvr0Z", username: "testUser1" },
29+
archived_by: {
30+
user_id: "ReMyuklislajwooncVL",
31+
roles: {
32+
in_discord: true,
33+
super_user: false,
34+
member: true,
35+
archived: false,
36+
},
37+
},
38+
},
39+
timestamp: {
40+
_seconds: 1657193216,
41+
_nanoseconds: 912000000,
42+
},
43+
},
44+
{
45+
type: "archived-details",
46+
meta: {},
47+
body: {
48+
reason: "test reason",
49+
archived_user: { user_id: "Efskee4Gr2t7tvr0Z", username: "testUser2" },
50+
archived_by: {
51+
user_id: "ReMyuklislajwooncVL",
52+
roles: {
53+
in_discord: true,
54+
super_user: false,
55+
member: true,
56+
archived: false,
57+
},
58+
},
59+
},
60+
timestamp: {
61+
_seconds: 1657193216,
62+
_nanoseconds: 912000000,
63+
},
64+
},
65+
];
66+
module.exports = {
67+
archivedUserDetailsModal,
68+
};

test/integration/members.test.js

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ const userData = require("../fixtures/user/user")();
1212

1313
const config = require("config");
1414
const cookieName = config.get("userToken.cookieName");
15+
const Sinon = require("sinon");
16+
const { INTERNAL_SERVER_ERROR } = require("../../constants/errorMessages");
17+
const dataAccess = require("../.././services/dataAccessLayer");
1518

1619
chai.use(chaiHttp);
1720

@@ -258,16 +261,40 @@ describe("Members", function () {
258261
});
259262

260263
describe("PATCH /members/archiveMembers/:username", function () {
264+
let dataAccessStub;
261265
beforeEach(async function () {
262266
const superUserId = await addUser(superUser);
263267
jwt = authService.generateAuthToken({ userId: superUserId });
264268
});
265-
269+
afterEach(async function () {
270+
Sinon.restore();
271+
await cleanDb();
272+
});
273+
it("Should return an object with status 500 and an error message", function (done) {
274+
dataAccessStub = Sinon.stub(dataAccess, "retrieveUsers");
275+
dataAccessStub.throws(new Error(INTERNAL_SERVER_ERROR));
276+
addUser(userToBeArchived).then(() => {
277+
chai
278+
.request(app)
279+
.patch(`/members/archiveMembers/${userToBeArchived.username}`)
280+
.set("Cookie", `${cookieName}=${jwt}`)
281+
.send({ reason: "some reason" })
282+
.end((err, res) => {
283+
if (err) {
284+
return done(err);
285+
}
286+
expect(res).to.have.status(500);
287+
expect(res.body.message).to.be.equal(INTERNAL_SERVER_ERROR);
288+
return done();
289+
});
290+
});
291+
});
266292
it("Should return 404 if user doesn't exist", function (done) {
267293
chai
268294
.request(app)
269295
.patch(`/members/archiveMembers/${userDoesNotExists.username}`)
270296
.set("cookie", `${cookieName}=${jwt}`)
297+
.send({ reason: "some reason" })
271298
.end((err, res) => {
272299
if (err) {
273300
return done(err);
@@ -278,13 +305,31 @@ describe("Members", function () {
278305
return done();
279306
});
280307
});
308+
it("Should return 400 if body is empty", function (done) {
309+
chai
310+
.request(app)
311+
.patch(`/members/archiveMembers/${userToBeArchived.username}`)
312+
.set("cookie", `${cookieName}=${jwt}`)
313+
.send({})
314+
.end((err, res) => {
315+
if (err) {
316+
return done(err);
317+
}
318+
319+
expect(res).to.have.status(400);
320+
expect(res.body).to.be.a("object");
321+
expect(res.body.message).to.equal("Reason is required");
281322

323+
return done();
324+
});
325+
});
282326
it("Should archive the user", function (done) {
283327
addUser(userToBeArchived).then(() => {
284328
chai
285329
.request(app)
286330
.patch(`/members/archiveMembers/${userToBeArchived.username}`)
287331
.set("cookie", `${cookieName}=${jwt}`)
332+
.send({ reason: "some reason" })
288333
.end((err, res) => {
289334
if (err) {
290335
return done(err);
@@ -305,6 +350,7 @@ describe("Members", function () {
305350
.request(app)
306351
.patch(`/members/archiveMembers/${userAlreadyArchived.username}`)
307352
.set("cookie", `${cookieName}=${jwt}`)
353+
.send({ reason: "some reason" })
308354
.end((err, res) => {
309355
if (err) {
310356
return done(err);
@@ -318,26 +364,5 @@ describe("Members", function () {
318364
});
319365
});
320366
});
321-
322-
it("Should return 401 if user is not a super user", function (done) {
323-
addUser(nonSuperUser).then((nonSuperUserId) => {
324-
const nonSuperUserJwt = authService.generateAuthToken({ userId: nonSuperUserId });
325-
chai
326-
.request(app)
327-
.patch(`/members/moveToMembers/${nonSuperUser.username}`)
328-
.set("cookie", `${cookieName}=${nonSuperUserJwt}`)
329-
.end((err, res) => {
330-
if (err) {
331-
return done(err);
332-
}
333-
334-
expect(res).to.have.status(401);
335-
expect(res.body).to.be.a("object");
336-
expect(res.body.message).to.equal("You are not authorized for this action.");
337-
338-
return done();
339-
});
340-
});
341-
});
342367
});
343368
});

test/unit/models/logs.test.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ const { expect } = chai;
44
const cleanDb = require("../../utils/cleanDb");
55
const logsQuery = require("../../../models/logs");
66
const cacheData = require("../../fixtures/cloudflareCache/data");
7+
const logsData = require("../../fixtures/logs/archievedUsers");
8+
const app = require("../../../server");
9+
const Sinon = require("sinon");
10+
const { INTERNAL_SERVER_ERROR } = require("../../../constants/errorMessages");
11+
const userData = require("../../fixtures/user/user")();
12+
const addUser = require("../../utils/addUser");
13+
const cookieName = config.get("userToken.cookieName");
14+
const authService = require("../../../services/authService");
15+
16+
const superUser = userData[4];
17+
const userToBeMadeMember = userData[1];
718

819
describe("Logs", function () {
920
after(async function () {
@@ -37,4 +48,77 @@ describe("Logs", function () {
3748
expect(data[0].timestamp._nanoseconds).to.be.a("number");
3849
});
3950
});
51+
52+
describe("GET /logs/archived-details", function () {
53+
let addLogsStub;
54+
let jwt;
55+
beforeEach(async function () {
56+
const superUserId = await addUser(superUser);
57+
jwt = authService.generateAuthToken({ userId: superUserId });
58+
await cleanDb();
59+
});
60+
afterEach(function () {
61+
Sinon.restore();
62+
});
63+
64+
it("Should return an Internal server error message", async function () {
65+
addLogsStub = Sinon.stub(logsQuery, "fetchLogs");
66+
addLogsStub.throws(new Error(INTERNAL_SERVER_ERROR));
67+
68+
addUser(userToBeMadeMember).then(() => {
69+
const res = chai.request(app).get("/logs/archived-details").set("cookie", `${cookieName}=${jwt}`).send();
70+
71+
expect(res.body.message).to.equal(INTERNAL_SERVER_ERROR);
72+
});
73+
});
74+
it("Should return empty array if no logs found", async function () {
75+
const { type } = logsData.archivedUserDetailsModal[0];
76+
const query = {};
77+
78+
const data = await logsQuery.fetchLogs(query, type);
79+
80+
expect(data).to.be.an("array").with.lengthOf(0);
81+
});
82+
it("Should fetch all archived logs", async function () {
83+
const { type, meta, body } = logsData.archivedUserDetailsModal[0];
84+
const query = {};
85+
86+
await logsQuery.addLog(type, meta, body);
87+
const data = await logsQuery.fetchLogs(query, type);
88+
89+
expect(data).to.be.an("array").with.lengthOf.greaterThan(0);
90+
expect(data[0]).to.have.property("timestamp").that.is.an("object");
91+
expect(data[0].timestamp).to.have.property("_seconds").that.is.a("number");
92+
expect(data[0].timestamp).to.have.property("_nanoseconds").that.is.a("number");
93+
expect(data[0].body.archived_user).to.have.property("username").that.is.a("string");
94+
expect(data[0].body).to.have.property("reason").that.is.a("string");
95+
});
96+
it("Should fetch all archived logs for given user_id", async function () {
97+
const { type, meta, body } = logsData.archivedUserDetailsModal[0];
98+
const query = {
99+
userId: body.archived_user.user_id,
100+
};
101+
await logsQuery.addLog(type, meta, body);
102+
const data = await logsQuery.fetchLogs(query, type);
103+
104+
expect(data).to.be.an("array").with.lengthOf.greaterThan(0);
105+
expect(data[0]).to.have.property("timestamp").that.is.an("object");
106+
expect(data[0].timestamp).to.have.property("_seconds").that.is.a("number");
107+
expect(data[0].timestamp).to.have.property("_nanoseconds").that.is.a("number");
108+
expect(data[0].body).to.have.property("reason").that.is.a("string");
109+
});
110+
it("Should throw response status 404, if username is incorrect in the query", async function () {
111+
const { type, meta, body } = logsData.archivedUserDetailsModal[0];
112+
const query = {
113+
userId: "1234_test", // incorrect username
114+
};
115+
await logsQuery.addLog(type, meta, body);
116+
const data = await logsQuery.fetchLogs(query, type);
117+
const response = await chai.request(app).get(`/logs/${type}/${query}`);
118+
119+
expect(data).to.be.an("array").with.lengthOf(0);
120+
expect(response).to.have.status(404);
121+
expect(response.body.message).to.be.equal("Not Found");
122+
});
123+
});
40124
});

0 commit comments

Comments
 (0)