@synanetics/syn-utils v3.0.2
@synanetics/syn-utils
A collection of common utility functions used across multiple Synanetics repositories
Usage
const {
  getPrefix, removePrefix
} = require('@synanetics/syn-utils');
// or
import {
  getPrefix, removePrefix
} from '@synanetics/syn-utils';API
Paging
Function: pageBuilder
The pageBuilder function takes in an array of sorted sourceBundles and pageConfig, and returns a bundle page with those with the combined resources from all the bundles.
Arguments
function pageBuilder(sourceBundles: SourceBundle[], config: PageConfig) => PageOutput
interface SourceBundle {
  sourceId: string;
  bundle: Bundle; 
}
interface SortConfig {
  fhirSorter: FhirSorter;
  sortTerms: string[];
  allowChainedSort?: boolean;
}
interface PageConfig {
  pagingBaseUrl: URL;
  pageSize?: number;
  sortConfig?: SortConfig;
  currentPageNum?: number;
  partialPage?: Bundle;
}
interface PageOutput {
  completePage: boolean;
  page: Bundle;
  sourceBundles: SourceBundle[];
  bundleToReplace?: string;
}- sourceBundles- The Bundles to combine into a single page
- All Bundles must have a 'Total'
- At least one Bundle must have entries
 
- config- The config defines how the page is to be built
- pagingBaseUrl- The url to which the paging links can be appended
- e.g https://fhir.store.nhs.uk/patient?_queryId=uuid -> - {relation: 'self', url: 'https://fhir.store.nhs.uk/patient?_queryId=uuid&_page=4'}
- {relation: 'first', url: 'https://fhir.store.nhs.uk/patient?_queryId=uuid&_page=1'}
- {relation: 'last', url: 'https://fhir.store.nhs.uk/patient?_queryId=uuid&_page=9'}
- {relation: 'previous', url: 'https://fhir.store.nhs.uk/patient?_queryId=uuid&_page=3'}
- {relation: 'next', url: 'https://fhir.store.nhs.uk/patient?_queryId=uuid&_page=5'}
 
 
- pageSize- The desired maximum page size
- Must be a positive integer
- Will default to 100 if not provided
 
- sortConfig?- This is an optional parameter, if it is not provided the pageBuilder will iterate over each source to build the page
- fhirSorter- An instance of the FhirSorter from the @synanetics/fhir-sort package
 
- sortTerms- An array of strings that will be used to sort the resources in the Bundles
- e.g 'family', '_lastUpdated'
 
- allowChainedSort- A boolean to allow/disallow chained sorting
- For details on chained sorting see the @synanetics/fhir-sort package README
- This parameter is optional and will default to false if not provided
 
 
- currentPageNum?- The number of the page being built
- e.g If you are calling the pageBuilder to build the 3rd page in a set of 5, this value should be 3
- This value will default to 1 if not provided
 
- partialPage?- This is an optional parameter
- This parameter should only be provided when the pageBuilder previously returned an incomplete page
- This happens when the pageBuilder has emptied an input bundle and requires a replacement
- When you recall pageBuilder with the new bundle, you will pass in the incomplete page in partialPage returned by the first call
 
 
Return Values
- completePage- This is a boolean
- This is true when:- The pageBuilder was able to build a page of the desired maximum size
- The page builder has emptied all the bundles and built the final page
 
- This is false when:- The page builder emptied one of the input bundles before completing the page and has determined it needs a replacement
 
 
- page- This is a FHIR Bundle
- Its total will be the combined total of all bundles
- It will have paging links
- Its entries will be populated from the input bundles- Outcomes come first - Any outcomes from all input bundles will appear first on the page
- These can be in any order
 
- Matches come second- A sorted, if sortTerms are provided, subset of the matches from all the input bundles will come after the outcomes
- This will most often not contain all the matches from all the inputs, as the page size will likely be smaller than the number of matches provided
 
- Includes come third- Any includes referenced in the matches will come after the matches
- Each include will be referenced at least once by at least one match
- Each include will only appear once
 
 
- Outcomes come first 
 
- sourceBundles- The sourceBundles outputted by the page builder are similar to the ones inputted
- The key difference is the output bundles will not contain any matches or outcomes already put into a page
- e.g If we have input bundles with 5 matches each for 5 sources, and a page size of 10, the output bundles may contain 3 matches each for 5 sources.
- Bundles that are emptied of matches will not be returned by the pageBuilder
 
- bundleToReplace?- If the pageBuilder empties a paginated Bundle that is not on its last page, it needs to request a new bundle
- When this occurs it populates this optional string with the source id of the bundle to be replaced
 
Usage
This is an example flow where the first call returns a complete page, and the second call requires a replacement bundle.
First Call:
- FHIR query received requiring pagination
- Gather the sorted Bundles from each source
- Call the pageBuilder with the source Bundles, and desired config for page 1
- pageBuilder returns completed page, and partially consumed input bundles
- Cache the page, and partially consumed input bundles
- Return the page
Second Call (with emptied Bundle):
- Second page requested
- Retrieve partially consumed input bundles from cache
- Call the pageBuilder with the partially consumed Bundles, and desired config for page 2
- pageBuilder responds with an incomplete page, partially consumed Bundles, and requests a replacement Bundle from "source1"
- Retrieve the next Bundle from "source1"
- Call the pageBuilder with the partially consumed Bundles, the fresh Bundle from "source1", desired config for page 2 and the incomplete page previously returned
- pageBuilder returns completed page, and partially consumed input bundles
- Cache the page, and partially consumed input bundles
- Return the page
Prefix
Defaults
The prefix functions have default configuration values/functions which are as below.
const defaultPrefixDelimiter: string = '.';
const defaultEscapeRegex = (string: string): string => string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
const defaultPrefixRegex: string = `^(?:.*/)?(?<prefix>.*?)${defaultEsc
apeRegex(defaultPrefixDelimiter)}.*`;Function: getPrefix
The get prefix function takes in a string and returns a prefix that matches the provided arguments.
Arguments
function getPrefix(value: string, validPrefixes?: string[], prefixRegex: string = defaultPrefixRegex) => string- value- The string to retrieve the prefix from
- If no value is provided an error will be thrown
 
- validPrefixes- An array of valid prefix strings
- If this is not provided, all prefixes are considered valid
 
- dpPrefixRegex- Defaults to defaultDpPrefixRegex
- This regex is used to find the prefix in value
 
Usage
//Expect value: wow
getPrefix('wow.theRest');
//Expect value: apple
getPrefix('apple.pie', ['apple', 'pear']);
// Expect error: No value provided 
getPrefix();
//Expect Error: No valid prefixes found
getPrefix('this has no prefix');
//Expect Error: No valid prefixes found
getPrefix('tomato.pie', ['apple', 'pear']);Function: removePrefix
The get prefix function takes in a string and returns a prefix that matches the provided arguments.
Arguments
function removePrefix(value: string, prefix: string, prefixDelimiter: string = defaultPrefixDelimiter, escapeRegex: (string: string) => string = defaultEscapeRegex) => string - value- The string to remove the prefix from
- If no value is provided an error will be thrown
 
- prefix- The prefix to remove
- If no prefix is provided an error will be thrown
 
- prefixDelimiter- Defaults to defaultPrefixDelimiter
- This string is used to split prefix from value
 
- escapeRegex- Defaults to defaultEscapeRegex
- This regex is applied to the prefixDelimiter to escape protected characters
 
Usage
//Expect value: theRest
removePrefix('wow.theRest', 'wow');
//Expect value: apple.pie
removePrefix('apple.pie', 'pear');
// Expect error: No value provided 
removePrefix();
// Expect error: No prefix provided 
removePrefix('wow');Function: removePrefixFromFhirObj
The removePrefixFromFhirObj function removes a prefix string from all ids and reference ids in a FHIR object
function removePrefixFromFhirObj<T extends (fhir3.FhirResource | fhir4.FhirResource)>(  
    input: T,
    prefix: string,
    prefixDelimiter?: string,
    maxDepth?: number,
  ) => TArguments
- input is a FHIR stu3/r4 resource as defined in the @types/fhir package such as a Patient resource
- prefix is a string such as 'HUB' that should be removed from all ids and reference ids
- prefixDelimiter is a string that is used to delimit where a prefix ends and an id begins, this defaults to '.'
- maxDepth is a number that represents the maximum recursion depth that is allowed before an error is thrown, this defaults to 15
Usage
//Input resource: 
const patient: Patient = {
    id: 'HUB.pat1',
    resourceType: 'Patient',
    generalPractitioner: [
    {
        reference: '#contained',
    },
    {
        reference: 'Organization/HUB.org1',
    },
    {
        reference: 'Organization/HUB.org1',
    },
    {
        reference: 'Practitioner/HUB.DDCR.pra1',
    },
    {
        reference: 'Practitioner/HUB.pra2/_history/4',
    },
    ],
    managingOrganization: {
    reference: 'url/fhir/Organization/HUB.DDCR.org2',
    },
};
const unprefixedPatient: Patient = removePrefixFromFhirObj(patient, 'HUB');
// expect unprefixedPatient to equal expectedPatient
const expectedPatient: Patient = {
    id: 'pat1',
    resourceType: 'Patient',
    generalPractitioner: [
    {
        reference: '#contained',
    },
    {
        reference: 'Organization/org1',
    },
    {
        reference: 'Organization/org1',
    },
    {
        reference: 'Practitioner/DDCR.pra1',
    },
    {
        reference: 'Practitioner/pra2/_history/4',
    },
    ],
    managingOrganization: {
    reference: 'url/fhir/Organization/DDCR.org2',
    },
};Reference
Interface: ReferenceDetails
interface ReferenceDetails {
  original: string;
  contained: boolean;
  version: number;
  id?: string;
  type?: string;
  criteria?: string;
  url?: string;
  relative?: string;
}Function: getReferenceDetails
The get getReferenceDetails function takes in a reference string and extracts the details into a ReferenceDetails object
Arguments
function getReferenceDetails(referenceString: string) => ReferenceDetails- referenceString- The string to extract the details from
- If no referenceString is provided an error will be thrown
 
Usage
//Expect value: 
const expectedValue = {
    original: '#p1',
    contained: true,
    version: 1,
    id: 'p1'
}
getReferenceDetails('#p1');
//Expect value: 
const expectedValue = {
      original: 'ValueSet?url=http://hl7.org/fhir/ValueSet/my-valueset&version=0.8',
      contained: false,
      version: 1,
      type: 'ValueSet',
      criteria: 'url=http://hl7.org/fhir/ValueSet/my-valueset&version=0.8',
}
getReferenceDetails('ValueSet?url=http://hl7.org/fhir/ValueSet/my-valueset&version=0.8');
//Expect value: 
const expectedValue = {
      original: 'url/fhir/r4/Patient/12345',
      contained: false,
      version: 1,
      type: 'Patient',
      id: '12345',
      url: 'url/fhir/r4/Patient/12345',
      relative: 'Patient/12345',
}
getReferenceDetails('url/fhir/r4/Patient/12345');
//Expect value: 
const expectedValue = {
      original: 'Patient/12345/_history/5',
      contained: false,
      version: 5,
      type: 'Patient',
      id: '12345',
      relative: 'Patient/12345',
}
getReferenceDetails('Patient/12345/_history/5');Function: getUniqueRelativeReferences
The get getUniqueRelativeReferences function takes in a fhir resource and extracts unique relative reference strings
function getUniqueRelativeReferences(
    input: Record<any, any>,  
    relativeRefs?: Set<string>,
    maxDepth?: string,
) => Set<string>Arguments
- input is a FHIR resource as defined in the @types/fhir package such as a Patient resource
- relativeRefs is a set of unique relative reference strings, e.g Patient/12345, this defaults to an empty set
- maxDepth is a number that represents the maximum recursion depth that is allowed before an error is thrown, this defaults to 15
Usage
//Input resource: 
const patient: Patient = {
    resourceType: 'Patient',
    generalPractitioner: [
    {
        reference: '#notRelative',
    },
    {
        reference: 'Organization/1',
    },
    {
        reference: 'Organization/1',
    },
    {
        reference: 'Practitioner/1',
    },
    {
        reference: 'Practitioner/2/_history/4',
    },
    ],
    managingOrganization: {
    reference: 'url/fhir/Organization/2',
    },
};
//Expect set to contain 'Organization/1', 'Practitioner/1', 'Practitioner/2', 'Organization/2' 
getUniqueRelativeReferences(patient);Rebase References
Function: rebaseReferences
A function to prefix any identifiers or relative references with the supplied string.
Usage:
const resource = {
    id: 'DEF.123',
    subject: {
        reference: 'Patient/DEF.456',
    },
};
const rebasedResource = rebaseReferences(resource, 'ABC');
expect(resource).toEqual({
    id: 'ABC.DEF.123',
    subject: {
        reference: 'Patient/ABC.DEF.456',
    },
})