From 2b9cd7a3a034e725d0f3a27269a82b5b21a30623 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 19 Sep 2022 13:35:13 -0700 Subject: [PATCH 1/4] Add pagination to API - Setup `limit` and `offset` values for API --- package-lock.json | 15 +++++++++++ package.json | 1 + src/app.ts | 1 + src/v1/controllers/problems.ts | 13 ++++----- src/v1/services/index.ts | 5 ++-- src/v1/services/problems.ts | 49 ++++++++++++++++++++++++++++++---- 6 files changed, 69 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 34170a9..cf05f1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "dotenv": "^16.0.2", "express": "^4.18.1", "mongoose": "^6.6.1", + "querystring": "^0.2.1", "swagger-ui-express": "^4.5.0", "winston": "^3.8.2" }, @@ -8903,6 +8904,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -18046,6 +18056,11 @@ "side-channel": "^1.0.4" } }, + "querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==" + }, "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", diff --git a/package.json b/package.json index c627bb0..ae72055 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "dotenv": "^16.0.2", "express": "^4.18.1", "mongoose": "^6.6.1", + "querystring": "^0.2.1", "swagger-ui-express": "^4.5.0", "winston": "^3.8.2" }, diff --git a/src/app.ts b/src/app.ts index a941a91..dbf6366 100644 --- a/src/app.ts +++ b/src/app.ts @@ -10,6 +10,7 @@ config(); const serve = async () : Promise => { const app: Application = express(); + app.use(express.json()); try { await Database.connect(); diff --git a/src/v1/controllers/problems.ts b/src/v1/controllers/problems.ts index d74cbe0..21277fa 100644 --- a/src/v1/controllers/problems.ts +++ b/src/v1/controllers/problems.ts @@ -1,12 +1,12 @@ import { Request, Response } from "express"; -import { ProblemData, ProblemService } from "../services"; +import { ResponseJson, ProblemService, Query } from "../services"; export class ProblemController { - public static async getAllProblems(req: Request, res: Response) { - let problems : ProblemData[] | Error; + public static async getAllProblems(req: Request<{}, {}, {}, Query>, res: Response) { + let responseJson : ResponseJson; try { - problems = await ProblemService.getAllProblems(); + responseJson = await ProblemService.getAllProblems(req); } catch (e) { res.status(500).json({ message: "Unexpected error getting problems", @@ -15,9 +15,6 @@ export class ProblemController { return e; } - res.status(200).json({ - message: "Successfully fetched problems", - questions: problems - }); + res.status(200).json(responseJson); } } \ No newline at end of file diff --git a/src/v1/services/index.ts b/src/v1/services/index.ts index da273d8..1b887e1 100644 --- a/src/v1/services/index.ts +++ b/src/v1/services/index.ts @@ -1,6 +1,7 @@ -import { ProblemService, ProblemData } from "./problems"; +import { ProblemService, ResponseJson, Query } from "./problems"; export { ProblemService, - ProblemData + ResponseJson, + Query }; \ No newline at end of file diff --git a/src/v1/services/problems.ts b/src/v1/services/problems.ts index 051507d..513afb3 100644 --- a/src/v1/services/problems.ts +++ b/src/v1/services/problems.ts @@ -1,7 +1,14 @@ import { IProblem, Problems } from '../models/problems'; import { logger } from '../../logger'; +import { Request } from 'express'; -export interface ProblemData { +export interface ResponseJson { + message: string; + questions: ProblemData[]; + paging: PagingData; +} + +interface ProblemData { title: string; title_slug: string; id: Number; @@ -9,17 +16,37 @@ export interface ProblemData { is_premium: boolean; }; +interface PagingData { + total: number; + page: number; + pages: number; +} + +export interface Query { + limit: string; + offset: string; +}; + export class ProblemService { - public static async getAllProblems() : Promise { + public static async getAllProblems(req : Request<{}, {}, {}, Query>) : Promise { let data : IProblem[]; + let total : number; + const limit : number = parseInt(req.query.limit); + const offset : number = parseInt(req.query.offset); try { - data = await Problems.find({}); + data = await Problems.find({}) + .limit(limit) + .skip(offset); + total = await Problems.count(); } catch(e : any) { logger.error("Exception caught retrieving leetcode problems from database: " + e); - return new Error("Error retrieving problems " + e); + throw new Error("Error retrieving problems " + e); } + const totalPages = Math.ceil(total / limit) || 1; + const currentPage = Math.ceil(total % offset) || 0; + const parsedData : ProblemData[] = data.map(problem => { return { title: problem.title, @@ -30,6 +57,18 @@ export class ProblemService { }; }); - return parsedData; + const paging : PagingData = { + total, + page: currentPage, + pages: totalPages + }; + + const responseJson : ResponseJson = { + message: "", + questions: parsedData, + paging + } + + return responseJson; } } \ No newline at end of file From ea21dd6cb77675dfa0b2399b166f77dcd319c915 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 19 Sep 2022 13:37:08 -0700 Subject: [PATCH 2/4] Add formating for JSON - Add `app.set('json spaces', 2)` to get formatted JSON --- src/app.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app.ts b/src/app.ts index dbf6366..1ae188c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -11,6 +11,7 @@ config(); const serve = async () : Promise => { const app: Application = express(); app.use(express.json()); + app.set('json spaces', 2) try { await Database.connect(); From c2cb1040733279479cd10f20d0088b5f3f47903d Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 19 Sep 2022 13:38:27 -0700 Subject: [PATCH 3/4] Fix offset issue --- src/v1/services/problems.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v1/services/problems.ts b/src/v1/services/problems.ts index 513afb3..33eb72c 100644 --- a/src/v1/services/problems.ts +++ b/src/v1/services/problems.ts @@ -45,7 +45,7 @@ export class ProblemService { } const totalPages = Math.ceil(total / limit) || 1; - const currentPage = Math.ceil(total % offset) || 0; + const currentPage = Math.ceil(total % offset) || 1; const parsedData : ProblemData[] = data.map(problem => { return { From 5f1dfd155264b141a1261ca0c6af801ca9450068 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 19 Sep 2022 13:42:29 -0700 Subject: [PATCH 4/4] Add JSON response message --- src/v1/services/problems.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v1/services/problems.ts b/src/v1/services/problems.ts index 33eb72c..c90a58f 100644 --- a/src/v1/services/problems.ts +++ b/src/v1/services/problems.ts @@ -64,7 +64,7 @@ export class ProblemService { }; const responseJson : ResponseJson = { - message: "", + message: "Successfully retrieved all leetcode problems", questions: parsedData, paging }