Skip to content

Commit

Permalink
feat: refactor contact code to remove contact-types.ts (#9753)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkuester authored Jan 27, 2025
1 parent e523a25 commit d4bc934
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 79 deletions.
28 changes: 0 additions & 28 deletions shared-libs/cht-datasource/src/contact-types.ts

This file was deleted.

16 changes: 12 additions & 4 deletions shared-libs/cht-datasource/src/contact.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
getPagedGenerator,
getPagedGenerator, NormalizedParent,
Nullable,
Page,
} from './libs/core';
Expand All @@ -13,7 +13,6 @@ import { LocalDataContext } from './local/libs/data-context';
import { RemoteDataContext } from './remote/libs/data-context';
import * as Local from './local';
import * as Remote from './remote';
import * as ContactTypes from './contact-types';
import { DEFAULT_DOCS_PAGE_LIMIT } from './libs/constants';
import {
assertContactTypeFreetextQualifier,
Expand All @@ -25,17 +24,26 @@ import {
isContactType,
isFreetextType,
} from './libs/parameter-validators';
import { Doc } from './libs/doc';

/** */
export namespace v1 {
/**
* Immutable data about a Contact.
*/
export type Contact = ContactTypes.v1.Contact;
export interface Contact extends Doc, NormalizedParent {
readonly contact_type?: string;
readonly name?: string;
readonly reported_date?: Date;
readonly type: string;
}

/**
* Immutable data about a contact, including the full records of the parent's lineage.
*/
export type ContactWithLineage = ContactTypes.v1.ContactWithLineage;
export interface ContactWithLineage extends Contact {
readonly parent?: ContactWithLineage | NormalizedParent;
}

const getContact =
<T>(
Expand Down
11 changes: 11 additions & 0 deletions shared-libs/cht-datasource/src/libs/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,14 @@ export const getPagedGenerator = async function* <S, T>(

return null;
};

/** @internal */
export interface NormalizedParent extends DataObject, Identifiable {
readonly parent?: NormalizedParent;
}

/** @ignore */
export const isNormalizedParent = (value: unknown): value is NormalizedParent => {
return isDataObject(value) && isIdentifiable(value) && (!value.parent || isNormalizedParent(value.parent));
};

8 changes: 4 additions & 4 deletions shared-libs/cht-datasource/src/local/contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
queryDocUuidsByRange
} from './libs/doc';
import { ContactTypeQualifier, FreetextQualifier, isKeyedFreetextQualifier, UuidQualifier } from '../qualifier';
import * as ContactType from '../contact-types';
import * as Contact from '../contact';
import { isNonEmptyArray, NonEmptyArray, Nullable, Page } from '../libs/core';
import { Doc } from '../libs/doc';
import logger from '@medic/logger';
Expand All @@ -25,7 +25,7 @@ export namespace v1 {
};

const isContact =
(settings: SettingsService) => (doc: Nullable<Doc>, uuid?: string): doc is ContactType.v1.Contact => {
(settings: SettingsService) => (doc: Nullable<Doc>, uuid?: string): doc is Contact.v1.Contact => {
if (!doc) {
if (uuid) {
logger.warn(`No contact found for identifier [${uuid}].`);
Expand All @@ -43,7 +43,7 @@ export namespace v1 {
/** @internal */
export const get = ({ medicDb, settings }: LocalDataContext) => {
const getMedicDocById = getDocById(medicDb);
return async (identifier: UuidQualifier): Promise<Nullable<ContactType.v1.Contact>> => {
return async (identifier: UuidQualifier): Promise<Nullable<Contact.v1.Contact>> => {
const doc = await getMedicDocById(identifier.uuid);
if (!isContact(settings)(doc, identifier.uuid)) {
return null;
Expand All @@ -57,7 +57,7 @@ export namespace v1 {
export const getWithLineage = ({ medicDb, settings }: LocalDataContext) => {
const getLineageDocs = getLineageDocsById(medicDb);

return async (identifier: UuidQualifier): Promise<Nullable<ContactType.v1.ContactWithLineage>> => {
return async (identifier: UuidQualifier): Promise<Nullable<Contact.v1.ContactWithLineage>> => {
const [contact, ...lineageContacts] = await getLineageDocs(identifier.uuid);
if (!isContact(settings)(contact, identifier.uuid)) {
return null;
Expand Down
9 changes: 4 additions & 5 deletions shared-libs/cht-datasource/src/local/libs/lineage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as Contact from '../../contact';
import * as ContactTypes from '../../contact-types';
import * as Person from '../../person';
import {
DataObject,
Expand All @@ -9,7 +8,7 @@ import {
isIdentifiable,
isNonEmptyArray,
isNotNull,
NonEmptyArray,
NonEmptyArray, NormalizedParent,
Nullable
} from '../../libs/core';
import { Doc } from '../../libs/doc';
Expand Down Expand Up @@ -50,7 +49,7 @@ export const hydratePrimaryContact = (contacts: Doc[]) => (place: Nullable<Doc>)
};
};

const getParentUuid = (index: number, contact?: ContactTypes.v1.NormalizedParent): Nullable<string> => {
const getParentUuid = (index: number, contact?: NormalizedParent): Nullable<string> => {
if (!contact) {
return null;
}
Expand Down Expand Up @@ -101,7 +100,7 @@ export const getContactLineage = (medicDb: PouchDB.Database<Doc>) => {
contacts: NonEmptyArray<Nullable<Doc>>,
person?: Person.v1.Person,
filterSelf = false,
): Promise<Nullable<ContactTypes.v1.ContactWithLineage>> => {
): Promise<Nullable<Contact.v1.ContactWithLineage>> => {
const contactUuids = getPrimaryContactIds(contacts);
const uuidsToFetch = filterSelf ? contactUuids.filter(uuid => uuid !== person?._id) : contactUuids;
const fetchedContacts = await getMedicDocsById(uuidsToFetch);
Expand All @@ -111,7 +110,7 @@ export const getContactLineage = (medicDb: PouchDB.Database<Doc>) => {
).filter(item => item ?? false);
const [mainContact, ...lineageContacts] = contactsWithHydratedPrimaryContact;
const contactWithLineage = hydrateLineage(
(person ?? mainContact) as ContactTypes.v1.Contact,
(person ?? mainContact) as Contact.v1.Contact,
person ? contactsWithHydratedPrimaryContact : lineageContacts
);

Expand Down
5 changes: 2 additions & 3 deletions shared-libs/cht-datasource/src/person.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { ContactTypeQualifier, UuidQualifier } from './qualifier';
import { adapt, assertDataContext, DataContext } from './libs/data-context';
import * as Contact from './contact';
import * as ContactTypes from './contact-types';
import * as Remote from './remote';
import * as Local from './local';
import * as Place from './place';
import { LocalDataContext } from './local/libs/data-context';
import { RemoteDataContext } from './remote/libs/data-context';
import { getPagedGenerator, Nullable, Page } from './libs/core';
import { getPagedGenerator, NormalizedParent, Nullable, Page } from './libs/core';
import { DEFAULT_DOCS_PAGE_LIMIT } from './libs/constants';
import { assertCursor, assertLimit, assertTypeQualifier, assertUuidQualifier } from './libs/parameter-validators';

Expand All @@ -27,7 +26,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 | ContactTypes.v1.NormalizedParent;
readonly parent?: Place.v1.PlaceWithLineage | NormalizedParent;
}

const getPerson =
Expand Down
9 changes: 4 additions & 5 deletions shared-libs/cht-datasource/src/place.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import * as Contact from './contact';
import * as ContactTypes from './contact-types';
import * as Person from './person';
import { LocalDataContext } from './local/libs/data-context';
import { ContactTypeQualifier, 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 { getPagedGenerator, Nullable, Page } from './libs/core';
import { getPagedGenerator, NormalizedParent, Nullable, Page } from './libs/core';
import { DEFAULT_DOCS_PAGE_LIMIT } from './libs/constants';
import { assertCursor, assertLimit, assertTypeQualifier, assertUuidQualifier } from './libs/parameter-validators';

Expand All @@ -17,7 +16,7 @@ export namespace v1 {
* Immutable data about a place contact.
*/
export interface Place extends Contact.v1.Contact {
readonly contact?: ContactTypes.v1.NormalizedParent;
readonly contact?: NormalizedParent;
readonly place_id?: string;
}

Expand All @@ -26,8 +25,8 @@ export namespace v1 {
* contact for the place.
*/
export interface PlaceWithLineage extends Place {
readonly contact?: Person.v1.PersonWithLineage | ContactTypes.v1.NormalizedParent;
readonly parent?: PlaceWithLineage | ContactTypes.v1.NormalizedParent;
readonly contact?: Person.v1.PersonWithLineage | NormalizedParent;
readonly parent?: PlaceWithLineage | NormalizedParent;
}

const getPlace =
Expand Down
6 changes: 3 additions & 3 deletions shared-libs/cht-datasource/src/remote/contact.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getResource, getResources, RemoteDataContext } from './libs/data-context';
import { ContactTypeQualifier, FreetextQualifier, UuidQualifier } from '../qualifier';
import { Nullable, Page } from '../libs/core';
import * as ContactType from '../contact-types';
import * as Contact from '../contact';
import { isContactType, isFreetextType } from '../libs/parameter-validators';

/** @internal */
Expand All @@ -13,14 +13,14 @@ export namespace v1 {
/** @internal */
export const get = (remoteContext: RemoteDataContext) => (
identifier: UuidQualifier
): Promise<Nullable<ContactType.v1.Contact>> => getContact(remoteContext)(identifier.uuid);
): Promise<Nullable<Contact.v1.Contact>> => getContact(remoteContext)(identifier.uuid);

/** @internal */
export const getWithLineage = (
remoteContext: RemoteDataContext
) => (
identifier: UuidQualifier
): Promise<Nullable<ContactType.v1.ContactWithLineage>> => getContact(remoteContext)(identifier.uuid, {
): Promise<Nullable<Contact.v1.ContactWithLineage>> => getContact(remoteContext)(identifier.uuid, {
with_lineage: 'true',
});

Expand Down
26 changes: 0 additions & 26 deletions shared-libs/cht-datasource/test/contact.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import * as Local from '../src/local';
import * as Remote from '../src/remote';
import * as Qualifier from '../src/qualifier';
import * as Contact from '../src/contact';
import * as ContactType from '../src/contact-types';
import { expect } from 'chai';
import * as Core from '../src/libs/core';

Expand All @@ -28,31 +27,6 @@ describe('contact', () => {
afterEach(() => sinon.restore());

describe('v1', () => {
describe('isNormalizedParent', () => {
let isDataObject: SinonStub;

beforeEach(() => isDataObject = sinon.stub(Core, 'isDataObject'));
afterEach(() => sinon.restore());

([
[{ _id: 'my-id' }, true, true],
[{ _id: 'my-id' }, false, false],
[{ hello: 'my-id' }, true, false],
[{ _id: 1 }, true, false],
[{ _id: 'my-id', parent: 'hello' }, true, false],
[{ _id: 'my-id', parent: null }, true, true],
[{ _id: 'my-id', parent: { hello: 'world' } }, true, false],
[{ _id: 'my-id', parent: { _id: 'parent-id' } }, true, true],
[{ _id: 'my-id', parent: { _id: 'parent-id', parent: { hello: 'world' } } }, true, false],
[{ _id: 'my-id', parent: { _id: 'parent-id', parent: { _id: 'grandparent-id' } } }, true, true],
] as [unknown, boolean, boolean][]).forEach(([value, dataObj, expected]) => {
it(`evaluates ${JSON.stringify(value)}`, () => {
isDataObject.returns(dataObj);
expect(ContactType.v1.isNormalizedParent(value)).to.equal(expected);
});
});
});

describe('get', () => {
const contact = { _id: 'my-contact' } as Contact.v1.Contact;
const qualifier = { uuid: contact._id } as const;
Expand Down
28 changes: 27 additions & 1 deletion shared-libs/cht-datasource/test/libs/core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import {
hasFields,
isDataObject,
isIdentifiable,
isNonEmptyArray,
isNonEmptyArray, isNormalizedParent,
isRecord,
isString,
NonEmptyArray
} from '../../src/libs/core';
import sinon, { SinonStub } from 'sinon';
import * as Core from '../../src/libs/core';

describe('core lib', () => {
afterEach(() => sinon.restore());
Expand Down Expand Up @@ -274,4 +275,29 @@ describe('core lib', () => {
expect(fetchFunctionStub.calledOnce).to.be.true;
});
});

describe('isNormalizedParent', () => {
let isDataObject: SinonStub;

beforeEach(() => isDataObject = sinon.stub(Core, 'isDataObject'));
afterEach(() => sinon.restore());

([
[{ _id: 'my-id' }, true, true],
[{ _id: 'my-id' }, false, false],
[{ hello: 'my-id' }, true, false],
[{ _id: 1 }, true, false],
[{ _id: 'my-id', parent: 'hello' }, true, false],
[{ _id: 'my-id', parent: null }, true, true],
[{ _id: 'my-id', parent: { hello: 'world' } }, true, false],
[{ _id: 'my-id', parent: { _id: 'parent-id' } }, true, true],
[{ _id: 'my-id', parent: { _id: 'parent-id', parent: { hello: 'world' } } }, true, false],
[{ _id: 'my-id', parent: { _id: 'parent-id', parent: { _id: 'grandparent-id' } } }, true, true],
] as [unknown, boolean, boolean][]).forEach(([value, dataObj, expected]) => {
it(`evaluates ${JSON.stringify(value)}`, () => {
isDataObject.returns(dataObj);
expect(isNormalizedParent(value)).to.equal(expected);
});
});
});
});

0 comments on commit d4bc934

Please sign in to comment.