Skip to content

Commit 3676073

Browse files
authored
Merge pull request #1089 from Real-Dev-Squad/feat/progresses-api-v1.5
Implement API endpoint for retrieving progress document by user or task for a particular date
2 parents 55d3537 + c8cccd8 commit 3676073

File tree

8 files changed

+423
-31
lines changed

8 files changed

+423
-31
lines changed

constants/progresses.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const PROGRESS_DOCUMENT_RETRIEVAL_SUCCEEDED = "Progress document retrieved succe
33
const PROGRESS_DOCUMENT_NOT_FOUND = "No progress records found.";
44
const PROGRESS_ALREADY_CREATED = "Progress for the day has already been created.";
55
const MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
6+
const INTERNAL_SERVER_ERROR_MESSAGE =
7+
"The server has encountered an unexpected error. Please contact the administrator for more information.";
68

79
const RESPONSE_MESSAGES = {
810
PROGRESS_DOCUMENT_CREATED_SUCCEEDED,
@@ -11,4 +13,17 @@ const RESPONSE_MESSAGES = {
1113
PROGRESS_ALREADY_CREATED,
1214
};
1315

14-
module.exports = { RESPONSE_MESSAGES, MILLISECONDS_IN_DAY };
16+
const TYPE_MAP = {
17+
user: "userId",
18+
task: "taskId",
19+
};
20+
21+
const VALID_PROGRESS_TYPES = ["task", "user"];
22+
23+
module.exports = {
24+
RESPONSE_MESSAGES,
25+
MILLISECONDS_IN_DAY,
26+
INTERNAL_SERVER_ERROR_MESSAGE,
27+
TYPE_MAP,
28+
VALID_PROGRESS_TYPES,
29+
};

controllers/progresses.js

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
const { Conflict, NotFound } = require("http-errors");
2-
const { createProgressDocument, getProgressDocument, getRangeProgressData } = require("../models/progresses");
3-
const { RESPONSE_MESSAGES } = require("../constants/progresses");
2+
const {
3+
createProgressDocument,
4+
getProgressDocument,
5+
getRangeProgressData,
6+
getProgressByDate,
7+
} = require("../models/progresses");
8+
const { RESPONSE_MESSAGES, INTERNAL_SERVER_ERROR_MESSAGE } = require("../constants/progresses");
49
const { PROGRESS_DOCUMENT_RETRIEVAL_SUCCEEDED, PROGRESS_DOCUMENT_CREATED_SUCCEEDED } = RESPONSE_MESSAGES;
510

611
/**
@@ -58,8 +63,9 @@ const createProgress = async (req, res) => {
5863
message: error.message,
5964
});
6065
}
61-
return res.status(400).json({
62-
message: error.message,
66+
logger.error(error.message);
67+
return res.status(500).json({
68+
message: INTERNAL_SERVER_ERROR_MESSAGE,
6369
});
6470
}
6571
};
@@ -112,8 +118,9 @@ const getProgress = async (req, res) => {
112118
message: error.message,
113119
});
114120
}
115-
return res.status(400).json({
116-
message: error.message,
121+
logger.error(error.message);
122+
return res.status(500).json({
123+
message: INTERNAL_SERVER_ERROR_MESSAGE,
117124
});
118125
}
119126
};
@@ -165,10 +172,65 @@ const getProgressRangeData = async (req, res) => {
165172
message: error.message,
166173
});
167174
}
168-
return res.status(400).json({
169-
message: error.message,
175+
logger.error(error.message);
176+
return res.status(500).json({
177+
message: INTERNAL_SERVER_ERROR_MESSAGE,
170178
});
171179
}
172180
};
173181

174-
module.exports = { createProgress, getProgress, getProgressRangeData };
182+
/**
183+
* @typedef {Object} progressPathParams
184+
* @property {string} type - The type of progress document user or task.
185+
* @property {string} typeId - The ID of the type.
186+
* @property {string} date - The iso format date of the query.
187+
*/
188+
189+
/**
190+
* @typedef {Object} ProgressDocument
191+
* @property {string} id - The id of the progress document.
192+
* @property {string} type - The type of progress document.
193+
* @property {string} completed - The completed progress.
194+
* @property {string} planned - The planned progress.
195+
* @property {string} blockers - The blockers.
196+
* @property {string} userId - The User ID
197+
* @property {string} [taskId] - The task ID (optional).
198+
* @property {number} createdAt - The timestamp when the progress document was created.
199+
* @property {number} date - The timestamp for the day the progress document was created.
200+
*/
201+
202+
/**
203+
* @typedef {Object} GetProgressByDateResponse
204+
* @property {string} message - The success message.
205+
* @property {ProgressDocument} data - An array of progress documents
206+
*/
207+
208+
/**
209+
* Retrieves the progress documents based on provided query parameters.
210+
* @param {Object} req - The HTTP request object.
211+
* @param {progressPathParams} req.params - The query parameters
212+
* @param {Object} res - The HTTP response object.
213+
* @returns {Promise<void>} A Promise that resolves when the response is sent.
214+
*/
215+
216+
const getProgressBydDateController = async (req, res) => {
217+
try {
218+
const data = await getProgressByDate(req.params);
219+
return res.json({
220+
message: PROGRESS_DOCUMENT_RETRIEVAL_SUCCEEDED,
221+
data,
222+
});
223+
} catch (error) {
224+
if (error instanceof NotFound) {
225+
return res.status(404).json({
226+
message: error.message,
227+
});
228+
}
229+
logger.error(error.message);
230+
return res.status(500).json({
231+
message: INTERNAL_SERVER_ERROR_MESSAGE,
232+
});
233+
}
234+
};
235+
236+
module.exports = { createProgress, getProgress, getProgressRangeData, getProgressBydDateController };

middlewares/validators/progresses.js

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
const joi = require("joi");
2+
const { VALID_PROGRESS_TYPES } = require("../../constants/progresses");
23

34
const validateCreateProgressRecords = async (req, res, next) => {
45
const baseSchema = joi
56
.object()
67
.strict()
78
.keys({
8-
type: joi.string().trim().valid("user", "task").required().messages({
9-
"any.required": "Required field 'type' is missing.",
10-
"any.only": "Type field is restricted to either 'user' or 'task'.",
11-
}),
9+
type: joi
10+
.string()
11+
.trim()
12+
.valid(...VALID_PROGRESS_TYPES)
13+
.required()
14+
.messages({
15+
"any.required": "Required field 'type' is missing.",
16+
"any.only": "Type field is restricted to either 'user' or 'task'.",
17+
}),
1218
completed: joi.string().trim().required().messages({
1319
"any.required": "Required field 'completed' is missing.",
1420
"string.trim": "completed must not have leading or trailing whitespace",
@@ -44,9 +50,13 @@ const validateCreateProgressRecords = async (req, res, next) => {
4450
const validateGetProgressRecordsQuery = async (req, res, next) => {
4551
const schema = joi
4652
.object({
47-
type: joi.string().valid("user", "task").optional().messages({
48-
"any.only": "Type field is restricted to either 'user' or 'task'.",
49-
}),
53+
type: joi
54+
.string()
55+
.valid(...VALID_PROGRESS_TYPES)
56+
.optional()
57+
.messages({
58+
"any.only": "Type field is restricted to either 'user' or 'task'.",
59+
}),
5060
userId: joi.string().optional().allow("").messages({
5161
"string.base": "userId must be a string",
5262
}),
@@ -92,8 +102,30 @@ const validateGetRangeProgressRecordsParams = async (req, res, next) => {
92102
res.boom.badRequest(error.details[0].message);
93103
}
94104
};
105+
106+
const validateGetDayProgressParams = async (req, res, next) => {
107+
const schema = joi.object({
108+
type: joi
109+
.string()
110+
.valid(...VALID_PROGRESS_TYPES)
111+
.required()
112+
.messages({
113+
"any.only": "Type field is restricted to either 'user' or 'task'.",
114+
}),
115+
typeId: joi.string().required(),
116+
date: joi.date().iso().required(),
117+
});
118+
try {
119+
await schema.validateAsync(req.params, { abortEarly: false });
120+
next();
121+
} catch (error) {
122+
logger.error(`Error validating payload: ${error}`);
123+
res.boom.badRequest(error.details[0].message);
124+
}
125+
};
95126
module.exports = {
96127
validateCreateProgressRecords,
97128
validateGetProgressRecordsQuery,
98129
validateGetRangeProgressRecordsParams,
130+
validateGetDayProgressParams,
99131
};

models/progresses.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
const { Conflict } = require("http-errors");
1+
const { Conflict, NotFound } = require("http-errors");
22
const fireStore = require("../utils/firestore");
33
const progressesCollection = fireStore.collection("progresses");
4-
const { RESPONSE_MESSAGES } = require("../constants/progresses");
4+
const { RESPONSE_MESSAGES, TYPE_MAP } = require("../constants/progresses");
55
const {
66
buildQueryToFetchDocs,
77
getProgressDocs,
@@ -11,8 +11,9 @@ const {
1111
buildQueryForPostingProgress,
1212
assertTaskExists,
1313
getProgressDateTimestamp,
14+
buildQueryToSearchProgressByDay,
1415
} = require("../utils/progresses");
15-
const { PROGRESS_ALREADY_CREATED } = RESPONSE_MESSAGES;
16+
const { PROGRESS_ALREADY_CREATED, PROGRESS_DOCUMENT_NOT_FOUND } = RESPONSE_MESSAGES;
1617

1718
/**
1819
* Adds a new progress document for the given user or task, with a limit of one progress document per day.
@@ -68,4 +69,22 @@ const getRangeProgressData = async (queryParams) => {
6869
};
6970
};
7071

71-
module.exports = { createProgressDocument, getProgressDocument, getRangeProgressData };
72+
/**
73+
* This function fetches the progress records for a particular user or task on the specified date.
74+
* @param pathParams {object} This is the data that will be used for querying the db. It should contain type, typeId and date
75+
* @returns {Promise<object>} A Promise that resolves with the progress records of the queried user or task.
76+
* @throws {Error} If the userId or taskId is invalid or does not exist.
77+
**/
78+
async function getProgressByDate(pathParams) {
79+
const { type, typeId, date } = pathParams;
80+
await assertUserOrTaskExists({ [TYPE_MAP[type]]: typeId });
81+
const query = buildQueryToSearchProgressByDay({ [TYPE_MAP[type]]: typeId, date });
82+
const result = await query.get();
83+
if (!result.size) {
84+
throw new NotFound(PROGRESS_DOCUMENT_NOT_FOUND);
85+
}
86+
const doc = result.docs[0];
87+
return { id: doc.id, ...doc.data() };
88+
}
89+
90+
module.exports = { createProgressDocument, getProgressDocument, getRangeProgressData, getProgressByDate };

routes/progresses.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@ const {
55
validateCreateProgressRecords,
66
validateGetProgressRecordsQuery,
77
validateGetRangeProgressRecordsParams,
8+
validateGetDayProgressParams,
89
} = require("../middlewares/validators/progresses");
9-
const { createProgress, getProgress, getProgressRangeData } = require("../controllers/progresses");
10+
const {
11+
createProgress,
12+
getProgress,
13+
getProgressRangeData,
14+
getProgressBydDateController,
15+
} = require("../controllers/progresses");
1016

1117
router.post("/", authenticate, validateCreateProgressRecords, createProgress);
1218
router.get("/", validateGetProgressRecordsQuery, getProgress);
19+
router.get("/:type/:typeId/date/:date", validateGetDayProgressParams, getProgressBydDateController);
1320
router.get("/range", validateGetRangeProgressRecordsParams, getProgressRangeData);
1421

1522
module.exports = router;

0 commit comments

Comments
 (0)