From ec73ae309e44d8199b4f92725616aae37f20d71c Mon Sep 17 00:00:00 2001 From: Haidang666 Date: Fri, 17 Dec 2021 15:08:51 +0700 Subject: [PATCH 1/3] add custom pagination in getSearchSegments --- src/routes/getSearchSegments.ts | 49 +++++++++++++++++++++--- src/types/segments.model.ts | 10 ++++- test/cases/getSearchSegments.ts | 66 +++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 7 deletions(-) diff --git a/src/routes/getSearchSegments.ts b/src/routes/getSearchSegments.ts index cbfb1f7..e3a6d27 100644 --- a/src/routes/getSearchSegments.ts +++ b/src/routes/getSearchSegments.ts @@ -1,8 +1,8 @@ import { Request, Response } from "express"; import { db } from "../databases/databases"; -import { ActionType, Category, DBSegment, Service, VideoID } from "../types/segments.model"; +import { ActionType, Category, DBSegment, Service, VideoID, SortableFields } from "../types/segments.model"; import { getService } from "../utils/getService"; -const segmentsPerPage = 10; +const maxSegmentsPerPage = 100; type searchSegmentResponse = { segmentCount: number, @@ -19,6 +19,22 @@ function getSegmentsFromDBByVideoID(videoID: VideoID, service: Service): Promise ) as Promise; } +function getSortField(...value: T[]): SortableFields { + const fieldByName = Object.values(SortableFields).reduce((acc, fieldName) => { + acc[fieldName.toLowerCase()] = fieldName; + + return acc; + }, {} as Record); + + for (const name of value) { + if (name?.trim().toLowerCase() in fieldByName) { + return fieldByName[name.trim().toLowerCase()]; + } + } + + return SortableFields.timeSubmitted; +} + /** * * Returns what would be sent to the client. @@ -64,6 +80,10 @@ async function handleGetSegments(req: Request, res: Response): Promise maxSegmentsPerPage ? maxSegmentsPerPage : Number(limit); + const sortBy: SortableFields = getSortField(req.query.sortBy, req.body.sortBy); + const sortDir: string = req.query.sortDir ?? req.body.sortDir ?? "asc"; const minVotes: number = req.query.minVotes ?? req.body.minVotes ?? -3; const maxVotes: number = req.query.maxVotes ?? req.body.maxVotes ?? Infinity; @@ -99,11 +119,11 @@ async function handleGetSegments(req: Request, res: Response): Promise) { - const startIndex = 0+(page*segmentsPerPage); - const endIndex = segmentsPerPage+(page*segmentsPerPage); +function filterSegments(segments: DBSegment[], filters: Record, page: number, limit: number, sortBy: SortableFields, sortDir: string) { + const startIndex = 0+(page*limit); + const endIndex = limit+(page*limit); const filteredSegments = segments.filter((segment) => !((segment.votes < filters.minVotes || segment.votes > filters.maxVotes) || (segment.views < filters.minViews || segment.views > filters.maxViews) @@ -114,10 +134,27 @@ function filterSegments(segments: DBSegment[], page: number, filters: Record { + const key = sortDir === "desc" ? 1 : -1; + if (a[sortBy] < b[sortBy]) { + return key; + } + + if (a[sortBy] > b[sortBy]) { + return -key; + } + + return 0; + }); + } + return { segmentCount: filteredSegments.length, page, segments: filteredSegments.slice(startIndex, endIndex) + }; } diff --git a/src/types/segments.model.ts b/src/types/segments.model.ts index a94bd8e..ba613c4 100644 --- a/src/types/segments.model.ts +++ b/src/types/segments.model.ts @@ -107,4 +107,12 @@ export interface LockCategory { reason: string, videoID: VideoID, userID: UserID -} \ No newline at end of file +} + +export enum SortableFields { + timeSubmitted = "timeSubmitted", + startTime = "startTime", + endTime = "endTime", + votes = "votes", + views = "views", +} diff --git a/test/cases/getSearchSegments.ts b/test/cases/getSearchSegments.ts index d330f55..dc8a3d8 100644 --- a/test/cases/getSearchSegments.ts +++ b/test/cases/getSearchSegments.ts @@ -282,4 +282,70 @@ describe("getSearchSegments", () => { }) .catch(err => done(err)); }); + + it("Should be able to get with custom limit", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", limit: 2 } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments.length, 2); + done(); + }) + .catch(err => done(err)); + }); + + it("Should be able to get with custom limit(2) and page(2)", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", limit: 2, page: 2 } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 2); + assert.strictEqual(segments.length, 2); + assert.strictEqual(segments[0].UUID, "search-page1-5"); + assert.strictEqual(segments[1].UUID, "search-page1-6"); + done(); + }) + .catch(err => done(err)); + }); + + it("Should be able to get sorted result (desc)", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", sortBy: "endTime", sortDir: "desc" } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments[0].UUID, "search-page2-2"); + assert.strictEqual(segments[1].UUID, "search-page2-1"); + assert.strictEqual(segments[2].UUID, "search-page1-10"); + assert.strictEqual(segments[3].UUID, "search-page1-9"); + assert.strictEqual(segments[4].UUID, "search-page1-8"); + done(); + }) + .catch(err => done(err)); + }); + + it("Should be able to get sorted result (asc)", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", sortBy: "endTime" } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments[0].UUID, "search-page1-1"); + assert.strictEqual(segments[1].UUID, "search-page1-2"); + assert.strictEqual(segments[2].UUID, "search-page1-3"); + assert.strictEqual(segments[3].UUID, "search-page1-4"); + assert.strictEqual(segments[4].UUID, "search-page1-5"); + done(); + }) + .catch(err => done(err)); + }); }); From 89a83f78cc90dcfb74ac0d317b11e964d656073a Mon Sep 17 00:00:00 2001 From: Haidang666 Date: Sun, 19 Dec 2021 10:13:06 +0700 Subject: [PATCH 2/3] add more test on limit, page --- src/routes/getSearchSegments.ts | 27 +++++- test/cases/getSearchSegments.ts | 152 ++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 4 deletions(-) diff --git a/src/routes/getSearchSegments.ts b/src/routes/getSearchSegments.ts index e3a6d27..55bbb5e 100644 --- a/src/routes/getSearchSegments.ts +++ b/src/routes/getSearchSegments.ts @@ -3,6 +3,7 @@ import { db } from "../databases/databases"; import { ActionType, Category, DBSegment, Service, VideoID, SortableFields } from "../types/segments.model"; import { getService } from "../utils/getService"; const maxSegmentsPerPage = 100; +const defaultSegmentsPerPage = 10; type searchSegmentResponse = { segmentCount: number, @@ -35,6 +36,26 @@ function getSortField(...value: T[]): SortableFields { return SortableFields.timeSubmitted; } +function getLimit(value: T): number { + const limit = Number(value); + if (Number.isInteger(limit) + && limit >= 1 + && limit <= maxSegmentsPerPage) { + return limit; + } + + return defaultSegmentsPerPage; +} + +function getPage(value: T): number { + const page = Number(value); + if (Number.isInteger(page) && page >= 0) { + return page; + } + + return 0; +} + /** * * Returns what would be sent to the client. @@ -78,10 +99,8 @@ async function handleGetSegments(req: Request, res: Response): Promise maxSegmentsPerPage ? maxSegmentsPerPage : Number(limit); + const page: number = getPage(req.query.page ?? req.body.page); + const limit: number = getLimit(req.query.limit ?? req.body.limit); const sortBy: SortableFields = getSortField(req.query.sortBy, req.body.sortBy); const sortDir: string = req.query.sortDir ?? req.body.sortDir ?? "asc"; diff --git a/test/cases/getSearchSegments.ts b/test/cases/getSearchSegments.ts index dc8a3d8..a0a478c 100644 --- a/test/cases/getSearchSegments.ts +++ b/test/cases/getSearchSegments.ts @@ -313,6 +313,140 @@ describe("getSearchSegments", () => { .catch(err => done(err)); }); + it("Should be able to get with over range page", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", limit: 2, page: 2000 } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 2000); + assert.strictEqual(segments.length, 0); + done(); + }) + .catch(err => done(err)); + }); + + it("Should be able to get with invalid page (=-100)", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", page: -100 } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments.length, 10); + done(); + }) + .catch(err => done(err)); + }); + + it("Should be able to get with invalid page (=text)", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", page: "hello" } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments.length, 10); + done(); + }) + .catch(err => done(err)); + }); + + it("Should be use default limit if invalid limit query (=0)", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", limit: 0 } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments[0].UUID, "search-page1-1"); + assert.strictEqual(segments[1].UUID, "search-page1-2"); + assert.strictEqual(segments[2].UUID, "search-page1-3"); + assert.strictEqual(segments[3].UUID, "search-page1-4"); + assert.strictEqual(segments[4].UUID, "search-page1-5"); + assert.strictEqual(segments[5].UUID, "search-page1-6"); + assert.strictEqual(segments[6].UUID, "search-page1-7"); + assert.strictEqual(segments[7].UUID, "search-page1-8"); + assert.strictEqual(segments[8].UUID, "search-page1-9"); + assert.strictEqual(segments[9].UUID, "search-page1-10"); + done(); + }) + .catch(err => done(err)); + }); + + it("Should be use default limit if invalid limit query (=-100)", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", limit: -100 } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments[0].UUID, "search-page1-1"); + assert.strictEqual(segments[1].UUID, "search-page1-2"); + assert.strictEqual(segments[2].UUID, "search-page1-3"); + assert.strictEqual(segments[3].UUID, "search-page1-4"); + assert.strictEqual(segments[4].UUID, "search-page1-5"); + assert.strictEqual(segments[5].UUID, "search-page1-6"); + assert.strictEqual(segments[6].UUID, "search-page1-7"); + assert.strictEqual(segments[7].UUID, "search-page1-8"); + assert.strictEqual(segments[8].UUID, "search-page1-9"); + assert.strictEqual(segments[9].UUID, "search-page1-10"); + done(); + }) + .catch(err => done(err)); + }); + + it("Should be use default limit if invalid limit query (=text)", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", limit: "hello" } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments[0].UUID, "search-page1-1"); + assert.strictEqual(segments[1].UUID, "search-page1-2"); + assert.strictEqual(segments[2].UUID, "search-page1-3"); + assert.strictEqual(segments[3].UUID, "search-page1-4"); + assert.strictEqual(segments[4].UUID, "search-page1-5"); + assert.strictEqual(segments[5].UUID, "search-page1-6"); + assert.strictEqual(segments[6].UUID, "search-page1-7"); + assert.strictEqual(segments[7].UUID, "search-page1-8"); + assert.strictEqual(segments[8].UUID, "search-page1-9"); + assert.strictEqual(segments[9].UUID, "search-page1-10"); + done(); + }) + .catch(err => done(err)); + }); + + it("Should be use default limit if invalid limit query (=2000)", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", limit: 2000 } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments[0].UUID, "search-page1-1"); + assert.strictEqual(segments[1].UUID, "search-page1-2"); + assert.strictEqual(segments[2].UUID, "search-page1-3"); + assert.strictEqual(segments[3].UUID, "search-page1-4"); + assert.strictEqual(segments[4].UUID, "search-page1-5"); + assert.strictEqual(segments[5].UUID, "search-page1-6"); + assert.strictEqual(segments[6].UUID, "search-page1-7"); + assert.strictEqual(segments[7].UUID, "search-page1-8"); + assert.strictEqual(segments[8].UUID, "search-page1-9"); + assert.strictEqual(segments[9].UUID, "search-page1-10"); + done(); + }) + .catch(err => done(err)); + }); + it("Should be able to get sorted result (desc)", (done) => { client.get(endpoint, { params: { videoID: "searchTest4", sortBy: "endTime", sortDir: "desc" } }) .then(res => { @@ -348,4 +482,22 @@ describe("getSearchSegments", () => { }) .catch(err => done(err)); }); + + it("Should be use default sorted if invalid sort field", (done) => { + client.get(endpoint, { params: { videoID: "searchTest4", sortBy: "not exist", sortDir: "desc" } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 0); + assert.strictEqual(segments[0].UUID, "search-page1-1"); + assert.strictEqual(segments[1].UUID, "search-page1-2"); + assert.strictEqual(segments[2].UUID, "search-page1-3"); + assert.strictEqual(segments[3].UUID, "search-page1-4"); + assert.strictEqual(segments[4].UUID, "search-page1-5"); + done(); + }) + .catch(err => done(err)); + }); }); From 7caaf833ddb5bebadea2c1ffea893e62b60af288 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Mon, 20 Dec 2021 22:29:48 -0500 Subject: [PATCH 3/3] Fix safe navigation --- src/routes/getSearchSegments.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/getSearchSegments.ts b/src/routes/getSearchSegments.ts index 55bbb5e..51e4740 100644 --- a/src/routes/getSearchSegments.ts +++ b/src/routes/getSearchSegments.ts @@ -28,7 +28,7 @@ function getSortField(...value: T[]): SortableFields { }, {} as Record); for (const name of value) { - if (name?.trim().toLowerCase() in fieldByName) { + if (name?.trim()?.toLowerCase() in fieldByName) { return fieldByName[name.trim().toLowerCase()]; } }