Skip to content

Commit 1060bea

Browse files
authored
Capture logs when user document is updated (#2182)
* feat: add logs when user doc is updated * chore: remove query from api * chore: fix addlog when user doc is updated * chore: fix circular dependency * chore: fix failing test * chore: remove extra changes * chore: add integration test * chore: add test for log service
1 parent c0b5305 commit 1060bea

File tree

5 files changed

+132
-1
lines changed

5 files changed

+132
-1
lines changed

constants/logs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const logType = {
1313
EXTENSION_REQUESTS: "extensionRequests",
1414
TASK: "task",
1515
TASK_REQUESTS: "taskRequests",
16+
USER_DETAILS_UPDATED: "USER_DETAILS_UPDATED",
1617
...REQUEST_LOG_TYPE,
1718
};
1819

models/users.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const admin = require("firebase-admin");
2424
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");
2525
const { AUTHORITIES } = require("../constants/authorities");
2626
const { formatUsername } = require("../utils/username");
27+
const { logType } = require("../constants/logs");
28+
const { addLog } = require("../services/logService");
2729

2830
/**
2931
* Adds or updates the user data
@@ -48,6 +50,13 @@ const addOrUpdate = async (userData, userId = null) => {
4850
},
4951
{ merge: true }
5052
);
53+
54+
const logData = {
55+
type: logType.USER_DETAILS_UPDATED,
56+
meta: { userId: userId },
57+
body: userData,
58+
};
59+
await addLog(logData.type, logData.meta, logData.body);
5160
}
5261

5362
return { isNewUser, userId };
@@ -63,6 +72,14 @@ const addOrUpdate = async (userData, userId = null) => {
6372
}
6473
if (user && !user.empty && user.docs !== null) {
6574
await userModel.doc(user.docs[0].id).set({ ...userData, updated_at: Date.now() }, { merge: true });
75+
76+
const logData = {
77+
type: logType.USER_DETAILS_UPDATED,
78+
meta: { userId: user.docs[0].id },
79+
body: userData,
80+
};
81+
await addLog(logData.type, logData.meta, logData.body);
82+
6683
const data = user.docs[0].data();
6784
return {
6885
isNewUser: false,

services/logService.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import firestore from "../utils/firestore";
2+
const logsModel = firestore.collection("logs");
3+
import admin from "firebase-admin";
4+
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");
5+
6+
interface LogMeta {
7+
userId?: string;
8+
[key: string]: number | string | object;
9+
}
10+
11+
interface LogBody {
12+
[key: string]: number | string | object;
13+
}
14+
15+
/**
16+
* Adds log
17+
*
18+
* @param type { string }: Type of the log
19+
* @param meta { LogMeta }: Meta data of the log
20+
* @param body { LogBody }: Body of the log
21+
*/
22+
export const addLog = async (type: string, meta: LogMeta, body: LogBody): Promise<FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData>> => {
23+
try {
24+
const log = {
25+
type,
26+
timestamp: admin.firestore.Timestamp.fromDate(new Date()),
27+
meta,
28+
body,
29+
};
30+
return await logsModel.add(log);
31+
} catch (err) {
32+
logger.error("Error in adding log", err);
33+
throw new Error(INTERNAL_SERVER_ERROR);
34+
}
35+
};

test/integration/logs.test.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ describe("/logs", function () {
108108
}
109109
expect(res).to.have.status(200);
110110
expect(res.body.message).to.equal("All Logs fetched successfully");
111-
expect(res.body.data).to.lengthOf(7);
111+
expect(res.body.data).to.lengthOf(9);
112112
return done();
113113
});
114114
});
@@ -202,6 +202,37 @@ describe("/logs", function () {
202202
});
203203
});
204204
});
205+
206+
describe("Add logs when user doc is update", function () {
207+
let jwt;
208+
let userId;
209+
210+
beforeEach(async function () {
211+
userId = await addUser();
212+
jwt = authService.generateAuthToken({ userId });
213+
});
214+
215+
it("Should update the users and capture the logs", async function () {
216+
const res = await chai.request(app).patch("/users/self").set("cookie", `${cookieName}=${jwt}`).send({
217+
first_name: "Test first_name",
218+
});
219+
220+
expect(res).to.have.status(204);
221+
222+
const logRes = await chai
223+
.request(app)
224+
.get("/logs/USER_DETAILS_UPDATED")
225+
.set("cookie", `${cookieName}=${superUserToken}`);
226+
227+
expect(logRes).to.have.status(200);
228+
expect(logRes.body.message).to.equal("Logs fetched successfully");
229+
230+
const log = logRes.body.logs[0];
231+
expect(log).to.have.property("meta");
232+
expect(log).to.have.property("body");
233+
expect(log.type).to.equal("USER_DETAILS_UPDATED");
234+
});
235+
});
205236
});
206237

207238
async function addLogs() {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { expect } from "chai";
2+
const Sinon = require("sinon");
3+
const cleanDb = require("../../utils/cleanDb");
4+
const { addLog } = require("../../../services/logService");
5+
const { INTERNAL_SERVER_ERROR } = require("../../../constants/errorMessages");
6+
7+
describe("Logs services", function () {
8+
beforeEach(function () {
9+
Sinon.restore();
10+
});
11+
12+
afterEach(async function () {
13+
await cleanDb();
14+
});
15+
16+
it("should successfully add a log", async function () {
17+
const type = "TEST_LOG";
18+
const meta = {
19+
userId: "test-user-123",
20+
action: "test-action"
21+
};
22+
const body = {
23+
details: "test details",
24+
status: "success"
25+
};
26+
27+
const result = await addLog(type, meta, body);
28+
29+
expect(result).to.have.property('id');
30+
expect(typeof result.id).to.equal('string');
31+
expect(result.id).to.not.be.empty;
32+
});
33+
34+
it("should handle errors when adding log fails", async function () {
35+
36+
const type = "TEST_LOG";
37+
const meta = { userId: "test-user-123" };
38+
const body = { details: "test details" };
39+
40+
try {
41+
await addLog(type, meta, body);
42+
expect.fail(INTERNAL_SERVER_ERROR);
43+
} catch (error) {
44+
expect(error.message).to.equal(INTERNAL_SERVER_ERROR);
45+
}
46+
});
47+
});

0 commit comments

Comments
 (0)