Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
08e2b27
feat(#9237): Add functionality of getting people with pagination in c…
sugat009 Jul 15, 2024
ab92f0b
Update shared-libs/cht-datasource/src/local/libs/doc.ts
sugat009 Jul 17, 2024
de63197
feat(#9237): Address PR comments
sugat009 Jul 19, 2024
105d5db
Update shared-libs/cht-datasource/src/qualifier.ts
sugat009 Jul 23, 2024
2b92b4c
Update shared-libs/cht-datasource/test/local/person.spec.ts
sugat009 Jul 23, 2024
ba2e27a
Update shared-libs/cht-datasource/src/person.ts
sugat009 Jul 23, 2024
15068fa
Update shared-libs/cht-datasource/src/remote/person.ts
sugat009 Jul 23, 2024
a83a259
Update shared-libs/cht-datasource/src/person.ts
sugat009 Jul 23, 2024
29a3ad5
Update shared-libs/cht-datasource/src/index.ts
sugat009 Jul 23, 2024
8fbc245
feat(#9237): Address PR comments
sugat009 Jul 23, 2024
a99de72
feat(#9237): Add unit tests for getResources in remote mode
sugat009 Jul 23, 2024
d87c258
feat(#9237): Address PR comments
sugat009 Jul 24, 2024
eceeb75
(#feat): Minor fix
sugat009 Jul 24, 2024
1aab0e3
Update shared-libs/cht-datasource/src/person.ts
sugat009 Jul 26, 2024
54d04cb
Update shared-libs/cht-datasource/test/local/libs/lineage.spec.ts
sugat009 Jul 26, 2024
dcc3758
Update shared-libs/cht-datasource/test/local/libs/lineage.spec.ts
sugat009 Jul 26, 2024
e800b1a
Update shared-libs/cht-datasource/test/local/person.spec.ts
sugat009 Jul 26, 2024
34b466c
feat(#9237): Address PR comments
sugat009 Jul 26, 2024
4facbad
Implement cursor based pagination
sugat009 Aug 6, 2024
33d13fd
Fix unit tests according to implementation of cursor pagination
sugat009 Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions shared-libs/cht-datasource/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ export const getDatasource = (ctx: DataContext) => {
* @throws Error if no UUID is provided
*/
getByUuidWithLineage: (uuid: string) => ctx.bind(Person.v1.getWithLineage)(Qualifier.byUuid(uuid)),

/**
* Returns a list of people.
* @param limit the total number of records to retrieve
* @param skip the total number of records to skip
* @returns array of `Person`
*/
Comment thread
sugat009 marked this conversation as resolved.
getPage: (limit = 100, skip = 0) => ctx.bind(Person.v1.getPage)(limit, skip),
Comment thread
jkuester marked this conversation as resolved.
Outdated
}
}
};
Expand Down
27 changes: 19 additions & 8 deletions shared-libs/cht-datasource/src/local/libs/doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,25 @@ export const getDocsByIds = (db: PouchDB.Database<Doc>) => async (uuids: string[
};

/** @internal */
Comment thread
jkuester marked this conversation as resolved.
Outdated
export const queryDocsByKey = (
const queryDocs = (db: PouchDB.Database<Doc>, view: string, options: PouchDB.Query.Options<Doc, unknown>) => db
.query(view, {...options})
.then(({ rows }) => rows.map(({ doc }) => isDoc(doc) ? doc : null));

/** @internal */
export const queryDocsByRange = (
db: PouchDB.Database<Doc>,
view: string
) => async (key: string): Promise<Nullable<Doc>[]> => db
.query(view, {
startkey: [key],
endkey: [key, {}],
include_docs: true
})
.then(({ rows }) => rows.map(({ doc }) => isDoc(doc) ? doc : null));
) => async (
startkey: unknown,
endkey: unknown
): Promise<Nullable<Doc>[]> => queryDocs(db, view, { include_docs: true, startkey: [startkey], endkey: [endkey, {}]});
Comment thread
jkuester marked this conversation as resolved.
Outdated

/** @internal */
export const queryDocsByKey = (
db: PouchDB.Database<Doc>,
view: string
) => async (
key: unknown,
limit: number,
skip: number
): Promise<Nullable<Doc>[]> => queryDocs(db, view, { key: [key], include_docs: true, limit, skip });
Comment thread
sugat009 marked this conversation as resolved.
Outdated
7 changes: 5 additions & 2 deletions shared-libs/cht-datasource/src/local/libs/lineage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Nullable
} from '../../libs/core';
import { Doc } from '../../libs/doc';
import { queryDocsByKey } from './doc';
import { queryDocsByRange } from './doc';
import logger from '@medic/logger';

/**
Expand All @@ -20,7 +20,10 @@ import logger from '@medic/logger';
*/
export const getLineageDocsById = (
medicDb: PouchDB.Database<Doc>
): (id: string) => Promise<Nullable<Doc>[]> => queryDocsByKey(medicDb, 'medic-client/docs_by_id_lineage');
): (
startkey: unknown,
endkey: unknown
) => Promise<Nullable<Doc>[]> => queryDocsByRange(medicDb, 'medic-client/docs_by_id_lineage');
Comment thread
sugat009 marked this conversation as resolved.
Outdated

/** @internal */
export const getPrimaryContactIds = (places: NonEmptyArray<Nullable<Doc>>): string[] => places
Expand Down
22 changes: 20 additions & 2 deletions shared-libs/cht-datasource/src/local/person.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import contactTypeUtils from '@medic/contact-types-utils';
import { deepCopy, isNonEmptyArray, Nullable } from '../libs/core';
import { UuidQualifier } from '../qualifier';
import * as Person from '../person';
import { getDocById, getDocsByIds } from './libs/doc';
import { getDocById, getDocsByIds, queryDocsByKey } from './libs/doc';
import { LocalDataContext, SettingsService } from './libs/data-context';
import logger from '@medic/logger';
import { getLineageDocsById, getPrimaryContactIds, hydrateLineage, hydratePrimaryContact } from './libs/lineage';
Expand Down Expand Up @@ -40,7 +40,7 @@ export namespace v1 {
const getLineageDocs = getLineageDocsById(medicDb);
const getMedicDocsById = getDocsByIds(medicDb);
return async (identifier: UuidQualifier): Promise<Nullable<Person.v1.PersonWithLineage>> => {
const [person, ...lineagePlaces] = await getLineageDocs(identifier.uuid);
const [person, ...lineagePlaces] = await getLineageDocs(identifier.uuid, identifier.uuid);
if (!isPerson(settings, identifier.uuid, person)) {
return null;
}
Expand All @@ -58,4 +58,22 @@ export namespace v1 {
return deepCopy(personWithLineage);
};
};

/** @internal */
export const getPage = ({ medicDb, settings }: LocalDataContext) => {
const personIdentifierRecord = contactTypeUtils.getPersonTypes(settings.getAll());

let personIdentifier: string;
if (Object.entries(personIdentifierRecord).length > 0) {
personIdentifier = personIdentifierRecord[0]?.id as string;
} else {
throw new Error('Person type not found');
}
Comment thread
sugat009 marked this conversation as resolved.
Outdated

const getDocsByPage = queryDocsByKey(medicDb, 'medic-client/contacts_by_type');

return async (limit: number, skip: number): Promise<Nullable<Doc>[]> => {
Comment thread
sugat009 marked this conversation as resolved.
Outdated
return await getDocsByPage(personIdentifier, limit, skip);
};
};
}
2 changes: 1 addition & 1 deletion shared-libs/cht-datasource/src/local/place.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export namespace v1 {
const getLineageDocs = getLineageDocsById(medicDb);
const getMedicDocsById = getDocsByIds(medicDb);
return async (identifier: UuidQualifier): Promise<Nullable<Place.v1.PlaceWithLineage>> => {
const [place, ...lineagePlaces] = await getLineageDocs(identifier.uuid);
const [place, ...lineagePlaces] = await getLineageDocs(identifier.uuid, identifier.uuid);
if (!isPlace(settings, identifier.uuid, place)) {
return null;
}
Expand Down
19 changes: 19 additions & 0 deletions shared-libs/cht-datasource/src/person.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ export namespace v1 {
};
};

const getPeople = <T>(
Comment thread
sugat009 marked this conversation as resolved.
Outdated
localFn: (c: LocalDataContext) => (limit: number, skip: number) => Promise<T>,
remoteFn: (c: RemoteDataContext) => (limit: number, skip: number) => Promise<T>
) => (context: DataContext) => {
assertDataContext(context);
const fn = adapt(context, localFn, remoteFn);

return async (limit = 100, skip = 0): Promise<T> => {
Comment thread
sugat009 marked this conversation as resolved.
Outdated
return fn(limit, skip);
};
Comment thread
jkuester marked this conversation as resolved.
Outdated
};

/**
* Returns a person for the given qualifier.
* @param context the current data context
Expand All @@ -59,4 +71,11 @@ export namespace v1 {
* @throws Error if the provided context or qualifier is invalid
*/
export const getWithLineage = getPerson(Local.Person.v1.getWithLineage, Remote.Person.v1.getWithLineage);

/**
* Returns an array of people.
* @param context the current data context
* @returns an array of people
*/
Comment thread
sugat009 marked this conversation as resolved.
export const getPage = getPeople(Local.Person.v1.getPage, Remote.Person.v1.getPage);
}
9 changes: 9 additions & 0 deletions shared-libs/cht-datasource/src/remote/person.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@ export namespace v1 {
identifier.uuid,
{ with_lineage: 'true' }
);

/** @internal */
export const getPage = (remoteContext: RemoteDataContext) => (
limit: number,
skip: number
): Promise<null> => getPerson(remoteContext)(
Comment thread
sugat009 marked this conversation as resolved.
Outdated
'',
{'limit': limit.toString(), 'skip': skip.toString()}
);
}
17 changes: 16 additions & 1 deletion shared-libs/cht-datasource/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as Qualifier from '../src/qualifier';
import sinon, { SinonStub } from 'sinon';
import * as Context from '../src/libs/data-context';
import { DataContext } from '../src';
import { Doc } from '../src/libs/doc';

describe('CHT Script API - getDatasource', () => {
let dataContext: DataContext;
Expand Down Expand Up @@ -92,7 +93,7 @@ describe('CHT Script API - getDatasource', () => {
beforeEach(() => person = v1.person);

it('contains expected keys', () => {
expect(person).to.have.all.keys(['getByUuid', 'getByUuidWithLineage']);
expect(person).to.have.all.keys(['getByUuid', 'getByUuidWithLineage', 'getPage']);
});

it('getByUuid', async () => {
Expand Down Expand Up @@ -124,6 +125,20 @@ describe('CHT Script API - getDatasource', () => {
expect(personGet.calledOnceWithExactly(qualifier)).to.be.true;
expect(byUuid.calledOnceWithExactly(qualifier.uuid)).to.be.true;
});

it('getPage', async () => {
const expectedPeople: Index.Nullable<Doc>[] = [];
Comment thread
sugat009 marked this conversation as resolved.
Outdated
const personGetPage = sinon.stub().resolves(expectedPeople);
dataContextBind.returns(personGetPage);
const limit = 2;
const skip = 1;

const returnedPeople = await person.getPage(limit, skip);

expect(returnedPeople).to.equal(expectedPeople);
expect(dataContextBind.calledOnceWithExactly(Person.v1.getPage)).to.be.true;
expect(personGetPage.calledOnceWithExactly(limit, skip)).to.be.true;
});
});
});
});
61 changes: 56 additions & 5 deletions shared-libs/cht-datasource/test/local/libs/doc.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Doc from '../../../src/libs/doc';
import sinon, { SinonStub } from 'sinon';
import logger from '@medic/logger';
import { getDocById, getDocsByIds, queryDocsByKey } from '../../../src/local/libs/doc';
import { getDocById, getDocsByIds, queryDocsByKey, queryDocsByRange } from '../../../src/local/libs/doc';
import { expect } from 'chai';

describe('local doc lib', () => {
Expand Down Expand Up @@ -149,7 +149,7 @@ describe('local doc lib', () => {
});
});

describe('queryDocsByKey', () => {
describe('queryDocsByRange', () => {
it('returns lineage docs for the given id', async () => {
const doc0 = { _id: 'doc0' };
const doc1 = { _id: 'doc1' };
Expand All @@ -163,7 +163,7 @@ describe('local doc lib', () => {
});
isDoc.returns(true);

const result = await queryDocsByKey(db, 'medic-client/docs_by_id_lineage')(doc0._id);
const result = await queryDocsByRange(db, 'medic-client/docs_by_id_lineage')(doc0._id, doc0._id);
Comment thread
sugat009 marked this conversation as resolved.
Outdated

expect(result).to.deep.equal([doc0, doc1, doc2]);
expect(dbQuery.calledOnceWithExactly('medic-client/docs_by_id_lineage', {
Expand All @@ -186,7 +186,7 @@ describe('local doc lib', () => {
});
isDoc.returns(true);

const result = await queryDocsByKey(db, 'medic-client/docs_by_id_lineage')(doc0._id);
const result = await queryDocsByRange(db, 'medic-client/docs_by_id_lineage')(doc0._id, doc0._id);

expect(result).to.deep.equal([doc0, null, doc2]);
expect(dbQuery.calledOnceWithExactly('medic-client/docs_by_id_lineage', {
Expand All @@ -204,7 +204,7 @@ describe('local doc lib', () => {
});
isDoc.returns(false);

const result = await queryDocsByKey(db, 'medic-client/docs_by_id_lineage')(doc0._id);
const result = await queryDocsByRange(db, 'medic-client/docs_by_id_lineage')(doc0._id, doc0._id);

expect(result).to.deep.equal([null]);
expect(dbQuery.calledOnceWithExactly('medic-client/docs_by_id_lineage', {
Expand All @@ -215,4 +215,55 @@ describe('local doc lib', () => {
expect(isDoc.calledOnceWithExactly(doc0)).to.be.true;
});
});

describe('queryDocsByKey', () => {
const limit = 100;
const skip = 0;
const contactType = 'person';

it('returns docs on the basis of given key in pages', async () => {
const doc0 = { _id: 'doc0' };
const doc1 = { _id: 'doc1' };
const doc2 = { _id: 'doc2' };

dbQuery.resolves({
rows: [
{ doc: doc0 },
{ doc: doc1 },
{ doc: doc2 }
]
});
isDoc.returns(true);

const result = await queryDocsByKey(db, 'medic-client/contacts_by_type')(contactType, limit, skip);

expect(result).to.deep.equal([doc0, doc1, doc2]);
expect(dbQuery.calledOnceWithExactly('medic-client/contacts_by_type', {
key: [contactType],
include_docs: true,
limit,
skip
})).to.be.true;
expect(isDoc.args).to.deep.equal([[doc0], [doc1], [doc2]]);
});

it('returns empty array if docs are not found', async () => {
dbQuery.resolves({
rows: [
]
});
Comment thread
sugat009 marked this conversation as resolved.
isDoc.returns(true);

const result = await queryDocsByKey(db, 'medic-client/contacts_by_type')(contactType, limit, skip);

expect(result).to.deep.equal([]);
expect(dbQuery.calledOnceWithExactly('medic-client/contacts_by_type', {
key: [contactType],
include_docs: true,
limit,
skip
})).to.be.true;
expect(isDoc.args).to.deep.equal([]);
});
});
Comment thread
sugat009 marked this conversation as resolved.
});
6 changes: 3 additions & 3 deletions shared-libs/cht-datasource/test/local/libs/lineage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ describe('local lineage lib', () => {

it('getLineageDocsById', () => {
const queryFn = sinon.stub();
const queryDocsByKey = sinon
.stub(LocalDoc, 'queryDocsByKey')
const queryDocsByRange = sinon
.stub(LocalDoc, 'queryDocsByRange')
.returns(queryFn);
const medicDb = { hello: 'world' } as unknown as PouchDB.Database<Doc>;

const result = getLineageDocsById(medicDb);

expect(result).to.equal(queryFn);
expect(queryDocsByKey.calledOnceWithExactly(medicDb, 'medic-client/docs_by_id_lineage')).to.be.true;
expect(queryDocsByRange.calledOnceWithExactly(medicDb, 'medic-client/docs_by_id_lineage')).to.be.true;
});

describe('getPrimaryContactIds', () => {
Expand Down
Loading