Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ module.exports = defineConfig([
}],

['jsdoc/check-tag-names']: ['error', {
definedTags: ['typeParam'],
definedTags: ['typeParam', 'packageDocumentation'],
}],
},
},
Expand Down
6 changes: 5 additions & 1 deletion scripts/build/generate-openapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ const TYPE_SOURCES = [
'target.ts',
'input.ts',
].map(file => path.join(DATASOURCE_DIR, file));
const SCHEMAS_TO_SKIP = {
'*': undefined,
'v1.Datasource': undefined, // Imperative interface not referenced for REST apis
};

const TSJ_OPTIONS = {
tsconfig: TSCONFIG,
Expand Down Expand Up @@ -136,7 +140,7 @@ const generateTsSchemas = () => {
.map(path => ({ ...TSJ_OPTIONS, path }))
.map(opts => tsj.createGenerator(opts))
.map(generator => generator.createSchema())
.map(({ definitions }) => ({ ...definitions, '*': undefined }))
.map(({ definitions }) => ({ ...definitions, ...SCHEMAS_TO_SKIP }))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.map(({ definitions }) => ({ ...definitions, ...SCHEMAS_TO_SKIP }))
.map(({ definitions }) => ({ ...definitions, ...SCHEMAS_TO_SKIP }))

.forEach((definitions) => Object.assign(schemas, definitions));
return transformSchema(schemas);
};
Expand Down
37 changes: 36 additions & 1 deletion shared-libs/cht-datasource/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,42 @@

The CHT Datasource library is intended to be agnostic and simple. It provides a versioned API from feature modules.

See the TSDoc in [the code](./src/index.ts) for more information about using the API.
## Usage

To get started, get a `DataContext`:

```ts
import { getRemoteDataContext, getLocalDataContext } from '@medic/cht-datasource';

const dataContext = isOnlineOnly
? getRemoteDataContext(...)
: getLocalDataContext(...);
```

Then, use the context to perform data operations. There are two different usage modes available for performing the
same operations.

### Declarative usage mode
```ts
import { Person, Qualifier } from '@medic/cht-datasource';

const getPerson = Person.v1.get(dataContext);
// Or
const getPerson = dataContext.bind(Person.v1.get);

const myUuid = 'my-uuid';
const myPerson = await getPerson(Qualifier.byUuid(uuid));
```

### Imperative usage mode

```ts
import { getDatasource } from '@medic/cht-datasource';

const datasource = getDatasource(dataContext);
const myUuid = 'my-uuid';
const myPerson = await datasource.v1.person.getByUuid(myUuid);
```

## Development

Expand Down
2 changes: 1 addition & 1 deletion shared-libs/cht-datasource/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"build": "tsc -p tsconfig.build.json",
"build-watch": "tsc --watch -p tsconfig.build.json",
"test": "nyc --nycrcPath='../nyc.config.js' mocha \"test/**/*\"",
"gen-docs": "typedoc ./src/index.ts"
"gen-docs": "typedoc ./src/index.ts --readme none --excludeInternal --treatWarningsAsErrors"
},
"author": "",
"license": "Apache-2.0",
Expand Down
138 changes: 138 additions & 0 deletions shared-libs/cht-datasource/src/contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import {
Page,
} from './libs/core';
import {
and,
byContactType,
byFreetext,
byUuid,
ContactTypeQualifier,
FreetextQualifier,
UuidQualifier
Expand Down Expand Up @@ -139,4 +143,138 @@ export namespace v1 {
};
return curriedGen;
};

/**
* Operations for working with contacts.
*/
export interface Datasource {
/**
* Returns a contact by their UUID.
* @param uuid the UUID of the contact to retrieve
* @returns the contact or `null` if no contact is found for the UUID
* @throws InvalidArgumentError if no UUID is provided
*/
getByUuid: (uuid: string) => Promise<Nullable<v1.Contact>>;

/**
* Returns a contact by their UUID along with the contact's parent lineage.
* @param uuid the UUID of the contact to retrieve
* @returns the contact or `null` if no contact is found for the UUID
* @throws InvalidArgumentError if no UUID is provided
*/
getByUuidWithLineage: (uuid: string) => Promise<Nullable<v1.ContactWithLineage>>;

/**
* Returns an array of contact identifiers for the provided page specifications, freetext and type.
* @param freetext the search keyword(s)
* @param type the type of contact to search for
* @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 specifications
* @throws InvalidArgumentError if either `freetext` or `type` is not provided
* @throws InvalidArgumentError if the `freetext` is empty or if the `type is invalid for a contact
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: The closing backtick after type is missing on this @throws line. The unclosed code span runs forward through the subsequent @throws lines until the next backtick (in `<=0`), merging multiple throw clauses into one garbled <code>-wrapped paragraph in the rendered docs.

Suggested change
* @throws InvalidArgumentError if the `freetext` is empty or if the `type is invalid for a contact
* @throws InvalidArgumentError if the `freetext` is empty or if the `type` is invalid for a contact

Same pattern at three other locations in this file:

  • shared-libs/cht-datasource/src/contact.ts:195
  • shared-libs/cht-datasource/src/contact.ts:229
  • shared-libs/cht-datasource/src/contact.ts:238

Worth catching here since --treatWarningsAsErrors was just enabled on gen-docs in package.json:14 and the merged rendering is visible on the generated Contact.v1.Datasource.html page (getUuidsPageByFreetext and getUuidsPageByTypeFreetext properties).

* @throws InvalidArgumentError if the provided limit is `<= 0`
* @throws InvalidArgumentError if the provided cursor is not a valid page token or `null`
*/
getUuidsPageByTypeFreetext: (
freetext: string,
type: string,
cursor?: Nullable<string>,
limit?: number | `${number}`
) => Promise<Page<string>>;

/**
* Returns an array of contact identifiers for the provided page specifications and type.
* @param type the type of contact to search for
* @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 specifications
* @throws InvalidArgumentError if `type` is not provided
* @throws InvalidArgumentError if the `type is invalid for a contact
* @throws InvalidArgumentError if the provided limit is `<= 0`
* @throws InvalidArgumentError if the provided cursor is not a valid page token or `null`
*/
getUuidsPageByType: (
type: string,
cursor?: Nullable<string>,
limit?: number | `${number}`
) => Promise<Page<string>>;

/**
* Returns an array of contact identifiers for the provided page specifications and freetext.
* @param freetext the search keyword(s)
* @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 specifications
* @throws InvalidArgumentError if `freetext` is not provided
* @throws InvalidArgumentError if the `freetext` is less than 3 characters long or if it contains white-space
* @throws InvalidArgumentError if the provided limit is `<= 0`
* @throws InvalidArgumentError if the provided cursor is not a valid page token or `null`
*/
getUuidsPageByFreetext: (
freetext: string,
cursor?: Nullable<string>,
limit?: number | `${number}`
) => Promise<Page<string>>;

/**
* Returns a generator for fetching all the contact identifiers for given `freetext` and `type`.
* @param freetext the search keyword(s)
* @param type the type of contact identifiers to return
* @returns a generator for fetching all the contact identifiers matching the given `freetext` and `type`.
* @throws InvalidArgumentError if either `freetext` or `type` is not provided
* @throws InvalidArgumentError if the `freetext` is empty or if the `type is invalid for a contact
*/
getUuidsByTypeFreetext: (freetext: string, type: string) => AsyncGenerator<string, null>;

/**
* Returns a generator for fetching all the contact identifiers for given `type`.
* @param type the type of contact identifiers to return
* @returns a generator for fetching all the contact identifiers matching the given `type`.
* @throws InvalidArgumentError if `type` is not provided
* @throws InvalidArgumentError if the `type is invalid for a contact
*/
getUuidsByType: (type: string) => AsyncGenerator<string, null>;

/**
* Returns a generator for fetching all the contact identifiers for given `freetext`.
* @param freetext the search keyword(s)
* @returns a generator for fetching all the contact identifiers matching the given `freetext`.
* @throws InvalidArgumentError if `freetext`is not provided
* @throws InvalidArgumentError if the `freetext` is empty or invalid
*/
getUuidsByFreetext: (freetext: string) => AsyncGenerator<string, null>;
}

/** @internal */
export const getDatasource = (ctx: DataContext): Datasource => {
return {
getByUuid: (uuid) => ctx.bind(v1.get)(byUuid(uuid)),
getByUuidWithLineage: (uuid) => ctx.bind(v1.getWithLineage)(byUuid(uuid)),
getUuidsPageByTypeFreetext: (
freetext,
type,
cursor = null,
limit = DEFAULT_IDS_PAGE_LIMIT
) => ctx.bind(v1.getUuidsPage)(and(byFreetext(freetext), byContactType(type)), cursor, limit),
getUuidsPageByType: (
type,
cursor = null,
limit = DEFAULT_IDS_PAGE_LIMIT
) => ctx.bind(v1.getUuidsPage)(byContactType(type), cursor, limit),
getUuidsPageByFreetext: (
freetext,
cursor = null,
limit = DEFAULT_IDS_PAGE_LIMIT
) => ctx.bind(v1.getUuidsPage)(byFreetext(freetext), cursor, limit),
getUuidsByTypeFreetext: (freetext, type) => ctx.bind(v1.getUuids)(
and(byFreetext(freetext), byContactType(type))
),
getUuidsByType: (type) => ctx.bind(v1.getUuids)(byContactType(type)),
getUuidsByFreetext: (freetext) => ctx.bind(v1.getUuids)(byFreetext(freetext)),
};
};
}
Loading