From ac0ab670561dca8ca4ce1968554bd42bc7f169dc Mon Sep 17 00:00:00 2001 From: Sugat Bajracharya Date: Wed, 6 Nov 2024 18:29:00 +0545 Subject: [PATCH 01/50] Initial setup --- shared-libs/cht-datasource/src/contact.ts | 92 +++++++++++++++++++ .../cht-datasource/src/libs/contact.ts | 22 ----- .../cht-datasource/src/local/libs/lineage.ts | 10 +- shared-libs/cht-datasource/src/local/place.ts | 4 +- shared-libs/cht-datasource/src/person.ts | 6 +- shared-libs/cht-datasource/src/place.ts | 10 +- shared-libs/cht-datasource/src/qualifier.ts | 28 ++++++ shared-libs/cht-datasource/src/reports.ts | 69 ++++++++++++++ .../cht-datasource/test/libs/contact.spec.ts | 4 +- 9 files changed, 206 insertions(+), 39 deletions(-) create mode 100644 shared-libs/cht-datasource/src/contact.ts delete mode 100644 shared-libs/cht-datasource/src/libs/contact.ts create mode 100644 shared-libs/cht-datasource/src/reports.ts diff --git a/shared-libs/cht-datasource/src/contact.ts b/shared-libs/cht-datasource/src/contact.ts new file mode 100644 index 00000000000..98d8ea4b68d --- /dev/null +++ b/shared-libs/cht-datasource/src/contact.ts @@ -0,0 +1,92 @@ +import {Doc} from './libs/doc'; +import {DataObject, Identifiable, isDataObject, isIdentifiable, Nullable, Page} from './libs/core'; +import {ContactTypeQualifier, FreetextQualifier, UuidQualifier} from './qualifier'; +import {DataContext} from './libs/data-context'; + +export namespace v1 { + /** @internal */ + export interface NormalizedParent extends DataObject, Identifiable { + readonly parent?: NormalizedParent; + } + + /** @internal */ + export const isNormalizedParent = (value: unknown): value is NormalizedParent => { + return isDataObject(value) && isIdentifiable(value) && (!value.parent || isNormalizedParent(value.parent)); + }; + + /** @internal */ + export interface Contact extends Doc, NormalizedParent { + readonly contact_type?: string; + readonly name?: string; + readonly reported_date?: Date; + readonly type: string; + } + + /** + * Returns a function for retrieving a contact from the given data context. + * @param context the current data context + * @returns a function for retrieving a contact + * @throws Error if a data context is not provided + */ + /** + * Returns a contact for the given qualifier. + * @param qualifier identifier for the contact to retrieve + * @returns the contact or `null` if no contact is found for the qualifier + * @throws Error if the qualifier is invalid + */ + export const get = (context: DataContext) => (qualifier: UuidQualifier) => Promise>; + + /** + * Returns a function for retrieving a contact from the given data context with the contact's parent lineage. + * @param context the current data context + * @returns a function for retrieving a contact with the contact's parent lineage + * @throws Error if a data context is not provided + */ + /** + * Returns a contact for the given qualifier with the contact's parent lineage. + * @param qualifier identifier for the contact to retrieve + * @returns the contact or `null` if no contact is found for the qualifier + * @throws Error if the qualifier is invalid + */ + const getWithLineage = (context: DataContext) => (qualifier: UuidQualifier) => Promise>; + + // New REST api: /api/v1/contact/id + /** + * Returns a function for retrieving a paged array of contact identifiers from the given data context. + * @param context the current data context + * @returns a function for retrieving a paged array of contact identifiers + * @throws Error if a data context is not provided + * @see {@link getIdsAll} which provides the same data, but without having to manually account for paging + */ + /** + * Returns an array of contact identifiers for the provided page specifications. + * @param qualifier the limiter defining which identifiers to return + * @param cursor the token identifying which page to retrieve. A `null` value indicates the first page should be + * returned. Subsequent pages can be retrieved by providing the cursor returned with the previous page. + * @param limit the maximum number of identifiers to return. Default is 10000. + * @returns a page of contact identifiers for the provided specification + * @throws Error if no qualifier is provided or if the qualifier is invalid + * @throws Error if the provided `limit` value is `<=0` + * @throws Error if the provided cursor is not a valid page token or `null` + */ + const getIdsPage = (context: DataContext) => ( + qualifier: ContactTypeQualifier | FreetextQualifier, + cursor: Nullable, limit: number + ) => Promise>; + + /** + * Returns a function for getting a generator that fetches contact identifiers from the given data context. + * @param context the current data context + * @returns a function for getting a generator that fetches contact identifiers + * @throws Error if a data context is not provided + */ + /** + * Returns a generator for fetching all contact identifiers that match the given qualifier + * @param qualifier the limiter defining which identifiers to return + * @returns a generator for fetching all contact identifiers that match the given qualifier + * @throws Error if no qualifier is provided or if the qualifier is invalid + */ + const getIdsAll = (context: DataContext) => ( + qualifier: ContactTypeQualifier | FreetextQualifier + ) => AsyncGenerator; +} diff --git a/shared-libs/cht-datasource/src/libs/contact.ts b/shared-libs/cht-datasource/src/libs/contact.ts deleted file mode 100644 index 63c0f505b93..00000000000 --- a/shared-libs/cht-datasource/src/libs/contact.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Doc } from './doc'; -import { DataObject, Identifiable, isDataObject, isIdentifiable } from './core'; - -/** @internal */ -export interface NormalizedParent extends DataObject, Identifiable { - readonly parent?: NormalizedParent; -} - -/** @internal */ -export const isNormalizedParent = (value: unknown): value is NormalizedParent => { - return isDataObject(value) - && isIdentifiable(value) - && (!value.parent || isNormalizedParent(value.parent)); -}; - -/** @internal */ -export interface Contact extends Doc, NormalizedParent { - readonly contact_type?: string; - readonly name?: string; - readonly reported_date?: Date; - readonly type: string; -} diff --git a/shared-libs/cht-datasource/src/local/libs/lineage.ts b/shared-libs/cht-datasource/src/local/libs/lineage.ts index 15b7c97cbaa..d53868d3a3f 100644 --- a/shared-libs/cht-datasource/src/local/libs/lineage.ts +++ b/shared-libs/cht-datasource/src/local/libs/lineage.ts @@ -1,4 +1,4 @@ -import { Contact, NormalizedParent } from '../../libs/contact'; +import * as Contact from '../../contact'; import { DataObject, findById, @@ -47,7 +47,7 @@ export const hydratePrimaryContact = (contacts: Doc[]) => (place: Nullable) }; }; -const getParentUuid = (index: number, contact?: NormalizedParent): Nullable => { +const getParentUuid = (index: number, contact?: Contact.v1.NormalizedParent): Nullable => { if (!contact) { return null; } @@ -71,9 +71,9 @@ const mergeLineage = (lineage: DataObject[], parent: DataObject): DataObject => /** @internal */ export const hydrateLineage = ( - contact: Contact, + contact: Contact.v1.Contact, lineage: Nullable[] -): Contact => { +): Contact.v1.Contact => { const fullLineage = lineage .map((place, index) => { if (place) { @@ -87,5 +87,5 @@ export const hydrateLineage = ( return { _id: parentId }; }); const hierarchy: NonEmptyArray = [contact, ...fullLineage]; - return mergeLineage(hierarchy.slice(0, -1), getLastElement(hierarchy)) as Contact; + return mergeLineage(hierarchy.slice(0, -1), getLastElement(hierarchy)) as Contact.v1.Contact; }; diff --git a/shared-libs/cht-datasource/src/local/place.ts b/shared-libs/cht-datasource/src/local/place.ts index 0c2663a04c7..b356cab1023 100644 --- a/shared-libs/cht-datasource/src/local/place.ts +++ b/shared-libs/cht-datasource/src/local/place.ts @@ -5,7 +5,7 @@ import { ContactTypeQualifier, UuidQualifier } from '../qualifier'; import * as Place from '../place'; import {fetchAndFilter, getDocById, getDocsByIds, queryDocsByKey} from './libs/doc'; import { LocalDataContext, SettingsService } from './libs/data-context'; -import { Contact } from '../libs/contact'; +import * as Contact from '../contact'; import logger from '@medic/logger'; import { getLineageDocsById, getPrimaryContactIds, hydrateLineage, hydratePrimaryContact } from './libs/lineage'; import { InvalidArgumentError } from '../libs/error'; @@ -56,7 +56,7 @@ export namespace v1 { const contactUuids = getPrimaryContactIds(places); const contacts = await getMedicDocsById(contactUuids); const [placeWithContact, ...linagePlacesWithContact] = places.map(hydratePrimaryContact(contacts)); - const placeWithLineage = hydrateLineage(placeWithContact as Contact, linagePlacesWithContact); + const placeWithLineage = hydrateLineage(placeWithContact as Contact.v1.Contact, linagePlacesWithContact); return deepCopy(placeWithLineage); }; }; diff --git a/shared-libs/cht-datasource/src/person.ts b/shared-libs/cht-datasource/src/person.ts index 8d20f85c887..88a58cc6dad 100644 --- a/shared-libs/cht-datasource/src/person.ts +++ b/shared-libs/cht-datasource/src/person.ts @@ -1,6 +1,6 @@ import { ContactTypeQualifier, isUuidQualifier, UuidQualifier } from './qualifier'; import { adapt, assertDataContext, DataContext } from './libs/data-context'; -import { Contact, NormalizedParent } from './libs/contact'; +import * as Contact from './contact'; import * as Remote from './remote'; import * as Local from './local'; import * as Place from './place'; @@ -14,7 +14,7 @@ export namespace v1 { /** * Immutable data about a person contact. */ - export interface Person extends Contact { + export interface Person extends Contact.v1.Contact { readonly date_of_birth?: Date; readonly phone?: string; readonly patient_id?: string; @@ -25,7 +25,7 @@ export namespace v1 { * Immutable data about a person contact, including the full records of the parent place lineage. */ export interface PersonWithLineage extends Person { - readonly parent?: Place.v1.PlaceWithLineage | NormalizedParent, + readonly parent?: Place.v1.PlaceWithLineage | Contact.v1.NormalizedParent, } const assertPersonQualifier: (qualifier: unknown) => asserts qualifier is UuidQualifier = (qualifier: unknown) => { diff --git a/shared-libs/cht-datasource/src/place.ts b/shared-libs/cht-datasource/src/place.ts index 84c78d98de4..01da89ea147 100644 --- a/shared-libs/cht-datasource/src/place.ts +++ b/shared-libs/cht-datasource/src/place.ts @@ -1,4 +1,4 @@ -import { Contact, NormalizedParent } from './libs/contact'; +import * as Contact from './contact'; import * as Person from './person'; import { LocalDataContext} from './local/libs/data-context'; import {ContactTypeQualifier, isUuidQualifier, UuidQualifier} from './qualifier'; @@ -14,8 +14,8 @@ export namespace v1 { /** * Immutable data about a place contact. */ - export interface Place extends Contact { - readonly contact?: NormalizedParent, + export interface Place extends Contact.v1.Contact { + readonly contact?: Contact.v1.NormalizedParent, readonly place_id?: string, } @@ -24,8 +24,8 @@ export namespace v1 { * contact for the place. */ export interface PlaceWithLineage extends Place { - readonly contact?: Person.v1.PersonWithLineage | NormalizedParent, - readonly parent?: PlaceWithLineage | NormalizedParent, + readonly contact?: Person.v1.PersonWithLineage | Contact.v1.NormalizedParent, + readonly parent?: PlaceWithLineage | Contact.v1.NormalizedParent, } const assertPlaceQualifier: (qualifier: unknown) => asserts qualifier is UuidQualifier = (qualifier: unknown) => { diff --git a/shared-libs/cht-datasource/src/qualifier.ts b/shared-libs/cht-datasource/src/qualifier.ts index 67c6ac67b1a..0e76dbcd637 100644 --- a/shared-libs/cht-datasource/src/qualifier.ts +++ b/shared-libs/cht-datasource/src/qualifier.ts @@ -56,3 +56,31 @@ export const byContactType = (contactType: string): ContactTypeQualifier => { export const isContactTypeQualifier = (contactType: unknown): contactType is ContactTypeQualifier => { return isRecord(contactType) && hasField(contactType, { name: 'contactType', type: 'string' }); }; + +/** + * A qualifier that identifies entities based on a freetext search string. + */ +export type FreetextQualifier = Readonly<{ freetext: string }>; + +/** + * Builds a qualifier for finding entities by the given freetext string. + * @param freetext the text to search with + * @returns the qualifier + * @throws Error if the search string is not provided or is empty + */ +export const byFreetext = (freetext: string): FreetextQualifier => { + if (!isString(freetext) || freetext.length === 0) { + throw new InvalidArgumentError(`Invalid freetext [${JSON.stringify(freetext)}].`); + } + + return { freetext }; +}; + +/** + * Returns `true` if the given qualifier is a {@link FreetextQualifier} otherwise `false`. + * @param qualifier the qualifier to check + * @returns `true` if the given type is a {@link FreetextQualifier}, otherwise `false`. + */ +export const isFreetextQualifier = (qualifier: unknown): qualifier is FreetextQualifier => { + return isRecord(qualifier); +}; diff --git a/shared-libs/cht-datasource/src/reports.ts b/shared-libs/cht-datasource/src/reports.ts new file mode 100644 index 00000000000..2ea0140e4b5 --- /dev/null +++ b/shared-libs/cht-datasource/src/reports.ts @@ -0,0 +1,69 @@ +import {Doc} from './libs/doc'; +import {DataObject, Nullable, Page} from './libs/core'; +import {DataContext} from './libs/data-context'; +import {FreetextQualifier, UuidQualifier} from './qualifier'; + +export namespace v1 { + /** + * A report document. + */ + interface Report extends Doc { + readonly form: string; + readonly reported_date: Date; + readonly fields: DataObject; + } + + // New REST api: /api/v1/report + /** + * Returns a function for retrieving a report from the given data context. + * @param context the current data context + * @returns a function for retrieving a report + * @throws Error if a data context is not provided + */ + /** + * Returns a report for the given qualifier. + * @param qualifier identifier for the report to retrieve + * @returns the report or `null` if no report is found for the qualifier + * @throws Error if the qualifier is invalid + */ + const get = (context: DataContext) => (qualifier: UuidQualifier) => Promise>; + + // New REST api: /api/v1/report/id + /** + * Returns a function for retrieving a paged array of report identifiers from the given data context. + * @param context the current data context + * @returns a function for retrieving a paged array of report identifiers + * @throws Error if a data context is not provided + * @see {@link getIdsAll} which provides the same data, but without having to manually account for paging + */ + /** + * Returns an array of report identifiers for the provided page specifications. + * @param qualifier the limiter defining which identifiers to return + * @param cursor the token identifying which page to retrieve. A `null` value indicates the first page should be + * returned. Subsequent pages can be retrieved by providing the cursor returned with the previous page. + * @param limit the maximum number of identifiers to return. Default is 10000. + * @returns a page of report identifiers for the provided specification + * @throws Error if no qualifier is provided or if the qualifier is invalid + * @throws Error if the provided `limit` value is `<=0` + * @throws Error if the provided cursor is not a valid page token or `null` + */ + const getIdsPage = (context: DataContext) => ( + qualifier: FreetextQualifier, + cursor: Nullable, + limit: number + ) => Promise>; + + /** + * Returns a function for getting a generator that fetches report identifiers from the given data context. + * @param context the current data context + * @returns a function for getting a generator that fetches report identifiers + * @throws Error if a data context is not provided + */ + /** + * Returns a generator for fetching all report identifiers that match the given qualifier + * @param qualifier the limiter defining which identifiers to return + * @returns a generator for fetching all report identifiers that match the given qualifier + * @throws Error if no qualifier is provided or if the qualifier is invalid + */ + const getIdsAll = (context: DataContext) => (qualifier: FreetextQualifier) => AsyncGenerator; +} diff --git a/shared-libs/cht-datasource/test/libs/contact.spec.ts b/shared-libs/cht-datasource/test/libs/contact.spec.ts index dadb7d71ca0..e8978b3c1d7 100644 --- a/shared-libs/cht-datasource/test/libs/contact.spec.ts +++ b/shared-libs/cht-datasource/test/libs/contact.spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { isNormalizedParent } from '../../src/libs/contact'; +import * as Contact from '../../src/contact'; import sinon, { SinonStub } from 'sinon'; import * as Core from '../../src/libs/core'; @@ -24,7 +24,7 @@ describe('contact lib', () => { ] as [unknown, boolean, boolean][]).forEach(([value, dataObj, expected]) => { it(`evaluates ${JSON.stringify(value)}`, () => { isDataObject.returns(dataObj); - expect(isNormalizedParent(value)).to.equal(expected); + expect(Contact.v1.isNormalizedParent(value)).to.equal(expected); }); }); }); From cd11adc3a6089cf6347980dd26556687c03fa289 Mon Sep 17 00:00:00 2001 From: Sugat Bajracharya Date: Thu, 7 Nov 2024 13:05:32 +0545 Subject: [PATCH 02/50] add /api/v1/contacts/:uuid endpoint --- api/src/routing.js | 3 ++ shared-libs/cht-datasource/src/contact.ts | 54 +++++++++++++++---- shared-libs/cht-datasource/src/index.ts | 5 +- shared-libs/cht-datasource/src/local/index.ts | 1 + shared-libs/cht-datasource/src/person.ts | 21 ++++---- shared-libs/cht-datasource/src/place.ts | 32 +++++------ .../cht-datasource/src/remote/index.ts | 1 + shared-libs/cht-datasource/src/reports.ts | 20 ++++--- 8 files changed, 83 insertions(+), 54 deletions(-) diff --git a/api/src/routing.js b/api/src/routing.js index f027f1ddb03..613d286e45e 100644 --- a/api/src/routing.js +++ b/api/src/routing.js @@ -37,6 +37,7 @@ const exportData = require('./controllers/export-data'); const records = require('./controllers/records'); const forms = require('./controllers/forms'); const users = require('./controllers/users'); +const contact = require('./controllers/contact'); const person = require('./controllers/person'); const place = require('./controllers/place'); const { people, places } = require('@medic/contacts')(config, db, dataContext); @@ -485,6 +486,8 @@ app.postJson('/api/v1/people', function(req, res) { app.get('/api/v1/person', person.v1.getAll); app.get('/api/v1/person/:uuid', person.v1.get); +app.get('/api/v1/contact/:uuid', contact.v1.get); + app.postJson('/api/v1/bulk-delete', bulkDocs.bulkDelete); // offline users are not allowed to hydrate documents via the hydrate API diff --git a/shared-libs/cht-datasource/src/contact.ts b/shared-libs/cht-datasource/src/contact.ts index 98d8ea4b68d..adcfa7559d5 100644 --- a/shared-libs/cht-datasource/src/contact.ts +++ b/shared-libs/cht-datasource/src/contact.ts @@ -1,7 +1,12 @@ import {Doc} from './libs/doc'; import {DataObject, Identifiable, isDataObject, isIdentifiable, Nullable, Page} from './libs/core'; -import {ContactTypeQualifier, FreetextQualifier, UuidQualifier} from './qualifier'; -import {DataContext} from './libs/data-context'; +import {ContactTypeQualifier, FreetextQualifier, isUuidQualifier, UuidQualifier} from './qualifier'; +import {adapt, assertDataContext, DataContext} from './libs/data-context'; +import {LocalDataContext} from './local/libs/data-context'; +import {RemoteDataContext} from './remote/libs/data-context'; +import {InvalidArgumentError} from './libs/error'; +import * as Local from './local'; +import * as Remote from './remote'; export namespace v1 { /** @internal */ @@ -22,6 +27,33 @@ export namespace v1 { readonly type: string; } + /** + * + */ + const assertContactQualifier: (qualifier: unknown) => asserts qualifier is UuidQualifier = (qualifier: unknown) => { + if (!isUuidQualifier(qualifier)) { + throw new InvalidArgumentError(`Invalid identifier [${JSON.stringify(qualifier)}].`); + } + }; + + /** + * + */ + const getContact = ( + localFn: (c: LocalDataContext) => (qualifier: UuidQualifier) => Promise, + remoteFn: (c: RemoteDataContext) => (qualifier: UuidQualifier) => Promise + ) => (context: DataContext) => { + assertDataContext(context); + /** + * + */ + const fn = adapt(context, localFn, remoteFn); + return async (qualifier: UuidQualifier): Promise => { + assertContactQualifier(qualifier); + return fn(qualifier); + }; + }; + /** * Returns a function for retrieving a contact from the given data context. * @param context the current data context @@ -34,7 +66,7 @@ export namespace v1 { * @returns the contact or `null` if no contact is found for the qualifier * @throws Error if the qualifier is invalid */ - export const get = (context: DataContext) => (qualifier: UuidQualifier) => Promise>; + export const get = getContact(Local.Contact.v1.get, Remote.Contact.v1.get); /** * Returns a function for retrieving a contact from the given data context with the contact's parent lineage. @@ -48,7 +80,7 @@ export namespace v1 { * @returns the contact or `null` if no contact is found for the qualifier * @throws Error if the qualifier is invalid */ - const getWithLineage = (context: DataContext) => (qualifier: UuidQualifier) => Promise>; + // const getWithLineage = (context: DataContext) => (qualifier: UuidQualifier) => Promise>; // New REST api: /api/v1/contact/id /** @@ -69,10 +101,10 @@ export namespace v1 { * @throws Error if the provided `limit` value is `<=0` * @throws Error if the provided cursor is not a valid page token or `null` */ - const getIdsPage = (context: DataContext) => ( - qualifier: ContactTypeQualifier | FreetextQualifier, - cursor: Nullable, limit: number - ) => Promise>; + // const getIdsPage = (context: DataContext) => ( + // qualifier: ContactTypeQualifier | FreetextQualifier, + // cursor: Nullable, limit: number + // ) => Promise>; /** * Returns a function for getting a generator that fetches contact identifiers from the given data context. @@ -86,7 +118,7 @@ export namespace v1 { * @returns a generator for fetching all contact identifiers that match the given qualifier * @throws Error if no qualifier is provided or if the qualifier is invalid */ - const getIdsAll = (context: DataContext) => ( - qualifier: ContactTypeQualifier | FreetextQualifier - ) => AsyncGenerator; + // const getIdsAll = (context: DataContext) => ( + // qualifier: ContactTypeQualifier | FreetextQualifier + // ) => AsyncGenerator; } diff --git a/shared-libs/cht-datasource/src/index.ts b/shared-libs/cht-datasource/src/index.ts index 394b23e9bae..6274242c7e6 100644 --- a/shared-libs/cht-datasource/src/index.ts +++ b/shared-libs/cht-datasource/src/index.ts @@ -38,6 +38,7 @@ export { DataContext } from './libs/data-context'; export { getLocalDataContext } from './local'; export { getRemoteDataContext } from './remote'; export { InvalidArgumentError } from './libs/error'; +export * as Contact from './contact'; export * as Person from './person'; export * as Place from './place'; export * as Qualifier from './qualifier'; @@ -143,7 +144,7 @@ export const getDatasource = (ctx: DataContext) => { * @throws InvalidArgumentError if no type is provided or if the type is not for a person */ getByType: (personType: string) => ctx.bind(Person.v1.getAll)(Qualifier.byContactType(personType)), - } - } + }, + }, }; }; diff --git a/shared-libs/cht-datasource/src/local/index.ts b/shared-libs/cht-datasource/src/local/index.ts index f6fc2ffe2f6..81a9e76eb71 100644 --- a/shared-libs/cht-datasource/src/local/index.ts +++ b/shared-libs/cht-datasource/src/local/index.ts @@ -1,3 +1,4 @@ +export * as Contact from './contact'; export * as Person from './person'; export * as Place from './place'; export { getLocalDataContext } from './libs/data-context'; diff --git a/shared-libs/cht-datasource/src/person.ts b/shared-libs/cht-datasource/src/person.ts index 88a58cc6dad..789b7704c44 100644 --- a/shared-libs/cht-datasource/src/person.ts +++ b/shared-libs/cht-datasource/src/person.ts @@ -25,7 +25,7 @@ export namespace v1 { * Immutable data about a person contact, including the full records of the parent place lineage. */ export interface PersonWithLineage extends Person { - readonly parent?: Place.v1.PlaceWithLineage | Contact.v1.NormalizedParent, + readonly parent?: Place.v1.PlaceWithLineage | Contact.v1.NormalizedParent; } const assertPersonQualifier: (qualifier: unknown) => asserts qualifier is UuidQualifier = (qualifier: unknown) => { @@ -34,10 +34,11 @@ export namespace v1 { } }; - const getPerson = ( - localFn: (c: LocalDataContext) => (qualifier: UuidQualifier) => Promise, - remoteFn: (c: RemoteDataContext) => (qualifier: UuidQualifier) => Promise - ) => (context: DataContext) => { + const getPerson = + ( + localFn: (c: LocalDataContext) => (qualifier: UuidQualifier) => Promise, + remoteFn: (c: RemoteDataContext) => (qualifier: UuidQualifier) => Promise + ) => (context: DataContext) => { assertDataContext(context); const fn = adapt(context, localFn, remoteFn); return async (qualifier: UuidQualifier): Promise => { @@ -69,9 +70,7 @@ export namespace v1 { * @throws Error if a data context is not provided * @see {@link getAll} which provides the same data, but without having to manually account for paging */ - export const getPage = ( - context: DataContext - ): typeof curriedFn => { + export const getPage = (context: DataContext): typeof curriedFn => { assertDataContext(context); const fn = adapt(context, Local.Person.v1.getPage, Remote.Person.v1.getPage); @@ -89,7 +88,7 @@ export namespace v1 { const curriedFn = async ( personType: ContactTypeQualifier, cursor: Nullable = null, - limit = 100, + limit = 100 ): Promise> => { assertTypeQualifier(personType); assertCursor(cursor); @@ -106,9 +105,7 @@ export namespace v1 { * @returns a function for getting a generator that fetches people * @throws Error if a data context is not provided */ - export const getAll = ( - context: DataContext - ): typeof curriedGen => { + export const getAll = (context: DataContext): typeof curriedGen => { assertDataContext(context); const getPage = context.bind(v1.getPage); diff --git a/shared-libs/cht-datasource/src/place.ts b/shared-libs/cht-datasource/src/place.ts index 01da89ea147..8f55ee6e4cc 100644 --- a/shared-libs/cht-datasource/src/place.ts +++ b/shared-libs/cht-datasource/src/place.ts @@ -1,22 +1,21 @@ import * as Contact from './contact'; import * as Person from './person'; -import { LocalDataContext} from './local/libs/data-context'; -import {ContactTypeQualifier, isUuidQualifier, UuidQualifier} from './qualifier'; +import { LocalDataContext } from './local/libs/data-context'; +import { ContactTypeQualifier, isUuidQualifier, UuidQualifier } from './qualifier'; import { RemoteDataContext } from './remote/libs/data-context'; import { adapt, assertDataContext, DataContext } from './libs/data-context'; import * as Local from './local'; import * as Remote from './remote'; -import {assertCursor, assertLimit, assertTypeQualifier, getPagedGenerator, Nullable, Page} from './libs/core'; +import { assertCursor, assertLimit, assertTypeQualifier, getPagedGenerator, Nullable, Page } from './libs/core'; /** */ export namespace v1 { - /** * Immutable data about a place contact. */ export interface Place extends Contact.v1.Contact { - readonly contact?: Contact.v1.NormalizedParent, - readonly place_id?: string, + readonly contact?: Contact.v1.NormalizedParent; + readonly place_id?: string; } /** @@ -24,8 +23,8 @@ export namespace v1 { * contact for the place. */ export interface PlaceWithLineage extends Place { - readonly contact?: Person.v1.PersonWithLineage | Contact.v1.NormalizedParent, - readonly parent?: PlaceWithLineage | Contact.v1.NormalizedParent, + readonly contact?: Person.v1.PersonWithLineage | Contact.v1.NormalizedParent; + readonly parent?: PlaceWithLineage | Contact.v1.NormalizedParent; } const assertPlaceQualifier: (qualifier: unknown) => asserts qualifier is UuidQualifier = (qualifier: unknown) => { @@ -34,10 +33,11 @@ export namespace v1 { } }; - const getPlace = ( - localFn: (c: LocalDataContext) => (qualifier: UuidQualifier) => Promise, - remoteFn: (c: RemoteDataContext) => (qualifier: UuidQualifier) => Promise - ) => (context: DataContext) => { + const getPlace = + ( + localFn: (c: LocalDataContext) => (qualifier: UuidQualifier) => Promise, + remoteFn: (c: RemoteDataContext) => (qualifier: UuidQualifier) => Promise + ) => (context: DataContext) => { assertDataContext(context); const fn = adapt(context, localFn, remoteFn); return async (qualifier: UuidQualifier): Promise => { @@ -69,9 +69,7 @@ export namespace v1 { * @throws Error if a data context is not provided * @see {@link getAll} which provides the same data, but without having to manually account for paging */ - export const getPage = ( - context: DataContext - ): typeof curriedFn => { + export const getPage = (context: DataContext): typeof curriedFn => { assertDataContext(context); const fn = adapt(context, Local.Place.v1.getPage, Remote.Place.v1.getPage); @@ -106,9 +104,7 @@ export namespace v1 { * @returns a function for getting a generator that fetches places * @throws Error if a data context is not provided */ - export const getAll = ( - context: DataContext - ): typeof curriedGen => { + export const getAll = (context: DataContext): typeof curriedGen => { assertDataContext(context); const getPage = context.bind(v1.getPage); diff --git a/shared-libs/cht-datasource/src/remote/index.ts b/shared-libs/cht-datasource/src/remote/index.ts index 6db881ec27e..e1a562530cf 100644 --- a/shared-libs/cht-datasource/src/remote/index.ts +++ b/shared-libs/cht-datasource/src/remote/index.ts @@ -1,3 +1,4 @@ +export * as Contact from './contact'; export * as Person from './person'; export * as Place from './place'; export { getRemoteDataContext } from './libs/data-context'; diff --git a/shared-libs/cht-datasource/src/reports.ts b/shared-libs/cht-datasource/src/reports.ts index 2ea0140e4b5..7be0c364a19 100644 --- a/shared-libs/cht-datasource/src/reports.ts +++ b/shared-libs/cht-datasource/src/reports.ts @@ -1,7 +1,7 @@ -import {Doc} from './libs/doc'; -import {DataObject, Nullable, Page} from './libs/core'; -import {DataContext} from './libs/data-context'; -import {FreetextQualifier, UuidQualifier} from './qualifier'; +import { Doc } from './libs/doc'; +import { DataObject, Nullable, Page } from './libs/core'; +import { DataContext } from './libs/data-context'; +import { FreetextQualifier, UuidQualifier } from './qualifier'; export namespace v1 { /** @@ -26,7 +26,7 @@ export namespace v1 { * @returns the report or `null` if no report is found for the qualifier * @throws Error if the qualifier is invalid */ - const get = (context: DataContext) => (qualifier: UuidQualifier) => Promise>; + // const get = (context: DataContext) => (qualifier: UuidQualifier) => Promise>; // New REST api: /api/v1/report/id /** @@ -47,11 +47,9 @@ export namespace v1 { * @throws Error if the provided `limit` value is `<=0` * @throws Error if the provided cursor is not a valid page token or `null` */ - const getIdsPage = (context: DataContext) => ( - qualifier: FreetextQualifier, - cursor: Nullable, - limit: number - ) => Promise>; + // const getIdsPage = + // (context: DataContext) => (qualifier: FreetextQualifier, cursor: Nullable, limit: number) => + // Promise>; /** * Returns a function for getting a generator that fetches report identifiers from the given data context. @@ -65,5 +63,5 @@ export namespace v1 { * @returns a generator for fetching all report identifiers that match the given qualifier * @throws Error if no qualifier is provided or if the qualifier is invalid */ - const getIdsAll = (context: DataContext) => (qualifier: FreetextQualifier) => AsyncGenerator; + // const getIdsAll = (context: DataContext) => (qualifier: FreetextQualifier) => AsyncGenerator; } From b7c5d1f6e69d0726895326b6a102c7b8094d3852 Mon Sep 17 00:00:00 2001 From: Sugat Bajracharya Date: Thu, 7 Nov 2024 18:21:33 +0545 Subject: [PATCH 03/50] add /api/v1/contact/:uuid?with_lineage=