fume-fhir-converter
Version:
FHIR-Utilized Mapping Engine - Community
147 lines (132 loc) • 6.02 kB
text/typescript
/**
* © Copyright Outburn Ltd. 2022-2024 All Rights Reserved
* Project name: FUME-COMMUNITY
*/
/* eslint-disable no-trailing-spaces */
/**
* © Copyright Outburn Ltd. 2022-2023 All Rights Reserved
* Project name: FUME
*/
import HL7Dictionary from 'hl7-dictionary';
import jsonata from 'jsonata';
import config from '../../config';
import { IAppBinding } from '../../types';
import { getCache } from '../cache';
import * as conformance from '../conformance';
import { codeSystemDictionary, valueSetExpandDictionary } from '../conformance/conformance';
import fhirFuncs from '../fhirFunctions';
import { parseCsv } from '../inputConverters';
import * as v2 from '../inputConverters/hl7v2';
import { getLogger } from '../logger';
import * as objectFuncs from '../objectFunctions';
import compiler from '../parser';
import { removeComments } from '../parser/removeComments';
import runtime from '../runtime';
import * as stringFuncs from '../stringFunctions';
import { getStructureDefinition } from './getStructureDefinition';
import { logInfo, logWarn } from './log';
import { registerTable } from './registerTable';
const dev = process.env.NODE_ENV === 'dev';
const compiledExpression = async (expression: string): Promise<jsonata.Expression> => {
const { compiledExpressions } = getCache();
// takes a fume expression string and compiles it into a jsonata expression
// or returns the already compiled expression from cache
const key = stringFuncs.hashKey(expression); // turn expression string to a key
let compiled: any = compiledExpressions.get(key); // get from cache
if (compiled === undefined) { // not cached
getLogger().info('expression not cached, compiling it...');
const parsedAsJsonataStr = await compiler.toJsonataString(expression);
compiled = jsonata(parsedAsJsonataStr!);
compiledExpressions.set(key, compiled);
};
return compiled;
};
export const transform = async (input: any, expression: string, extraBindings: Record<string, IAppBinding> = {}) => {
try {
getLogger().info('Running transformation...');
const expr = await compiledExpression(expression);
let bindings: Record<string, Function | Record<string, any> | string> = {};
// bind all mappings from cache
const { compiledMappings } = getCache();
const mappingIds: any[] = Array.from(compiledMappings.keys());
if (mappingIds) {
mappingIds.forEach((mappingId) => {
const mapping: any = compiledMappings.get(mappingId);
expr.registerFunction(mappingId, mapping?.function, '<x-o?:x>');
});
}
// bind functions
bindings.__checkResourceId = runtime.checkResourceId;
bindings.__finalize = runtime.finalize;
bindings.__castToFhir = runtime.castToFhir;
bindings.__flashMerge = runtime.flashMerge;
bindings.wait = runtime.wait;
bindings.resourceId = fhirFuncs.resourceId;
bindings.initCap = stringFuncs.initCap;
bindings.isEmpty = objectFuncs.isEmpty;
bindings.matches = stringFuncs.matches;
bindings.stringify = JSON.stringify;
bindings.selectKeys = objectFuncs.selectKeys;
bindings.omitKeys = objectFuncs.omitKeys;
bindings.uuid = stringFuncs.uuid;
bindings.registerTable = registerTable;
bindings.translateCode = fhirFuncs.translateCode;
bindings.translate = fhirFuncs.translateCode;
bindings.translateCoding = fhirFuncs.translateCoding;
bindings.search = fhirFuncs.search;
bindings.searchSingle = fhirFuncs.searchSingle;
bindings.literal = fhirFuncs.literal;
bindings.resolve = fhirFuncs.resolve;
bindings.warning = logWarn;
bindings.info = logInfo;
bindings.parseCsv = parseCsv;
bindings.v2parse = v2.v2parse;
bindings.v2json = v2.v2json;
bindings.capabilities = fhirFuncs.capabilities;
const { aliases } = getCache();
// these are debug functions, should be removed in production versions
bindings.fhirCacheIndex = conformance.getFhirPackageIndex();
bindings.getTable = conformance.getTable;
bindings.v2dictionary = HL7Dictionary.definitions;
bindings.v2codeLookup = v2.v2codeLookup;
bindings.v2tableUrl = v2.v2tableUrl;
bindings.toJsonataString = compiler.toJsonataString;
if (dev) bindings.getMandatoriesOfElement = compiler.getMandatoriesOfElement;
if (dev) bindings.getMandatoriesOfStructure = compiler.getMandatoriesOfStructure;
if (dev) bindings.getElementDefinition = compiler.getElementDefinition;
if (dev) bindings.replaceColonsWithBrackets = compiler.replaceColonsWithBrackets;
if (dev) bindings.removeComments = removeComments;
if (dev) bindings.valueSetExpandDictionary = valueSetExpandDictionary;
if (dev) bindings.codeSystemDictionary = codeSystemDictionary;
bindings.getCodeSystem = conformance.getCodeSystem;
bindings.getValueSet = conformance.getValueSet;
// end of debug functions
// bind all aliases from cache
bindings = {
...aliases.getDict(),
...bindings,
...extraBindings
};
expr.registerFunction('base64encode', stringFuncs.base64encode, '<s-:s>');
expr.registerFunction('base64decode', stringFuncs.base64decode, '<s-:s>');
expr.registerFunction('startsWith', stringFuncs.startsWith, '<s-s:b>');
expr.registerFunction('endsWith', stringFuncs.endsWith, '<s-s:b>');
expr.registerFunction('isNumeric', stringFuncs.isNumeric, '<j-:b>');
expr.registerFunction('reference', fhirFuncs.reference, '<o-:s>');
expr.registerFunction(
'getStructureDefinition',
(defId: string) => getStructureDefinition(defId, config.getFhirVersion(), conformance.getFhirPackageIndex(), getLogger()),
'<s-:o>'
);
expr.registerFunction(
'getSnapshot',
async (defId: string) => await compiler.getSnapshot(defId, config.getFhirVersion(), conformance.getFhirPackageIndex(), getLogger()),
'<s-:o>'
);
const res = await expr.evaluate(input, bindings);
return res;
} catch (error) {
getLogger().error(error);
throw error;
}
};