UNPKG

@oystehr/sdk

Version:

Oystehr SDK

251 lines (248 loc) 8.62 kB
import { addParamsToSearch } from '../../client/client.js'; // Code adapted from https://github.com/sindresorhus/uint8array-extras const MAX_BLOCK_SIZE = 65_535; function stringToBase64(input) { const data = new globalThis.TextEncoder().encode(input); let base64 = ''; for (let index = 0; index < data.length; index += MAX_BLOCK_SIZE) { const chunk = data.subarray(index, index + MAX_BLOCK_SIZE); // Required as `btoa` and `atob` don't properly support Unicode: https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem base64 += globalThis.btoa(String.fromCodePoint.apply(undefined, chunk)); } return base64; } /** * Performs a FHIR search and returns the results as a Bundle resource * * @param options FHIR resource type and FHIR search parameters * @param request optional OystehrClientRequest object * @returns FHIR Bundle resource */ async function search(params, request) { const { resourceType, params: searchParams } = params; let paramMap; if (searchParams) { paramMap = Object.entries(searchParams).reduce((acc, [_, param]) => { if (!acc[param.name]) { acc[param.name] = []; } acc[param.name].push(param.value); return acc; }, {}); } const requestBundle = await this.fhirRequest(`/${resourceType}/_search`, 'POST')(paramMap, { ...request, contentType: 'application/x-www-form-urlencoded', }); const bundle = { ...requestBundle, entry: requestBundle.entry, unbundle: function () { return (this.entry?.map((entry) => entry.resource).filter((resource) => resource !== undefined) ?? []); }, }; return bundle; } async function create(params, request) { const { resourceType } = params; return this.fhirRequest(`/${resourceType}`, 'POST')(params, request); } async function get({ resourceType, id }, request) { return this.fhirRequest(`/${resourceType}/${id}`, 'GET')({}, request); } async function update(params, request) { const { id, resourceType } = params; return this.fhirRequest(`/${resourceType}/${id}`, 'PUT')(params, { ...request, ifMatch: request?.optimisticLockingVersionId ? `W/"${request.optimisticLockingVersionId}"` : undefined, }); } async function patch({ resourceType, id, operations }, request) { return this.fhirRequest(`/${resourceType}/${id}`, 'PATCH')(operations, { ...request, contentType: 'application/json-patch+json', ifMatch: request?.optimisticLockingVersionId ? `W/"${request.optimisticLockingVersionId}"` : undefined, }); } async function del({ resourceType, id }, request) { return this.fhirRequest(`/${resourceType}/${id}`, 'DELETE')({}, request); } async function history({ resourceType, id, versionId, count, offset, }, request) { if (versionId) { return this.fhirRequest(`/${resourceType}/${id}/_history/${versionId}`, 'GET')({}, request); } if (count) { return this.fhirRequest(`/${resourceType}/${id}/_history?_total=accurate&_count=${count} ${offset ? `&_offset=${offset}` : ''}`, 'GET')({}, request); } return this.fhirRequest(`/${resourceType}/${id}/_history?_total=accurate`, 'GET')({}, request); } function batchInputRequestToBundleEntryItem(request) { const { method, url } = request; const baseRequest = { request: { method, url, }, }; // Escape query string parameters in entry.request.url if (url.split('?').length > 1) { const [resource, query] = url.split('?'); const params = query .split('&') .map((param) => { const [name, value] = param.split('='); return { name, value }; }) .reduce((acc, { name, value }) => { if (!name) { return acc; } if (!acc[name]) { acc[name] = []; } acc[name].push(value); return acc; }, {}); const search = new URLSearchParams(); addParamsToSearch(params, search); baseRequest.request.url = `${resource}?${search.toString()}`; } // GET, DELETE, and HEAD require no further parameters if (['GET', 'DELETE', 'HEAD'].includes(method)) { return baseRequest; } // PUT updates require a full resource if (method === 'PUT') { const { resource } = request; return { request: { ...baseRequest.request, ifMatch: request.ifMatch, }, resource: resource, }; } // PATCH can be Binary resource or JSON patch if (method === 'PATCH') { if ('resource' in request) { return { request: { ...baseRequest.request, ifMatch: request.ifMatch, }, resource: request.resource, }; } const data = stringToBase64(JSON.stringify(request.operations)); return { ...baseRequest, resource: { resourceType: 'Binary', contentType: 'application/json-patch+json', data, }, }; } // POST creates require a full resource if (method === 'POST') { const { resource, fullUrl } = request; return { ...baseRequest, resource: resource, fullUrl, }; } // // Add ifMatch for the entries that support it // if ('ifMatch' in request) { // baseRequest.request = { // ...baseRequest.request, // ifMatch: request.ifMatch, // }; // } throw new Error('Unrecognized method'); } async function batch(input, request) { const resp = await this.fhirRequest('/', 'POST')({ resourceType: 'Bundle', type: 'batch', entry: input.requests.map(batchInputRequestToBundleEntryItem), }, request); const bundle = { ...resp, entry: resp.entry, unbundle: function () { return (this.entry ?.map((entry) => entry.resource) .filter((resource) => resource !== undefined) ?? []); }, errors: function () { return this.entry ?.filter((entry) => entry.response?.status?.startsWith('4') || entry.response?.status?.startsWith('5')) .map((entry) => entry.response?.outcome) .filter((outcome) => outcome !== undefined); }, }; return bundle; } async function transaction(input, request) { const resp = await this.fhirRequest('/', 'POST')({ resourceType: 'Bundle', type: 'transaction', entry: input.requests.map(batchInputRequestToBundleEntryItem), }, request); const bundle = { ...resp, entry: resp.entry, unbundle: function () { return (this.entry ?.map((entry) => entry.resource) .filter((resource) => resource !== undefined) ?? []); }, }; return bundle; } function formatAddress(address, options) { const builder = []; if (address.line) { builder.push(...address.line); } if (address.city || address.state || address.postalCode) { const cityStateZip = []; if (address.city) { cityStateZip.push(address.city); } if (address.state) { cityStateZip.push(address.state); } if (address.postalCode) { cityStateZip.push(address.postalCode); } builder.push(cityStateZip.join(', ')); } if (address.use && (options?.all || options?.use)) { builder.push('[' + address.use + ']'); } return builder.join(options?.lineSeparator || ', ').trim(); } function formatHumanName(name, options) { const builder = []; if (name.prefix && options?.prefix !== false) { builder.push(...name.prefix); } if (name.given) { builder.push(...name.given); } if (name.family) { builder.push(name.family); } if (name.suffix && options?.suffix !== false) { builder.push(...name.suffix); } if (name.use && (options?.all || options?.use)) { builder.push('[' + name.use + ']'); } return builder.join(' ').trim(); } export { batch, create, del as delete, formatAddress, formatHumanName, get, history, patch, search, transaction, update }; //# sourceMappingURL=fhir-ext.js.map