Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Integrate userData into Progresses API to reduce redundant calls #2311

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
2 changes: 1 addition & 1 deletion controllers/progresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ const getProgressRangeData = async (req, res) => {

const getProgressBydDateController = async (req, res) => {
try {
const data = await getProgressByDate(req.params);
const data = await getProgressByDate(req.params, req.query);
return res.json({
message: PROGRESS_DOCUMENT_RETRIEVAL_SUCCEEDED,
data,
Expand Down
5 changes: 5 additions & 0 deletions middlewares/validators/progresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ const validateGetProgressRecordsQuery = async (req, res, next) => {
taskId: joi.string().optional().allow("").messages({
"string.base": "taskId must be a string",
}),
dev: joi.boolean().optional().messages({
"boolean.base": "dev must be a boolean value (true or false).",
}),
orderBy: joi
.string()
.optional()
Expand Down Expand Up @@ -92,6 +95,7 @@ const validateGetRangeProgressRecordsParams = async (req, res, next) => {
taskId: joi.string().optional(),
startDate: joi.date().iso().required(),
endDate: joi.date().iso().min(joi.ref("startDate")).required(),
dev: joi.boolean().optional(),
})
.xor("userId", "taskId")
.messages({
Expand Down Expand Up @@ -121,6 +125,7 @@ const validateGetDayProgressParams = async (req, res, next) => {
}),
typeId: joi.string().required(),
date: joi.date().iso().required(),
dev: joi.boolean().optional(),
});
try {
await schema.validateAsync(req.params, { abortEarly: false });
Expand Down
43 changes: 41 additions & 2 deletions models/progresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
getProgressDateTimestamp,
buildQueryToSearchProgressByDay,
} = require("../utils/progresses");
const { fetchUser } = require("./users");
const { PROGRESS_ALREADY_CREATED, PROGRESS_DOCUMENT_NOT_FOUND } = PROGRESSES_RESPONSE_MESSAGES;

/**
Expand Down Expand Up @@ -47,9 +48,40 @@
* @throws {Error} If the userId or taskId is invalid or does not exist.
**/
const getProgressDocument = async (queryParams) => {
const { dev } = queryParams;
vikasosmium marked this conversation as resolved.
Show resolved Hide resolved
await assertUserOrTaskExists(queryParams);
const query = buildQueryToFetchDocs(queryParams);
const progressDocs = await getProgressDocs(query);

if (dev) {
AnujChhikara marked this conversation as resolved.
Show resolved Hide resolved
try {
const uniqueUserIds = [...new Set(progressDocs.map((doc) => doc.userId))];
const batchSize = 500;

const batches = Array.from({ length: Math.ceil(uniqueUserIds.length / batchSize) }, (_, index) =>
uniqueUserIds.slice(index * batchSize, index * batchSize + batchSize)
);

const batchPromises = batches.map((batch) => Promise.all(batch.map((userId) => fetchUser({ userId }))));
AnujChhikara marked this conversation as resolved.
Show resolved Hide resolved

const usersDataBatches = await Promise.all(batchPromises);

const usersData = usersDataBatches.flat();

const userLookupMap = usersData.reduce((lookup, { user }) => {
if (user) lookup[user.id] = user;
Achintya-Chatterjee marked this conversation as resolved.
Show resolved Hide resolved
return lookup;
}, {});

const progressDocsWithUserDetails = progressDocs.map((doc) => {
const userDetails = userLookupMap[doc.userId] || null;
return { ...doc, userData: userDetails };
});
return progressDocsWithUserDetails;
} catch (err) {
return progressDocs.map((doc) => ({ ...doc, userData: null }));
}
}
return progressDocs;
};

Expand Down Expand Up @@ -77,16 +109,23 @@
* @returns {Promise<object>} A Promise that resolves with the progress records of the queried user or task.
* @throws {Error} If the userId or taskId is invalid or does not exist.
**/
async function getProgressByDate(pathParams) {
async function getProgressByDate(pathParams, queryParams) {
const { type, typeId, date } = pathParams;
const { dev } = queryParams;
await assertUserOrTaskExists({ [TYPE_MAP[type]]: typeId });

Check warning on line 115 in models/progresses.js

View workflow job for this annotation

GitHub Actions / build (20.11.x)

Generic Object Injection Sink
const query = buildQueryToSearchProgressByDay({ [TYPE_MAP[type]]: typeId, date });

Check warning on line 116 in models/progresses.js

View workflow job for this annotation

GitHub Actions / build (20.11.x)

Generic Object Injection Sink
const result = await query.get();
if (!result.size) {
throw new NotFound(PROGRESS_DOCUMENT_NOT_FOUND);
}
const doc = result.docs[0];
return { id: doc.id, ...doc.data() };
const docData = doc.data();
if (dev) {
const { user: userData } = await fetchUser({ userId: docData.userId });
return { id: doc.id, ...docData, userData };
}

return { id: doc.id, ...docData };
AnujChhikara marked this conversation as resolved.
Show resolved Hide resolved
}

module.exports = { createProgressDocument, getProgressDocument, getRangeProgressData, getProgressByDate };
72 changes: 72 additions & 0 deletions test/integration/progressesTasks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,49 @@ describe("Test Progress Updates API for Tasks", function () {
});
});

it("Returns the progress array for the task with userData object", function (done) {
chai
.request(app)
Achintya-Chatterjee marked this conversation as resolved.
Show resolved Hide resolved
.get(`/progresses?taskId=${taskId1}&dev=true`)
.end((err, res) => {
if (err) return done(err);
expect(res).to.have.status(200);
expect(res.body).to.have.keys(["message", "data", "count"]);
expect(res.body.data).to.be.an("array");
expect(res.body.message).to.be.equal("Progress document retrieved successfully.");
res.body.data.forEach((progress) => {
expect(progress).to.have.keys([
"id",
"taskId",
"type",
"completed",
"planned",
"blockers",
"userData",
"userId",
"createdAt",
"date",
]);
});
return done();
});
});

it("Returns a 404 error when the task does not exist", function (done) {
chai
.request(app)
.get(`/progresses?taskId=nonExistingTaskId&dev=true`)
.end((err, res) => {
if (err) return done(err);

expect(res).to.have.status(404);
expect(res.body).to.have.keys(["message"]);
expect(res.body.message).to.be.equal(`Task with id nonExistingTaskId does not exist.`);

return done();
});
});

it("Gives 400 status when anything other than -date or date is supplied", function (done) {
chai
.request(app)
Expand Down Expand Up @@ -311,6 +354,35 @@ describe("Test Progress Updates API for Tasks", function () {
});
});

it("Returns the progress array for all the tasks with userData object", function (done) {
chai
.request(app)
.get(`/progresses?type=task&dev=true`)
.end((err, res) => {
if (err) return done(err);
expect(res).to.have.status(200);
expect(res.body).to.have.keys(["message", "data", "count"]);
expect(res.body.data).to.be.an("array");
expect(res.body.message).to.be.equal("Progress document retrieved successfully.");
expect(res.body.count).to.be.equal(4);
res.body.data.forEach((progress) => {
expect(progress).to.have.keys([
"id",
"taskId",
"type",
"completed",
"planned",
"blockers",
"userData",
"userId",
"createdAt",
"date",
]);
});
return done();
});
});

it("Returns 400 for bad request", function (done) {
chai
.request(app)
Expand Down
79 changes: 79 additions & 0 deletions test/integration/progressesUsers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,60 @@ describe("Test Progress Updates API for Users", function () {
});
});

it("Returns the progress array for a specific user with userData object", function (done) {
chai
.request(app)
.get(`/progresses?userId=${userId1}&dev=true`)
.end((err, res) => {
if (err) return done(err);
expect(res).to.have.status(200);
expect(res.body).to.have.keys(["message", "data", "count"]);
expect(res.body.data).to.be.an("array");
expect(res.body.message).to.be.equal("Progress document retrieved successfully.");
res.body.data.forEach((progress) => {
expect(progress).to.have.keys([
"id",
"type",
"completed",
"planned",
"blockers",
"userId",
"userData",
"createdAt",
"date",
]);
});
return done();
});
});

it("Returns the progress array for all the user with userData object when dev is true", function (done) {
chai
.request(app)
.get(`/progresses?type=user&dev=true`)
.end((err, res) => {
if (err) return done(err);
expect(res).to.have.status(200);
expect(res.body).to.have.keys(["message", "data", "count"]);
expect(res.body.data).to.be.an("array");
expect(res.body.message).to.be.equal("Progress document retrieved successfully.");
res.body.data.forEach((progress) => {
expect(progress).to.have.keys([
"id",
"type",
"completed",
"planned",
"blockers",
"userId",
"userData",
"createdAt",
"date",
]);
});
return done();
});
});

it("Returns 400 for bad request", function (done) {
chai
.request(app)
Expand Down Expand Up @@ -370,6 +424,31 @@ describe("Test Progress Updates API for Users", function () {
});
});

it("Returns the progress data for a specific user with userData object", function (done) {
chai
.request(app)
.get(`/progresses/user/${userId}/date/2023-05-02?dev=true`)
.end((err, res) => {
if (err) return done(err);
expect(res).to.have.status(200);
expect(res.body).to.have.keys(["message", "data"]);
expect(res.body.data).to.be.an("object");
expect(res.body.message).to.be.equal("Progress document retrieved successfully.");
expect(res.body.data).to.have.keys([
"id",
"type",
"completed",
"planned",
"blockers",
"userData",
"userId",
"createdAt",
"date",
]);
return done();
});
});

it("Should return 404 No progress records found if the document doesn't exist", function (done) {
chai
.request(app)
Expand Down
Loading