Description
openedon Oct 25, 2024
Spinning this off into its own ticket (from #9544) since it can stand alone.
What feature do you want to improve?
As a part of updating our freetext search implementation, we can take the opportunity to implement the search logic in cht-datasource. This will provide a more clean interface for cht-code code to perform searches of contacts and reports as well as resulting in the creation of new REST endpoints for the same searching.
Describe the improvement you'd like
shared-libs/cht-datasource
qualifier.ts
/**
* Combines multiple qualifiers into a single object.
* @returns the combined qualifier
* @throws Error if any of the qualifiers contain intersecting property names
*/
const and = <A, B, C, D>(qualA: A, qualB: B, qualC?: C, qualD?: D): => A & B & (C | object) & (D | object);
/**
* A qualifier that identifies entities based on a freetext search string.
*/
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
*/
const byFreetext(freetext: string) => FreetextQualifier;
/**
* 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`.
*/
const isFreetextQualifier = (qualifier: unknown) => qualifier is FreetextQualifier;
contact.ts
// Move contents of libs/contact.ts into this file
namespace v1 {
// Contact interface should go in the v1 namespace with the following documentation:
/**
* Immutable data about a contact.
*/
/**
* Immutable data about a contact, including the full records of the parent lineage.
*/
interface ContactWithLineage extends Contact {
readonly parent?: ContactWithLineage | NormalizedParent,
}
// New REST api: /api/v1/contact
/**
* 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
*/
const get = (context: DataContext) => (qualifier: UuidQualifier) => Promise<Nullable<Contact>>
/**
* 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<Nullable<ContactWithLineage>>
// 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<string>, limit: number) => Promise<Page<string>>
/**
* 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<string, null>
}
report.ts
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<Nullable<Report>>
// 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<string>, limit: number) => Promise<Page<string>>
/**
* 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<string, null>
}
person.ts/place.ts
Update the Person/PlaceWithLineage
interfaces to use ContactWithLineage
as the parent
(instead of PlaceWithLineage
). Pending #9584 it is probably not safe to assume that a contact parent will always be a place.
Need to do more research regarding the Place.contact
value...
shared-libs/search
Update search logic to call through to the cht-datasource functions when doing freetext searches (or the basic contact_by_type search). For the multi-view searches, we will need to just stream in all pages.
Design details
As mentioned here (#9544 (comment)) the local adapter will need to account for Nouveau logic (though depending on the progress of that implementation, this PR might just port the same online/offline logic from shared-libs/search
that was created by #9544).
In general we should strive to align to the patterns/code used for implementing the person/place-by-type flows in cht-datasource. All the paging logic should basically be the same.
Describe alternatives you've considered
Additional context
Metadata
Assignees
Labels
Type
Projects
Status
This Week's commitments