From c21e52a50482f7879e152284c5c8d68e65d0c674 Mon Sep 17 00:00:00 2001 From: Sugat Bajracharya Date: Fri, 6 Dec 2024 15:23:24 +0545 Subject: [PATCH] change limit to number and stringified number in cht-datasource --- api/src/controllers/contact.js | 4 +--- api/src/controllers/person.js | 3 +-- api/src/controllers/place.js | 3 +-- api/src/controllers/report.js | 3 +-- shared-libs/cht-datasource/src/contact.ts | 4 ++-- shared-libs/cht-datasource/src/index.ts | 8 ++++---- shared-libs/cht-datasource/src/libs/core.ts | 5 +++-- shared-libs/cht-datasource/src/person.ts | 4 ++-- shared-libs/cht-datasource/src/place.ts | 4 ++-- shared-libs/cht-datasource/src/report.ts | 4 ++-- .../cht-datasource/test/contact.spec.ts | 19 +++++++++++++++++++ .../cht-datasource/test/person.spec.ts | 15 +++++++++++++++ shared-libs/cht-datasource/test/place.spec.ts | 15 +++++++++++++++ .../cht-datasource/test/report.spec.ts | 16 ++++++++++++++++ .../cht-datasource/contact.spec.js | 11 +++++++++++ .../shared-libs/cht-datasource/person.spec.js | 10 ++++++++++ .../shared-libs/cht-datasource/place.spec.js | 11 +++++++++++ .../shared-libs/cht-datasource/report.spec.js | 10 ++++++++++ 18 files changed, 126 insertions(+), 23 deletions(-) diff --git a/api/src/controllers/contact.js b/api/src/controllers/contact.js index 6d3eae9f44b..a03b812cb7c 100644 --- a/api/src/controllers/contact.js +++ b/api/src/controllers/contact.js @@ -42,9 +42,7 @@ module.exports = { Object.assign(qualifier, Qualifier.byContactType(req.query.type)); } - const limit = req.query.limit ? Number(req.query.limit) : req.query.limit; - - const docs = await getContactIds()(qualifier, req.query.cursor, limit); + const docs = await getContactIds()(qualifier, req.query.cursor, req.query.limit); return res.json(docs); }), diff --git a/api/src/controllers/person.js b/api/src/controllers/person.js index 55ea939fcd2..dd66f3fbfd7 100644 --- a/api/src/controllers/person.js +++ b/api/src/controllers/person.js @@ -32,9 +32,8 @@ module.exports = { await checkUserPermissions(req); const personType = Qualifier.byContactType(req.query.type); - const limit = req.query.limit ? Number(req.query.limit) : req.query.limit; - const docs = await getPageByType()( personType, req.query.cursor, limit ); + const docs = await getPageByType()( personType, req.query.cursor, req.query.limit ); return res.json(docs); }), diff --git a/api/src/controllers/place.js b/api/src/controllers/place.js index ff184786561..9a30038067c 100644 --- a/api/src/controllers/place.js +++ b/api/src/controllers/place.js @@ -33,9 +33,8 @@ module.exports = { await checkUserPermissions(req); const placeType = Qualifier.byContactType(req.query.type); - const limit = req.query.limit ? Number(req.query.limit) : req.query.limit; - const docs = await getPageByType()( placeType, req.query.cursor, limit ); + const docs = await getPageByType()( placeType, req.query.cursor, req.query.limit ); return res.json(docs); }) diff --git a/api/src/controllers/report.js b/api/src/controllers/report.js index f7757380d6e..b4b194b36b4 100644 --- a/api/src/controllers/report.js +++ b/api/src/controllers/report.js @@ -30,9 +30,8 @@ module.exports = { await checkUserPermissions(req); const qualifier = Qualifier.byFreetext(req.query.freetext); - const limit = req.query.limit ? Number(req.query.limit) : req.query.limit; - const docs = await getReportIds()(qualifier, req.query.cursor, limit); + const docs = await getReportIds()(qualifier, req.query.cursor, req.query.limit); return res.json(docs); }) diff --git a/shared-libs/cht-datasource/src/contact.ts b/shared-libs/cht-datasource/src/contact.ts index d60169469f8..ec64a934c18 100644 --- a/shared-libs/cht-datasource/src/contact.ts +++ b/shared-libs/cht-datasource/src/contact.ts @@ -102,7 +102,7 @@ export namespace v1 { const curriedFn = async ( qualifier: ContactTypeQualifier | FreetextQualifier, cursor: Nullable = null, - limit = DEFAULT_CONTACT_PAGE_LIMIT + limit: number | `${number}` = DEFAULT_CONTACT_PAGE_LIMIT ): Promise> => { assertCursor(cursor); assertLimit(limit); @@ -115,7 +115,7 @@ export namespace v1 { assertFreetextQualifier(qualifier); } - return fn(qualifier, cursor, limit); + return fn(qualifier, cursor, Number(limit)); }; return curriedFn; }; diff --git a/shared-libs/cht-datasource/src/index.ts b/shared-libs/cht-datasource/src/index.ts index a7fdfc31f96..e27034f3d55 100644 --- a/shared-libs/cht-datasource/src/index.ts +++ b/shared-libs/cht-datasource/src/index.ts @@ -98,7 +98,7 @@ export const getDatasource = (ctx: DataContext) => { freetext: Nullable = null, type: Nullable = null, cursor: Nullable = null, - limit = DEFAULT_CONTACT_PAGE_LIMIT + limit: number | `${number}` = DEFAULT_CONTACT_PAGE_LIMIT ) => ctx.bind(Contact.v1.getIdsPage)( Contact.v1.createQualifier(freetext, type), cursor, limit ), @@ -149,7 +149,7 @@ export const getDatasource = (ctx: DataContext) => { getPageByType: ( placeType: string, cursor: Nullable = null, - limit = DEFAULT_PLACE_PAGE_LIMIT + limit: number | `${number}` = DEFAULT_PLACE_PAGE_LIMIT ) => ctx.bind(Place.v1.getPage)( Qualifier.byContactType(placeType), cursor, limit ), @@ -194,7 +194,7 @@ export const getDatasource = (ctx: DataContext) => { getPageByType: ( personType: string, cursor: Nullable = null, - limit = DEFAULT_PEOPLE_PAGE_LIMIT + limit: number | `${number}` = DEFAULT_PEOPLE_PAGE_LIMIT ) => ctx.bind(Person.v1.getPage)( Qualifier.byContactType(personType), cursor, limit ), @@ -231,7 +231,7 @@ export const getDatasource = (ctx: DataContext) => { getIdsPage: ( qualifier: string, cursor: Nullable = null, - limit = DEFAULT_REPORT_PAGE_LIMIT + limit: number | `${number}` = DEFAULT_REPORT_PAGE_LIMIT // eslint-disable-next-line compat/compat ) => ctx.bind(Report.v1.getIdsPage)( Qualifier.byFreetext(qualifier), cursor, limit diff --git a/shared-libs/cht-datasource/src/libs/core.ts b/shared-libs/cht-datasource/src/libs/core.ts index b27ba0a3e7f..4d45b3b3749 100644 --- a/shared-libs/cht-datasource/src/libs/core.ts +++ b/shared-libs/cht-datasource/src/libs/core.ts @@ -157,8 +157,9 @@ export const assertTypeQualifier: (qualifier: unknown) => asserts qualifier is C }; /** @internal */ -export const assertLimit: (limit: unknown) => asserts limit is number = (limit: unknown) => { - if (typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0) { +export const assertLimit: (limit: unknown) => asserts limit is number | `${number}` = (limit: unknown) => { + const numberLimit = Number(limit); + if (!Number.isInteger(numberLimit) || numberLimit <= 0) { throw new InvalidArgumentError(`The limit must be a positive number: [${String(limit)}].`); } }; diff --git a/shared-libs/cht-datasource/src/person.ts b/shared-libs/cht-datasource/src/person.ts index 3cc1979a939..4ae74759d22 100644 --- a/shared-libs/cht-datasource/src/person.ts +++ b/shared-libs/cht-datasource/src/person.ts @@ -89,13 +89,13 @@ export namespace v1 { const curriedFn = async ( personType: ContactTypeQualifier, cursor: Nullable = null, - limit = DEFAULT_PEOPLE_PAGE_LIMIT + limit: number | `${number}` = DEFAULT_PEOPLE_PAGE_LIMIT ): Promise> => { assertTypeQualifier(personType); assertCursor(cursor); assertLimit(limit); - return fn(personType, cursor, limit); + return fn(personType, cursor, Number(limit)); }; return curriedFn; }; diff --git a/shared-libs/cht-datasource/src/place.ts b/shared-libs/cht-datasource/src/place.ts index e67a6598228..9eac7e5292c 100644 --- a/shared-libs/cht-datasource/src/place.ts +++ b/shared-libs/cht-datasource/src/place.ts @@ -89,13 +89,13 @@ export namespace v1 { const curriedFn = async ( placeType: ContactTypeQualifier, cursor: Nullable = null, - limit = DEFAULT_PLACE_PAGE_LIMIT + limit: number | `${number}` = DEFAULT_PLACE_PAGE_LIMIT ): Promise> => { assertTypeQualifier(placeType); assertCursor(cursor); assertLimit(limit); - return fn(placeType, cursor, limit); + return fn(placeType, cursor, Number(limit)); }; return curriedFn; }; diff --git a/shared-libs/cht-datasource/src/report.ts b/shared-libs/cht-datasource/src/report.ts index dcd359eee2e..14f49795d62 100644 --- a/shared-libs/cht-datasource/src/report.ts +++ b/shared-libs/cht-datasource/src/report.ts @@ -82,13 +82,13 @@ export namespace v1 { const curriedFn = async ( qualifier: FreetextQualifier, cursor: Nullable = null, - limit = DEFAULT_REPORT_PAGE_LIMIT + limit: number | `${number}` = DEFAULT_REPORT_PAGE_LIMIT ): Promise> => { assertFreetextQualifier(qualifier); assertCursor(cursor); assertLimit(limit); - return fn(qualifier, cursor, limit); + return fn(qualifier, cursor, Number(limit)); }; return curriedFn; }; diff --git a/shared-libs/cht-datasource/test/contact.spec.ts b/shared-libs/cht-datasource/test/contact.spec.ts index e92cd9876e4..9ac65f667b1 100644 --- a/shared-libs/cht-datasource/test/contact.spec.ts +++ b/shared-libs/cht-datasource/test/contact.spec.ts @@ -160,6 +160,7 @@ describe('contact', () => { const cursor = '1'; const pageData = { data: contactIds, cursor }; const limit = 3; + const stringifiedLimit = '3'; const contactTypeQualifier = { contactType: 'person' } as const; const freetextQualifier = { freetext: 'freetext'} as const; const qualifier = { @@ -209,6 +210,24 @@ describe('contact', () => { expect(isFreetextQualifier.calledOnceWithExactly(qualifier)).to.be.true; }); + it('retrieves contact id page from the data context when cursor is not null and ' + + 'limit is stringified number', async () => { + isContactTypeQualifier.returns(true); + isFreetextQualifier.returns(true); + getIdsPage.resolves(pageData); + + const result = await Contact.v1.getIdsPage(dataContext)(qualifier, cursor, stringifiedLimit); + + expect(result).to.equal(pageData); + expect(assertDataContext.calledOnceWithExactly(dataContext)).to.be.true; + expect( + adapt.calledOnceWithExactly(dataContext, Local.Contact.v1.getPage, Remote.Contact.v1.getPage) + ).to.be.true; + expect(getIdsPage.calledOnceWithExactly(qualifier, cursor, limit)).to.be.true; + expect(isContactTypeQualifier.calledOnceWithExactly(qualifier)).to.be.true; + expect(isFreetextQualifier.calledOnceWithExactly(qualifier)).to.be.true; + }); + it('throws an error if the data context is invalid', () => { isContactTypeQualifier.returns(true); isFreetextQualifier.returns(true); diff --git a/shared-libs/cht-datasource/test/person.spec.ts b/shared-libs/cht-datasource/test/person.spec.ts index ae112b23cab..339322d2303 100644 --- a/shared-libs/cht-datasource/test/person.spec.ts +++ b/shared-libs/cht-datasource/test/person.spec.ts @@ -132,6 +132,7 @@ describe('person', () => { const cursor = '1'; const pageData = { data: people, cursor }; const limit = 3; + const stringifiedLimit = '3'; const personTypeQualifier = {contactType: 'person'} as const; const invalidQualifier = { contactType: 'invalid' } as const; let getPage: SinonStub; @@ -167,6 +168,20 @@ describe('person', () => { expect(isContactTypeQualifier.calledOnceWithExactly((personTypeQualifier))).to.be.true; }); + it('retrieves people from the data context when cursor is not null and ' + + 'limit is stringified number', async () => { + isContactTypeQualifier.returns(true); + getPage.resolves(pageData); + + const result = await Person.v1.getPage(dataContext)(personTypeQualifier, cursor, stringifiedLimit); + + expect(result).to.equal(pageData); + expect(assertDataContext.calledOnceWithExactly(dataContext)).to.be.true; + expect(adapt.calledOnceWithExactly(dataContext, Local.Person.v1.getPage, Remote.Person.v1.getPage)).to.be.true; + expect(getPage.calledOnceWithExactly(personTypeQualifier, cursor, limit)).to.be.true; + expect(isContactTypeQualifier.calledOnceWithExactly((personTypeQualifier))).to.be.true; + }); + it('throws an error if the data context is invalid', () => { isContactTypeQualifier.returns(true); assertDataContext.throws(new Error(`Invalid data context [null].`)); diff --git a/shared-libs/cht-datasource/test/place.spec.ts b/shared-libs/cht-datasource/test/place.spec.ts index bd3d4963fb9..23833939238 100644 --- a/shared-libs/cht-datasource/test/place.spec.ts +++ b/shared-libs/cht-datasource/test/place.spec.ts @@ -132,6 +132,7 @@ describe('place', () => { const cursor = '1'; const pageData = { data: places, cursor }; const limit = 3; + const stringifiedLimit = '3'; const placeTypeQualifier = {contactType: 'place'} as const; const invalidQualifier = { contactType: 'invalid' } as const; let getPage: SinonStub; @@ -167,6 +168,20 @@ describe('place', () => { expect(isContactTypeQualifier.calledOnceWithExactly((placeTypeQualifier))).to.be.true; }); + it('retrieves places from the data context when cursor is not null and ' + + 'limit is stringified number', async () => { + isContactTypeQualifier.returns(true); + getPage.resolves(pageData); + + const result = await Place.v1.getPage(dataContext)(placeTypeQualifier, cursor, stringifiedLimit); + + expect(result).to.equal(pageData); + expect(assertDataContext.calledOnceWithExactly(dataContext)).to.be.true; + expect(adapt.calledOnceWithExactly(dataContext, Local.Place.v1.getPage, Remote.Place.v1.getPage)).to.be.true; + expect(getPage.calledOnceWithExactly(placeTypeQualifier, cursor, limit)).to.be.true; + expect(isContactTypeQualifier.calledOnceWithExactly((placeTypeQualifier))).to.be.true; + }); + it('throws an error if the data context is invalid', () => { isContactTypeQualifier.returns(true); assertDataContext.throws(new Error(`Invalid data context [null].`)); diff --git a/shared-libs/cht-datasource/test/report.spec.ts b/shared-libs/cht-datasource/test/report.spec.ts index fbf6e466803..c1c81079fe7 100644 --- a/shared-libs/cht-datasource/test/report.spec.ts +++ b/shared-libs/cht-datasource/test/report.spec.ts @@ -80,6 +80,7 @@ describe('report', () => { const cursor = '1'; const pageData = { data: ids, cursor }; const limit = 3; + const stringifiedLimit = '3'; const freetextQualifier = { freetext: 'freetext'} as const; const invalidFreetextQualifier = { freetext: 'invalid_freetext'} as const; let getIdsPage: SinonStub; @@ -117,6 +118,21 @@ describe('report', () => { expect(isFreetextQualifier.calledOnceWithExactly(freetextQualifier)).to.be.true; }); + it('retrieves report ids from the data context when cursor is not null and ' + + 'limit is stringified number', async () => { + isFreetextQualifier.returns(true); + getIdsPage.resolves(pageData); + + // eslint-disable-next-line compat/compat + const result = await Report.v1.getIdsPage(dataContext)(freetextQualifier, cursor, stringifiedLimit); + + expect(result).to.equal(pageData); + expect(assertDataContext.calledOnceWithExactly(dataContext)).to.be.true; + expect(adapt.calledOnceWithExactly(dataContext, Local.Report.v1.getPage, Remote.Report.v1.getPage)).to.be.true; + expect(getIdsPage.calledOnceWithExactly(freetextQualifier, cursor, limit)).to.be.true; + expect(isFreetextQualifier.calledOnceWithExactly(freetextQualifier)).to.be.true; + }); + it('throws an error if the data context is invalid', () => { isFreetextQualifier.returns(true); assertDataContext.throws(new Error(`Invalid data context [null].`)); diff --git a/tests/integration/shared-libs/cht-datasource/contact.spec.js b/tests/integration/shared-libs/cht-datasource/contact.spec.js index 9f7fe2344f1..a3b1e2c8a66 100644 --- a/tests/integration/shared-libs/cht-datasource/contact.spec.js +++ b/tests/integration/shared-libs/cht-datasource/contact.spec.js @@ -152,6 +152,7 @@ describe('Contact', () => { const getIdsPage = Contact.v1.getIdsPage(dataContext); const fourLimit = 4; const twoLimit = 2; + const stringifiedLimit = '7'; const cursor = null; const invalidContactType = 'invalidPerson'; const freetext = 'contact'; @@ -212,6 +213,16 @@ describe('Contact', () => { expect(responseCursor).to.be.equal(null); }); + it('returns a page of people type contact ids ' + + 'when stringified limit and null cursor is passed', async () => { + const responsePage = await getIdsPage(Qualifier.byContactType(personType), null, stringifiedLimit); + const responsePeople = responsePage.data; + const responseCursor = responsePage.cursor; + + expect(responsePeople).excludingEvery([ '_rev', 'reported_date' ]).to.deep.equalInAnyOrder(expectedPeopleIds); + expect(responseCursor).to.be.equal('7'); + }); + it('returns a page of people type contact ids' + ' when limit and cursor is passed and cursor can be reused', async () => { const firstPage = await getIdsPage(Qualifier.byContactType(personType), cursor, fourLimit); diff --git a/tests/integration/shared-libs/cht-datasource/person.spec.js b/tests/integration/shared-libs/cht-datasource/person.spec.js index 39862623da4..50293d2868b 100644 --- a/tests/integration/shared-libs/cht-datasource/person.spec.js +++ b/tests/integration/shared-libs/cht-datasource/person.spec.js @@ -146,6 +146,7 @@ describe('Person', () => { describe('getPage', async () => { const getPage = Person.v1.getPage(dataContext); const limit = 4; + const stringifiedLimit = '7'; const cursor = null; const invalidContactType = 'invalidPerson'; @@ -158,6 +159,15 @@ describe('Person', () => { expect(responseCursor).to.be.equal(null); }); + it('returns a page of people for stringified limit and null cursor passed', async () => { + const responsePage = await getPage(Qualifier.byContactType(personType), null, stringifiedLimit); + const responsePeople = responsePage.data; + const responseCursor = responsePage.cursor; + + expect(responsePeople).excludingEvery([ '_rev', 'reported_date' ]).to.deep.equalInAnyOrder(expectedPeople); + expect(responseCursor).to.be.equal('7'); + }); + it('returns a page of people when limit and cursor is passed and cursor can be reused', async () => { const firstPage = await getPage(Qualifier.byContactType(personType), cursor, limit); const secondPage = await getPage(Qualifier.byContactType(personType), firstPage.cursor, limit); diff --git a/tests/integration/shared-libs/cht-datasource/place.spec.js b/tests/integration/shared-libs/cht-datasource/place.spec.js index 302ac6fc6d2..6d0afc0bc47 100644 --- a/tests/integration/shared-libs/cht-datasource/place.spec.js +++ b/tests/integration/shared-libs/cht-datasource/place.spec.js @@ -123,6 +123,7 @@ describe('Place', () => { describe('getPage', async () => { const getPage = Place.v1.getPage(dataContext); const limit = 2; + const stringifiedLimit = '3'; const cursor = null; const invalidContactType = 'invalidPlace'; @@ -136,6 +137,16 @@ describe('Place', () => { expect(responseCursor).to.be.equal(null); }); + it('returns a page of places for stringified limit and null cursor passed', async () => { + const responsePage = await getPage(Qualifier.byContactType(placeType), null, stringifiedLimit); + const responsePlaces = responsePage.data; + const responseCursor = responsePage.cursor; + + expect(responsePlaces).excludingEvery([ '_rev', 'reported_date' ]) + .to.deep.equalInAnyOrder(expectedPlaces); + expect(responseCursor).to.be.equal('3'); + }); + it('returns a page of places when limit and cursor is passed and cursor can be reused', async () => { const firstPage = await getPage(Qualifier.byContactType(placeType), cursor, limit); const secondPage = await getPage(Qualifier.byContactType(placeType), firstPage.cursor, limit); diff --git a/tests/integration/shared-libs/cht-datasource/report.spec.js b/tests/integration/shared-libs/cht-datasource/report.spec.js index 86f6e01d599..197c14a8159 100644 --- a/tests/integration/shared-libs/cht-datasource/report.spec.js +++ b/tests/integration/shared-libs/cht-datasource/report.spec.js @@ -118,6 +118,7 @@ describe('Report', () => { const getReport = Report.v1.getIdsPage(dataContext); const freetext = 'report'; const limit = 4; + const stringifiedLimit = '6'; const cursor = null; it('returns a page of report ids for no limit and cursor passed', async () => { @@ -129,6 +130,15 @@ describe('Report', () => { expect(responseCursor).to.be.equal(null); }); + it('returns a page of report ids for stringified limit and cursor passed', async () => { + const responsePage = await getReport(Qualifier.byFreetext(freetext), null, stringifiedLimit); + const responsePeople = responsePage.data; + const responseCursor = responsePage.cursor; + + expect(responsePeople).excludingEvery([ '_rev', 'reported_date' ]).to.deep.equalInAnyOrder(allReportsIds); + expect(responseCursor).to.be.equal('6'); + }); + it('returns a page of report ids when limit and cursor is passed and cursor can be reused', async () => { const firstPage = await getReport(Qualifier.byFreetext(freetext), cursor, limit); const secondPage = await getReport(Qualifier.byFreetext(freetext), firstPage.cursor, limit);