@oystehr/sdk
Version:
Oystehr SDK
263 lines (259 loc) • 8.82 kB
JavaScript
;
var client = require('../../client/client.cjs');
// 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();
client.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();
}
exports.batch = batch;
exports.create = create;
exports.delete = del;
exports.formatAddress = formatAddress;
exports.formatHumanName = formatHumanName;
exports.get = get;
exports.history = history;
exports.patch = patch;
exports.search = search;
exports.transaction = transaction;
exports.update = update;
//# sourceMappingURL=fhir-ext.cjs.map