Skip to content

Commit

Permalink
Merge pull request #17 in LFOR/fhirpath.js from feature/LF-1491/of-ty…
Browse files Browse the repository at this point in the history
…pe-function to master

* commit '17b115bf13573b3529a363efc7ba495cf9bd0e8d':
  npm audit fix
  Added comments about unsupported type hierarchy
  Added limited support for time and Quantity types
  Added limited support for date-time types
  Allowed to omit resource name at the beginning of an expression
  refactor: remove unnecessary "require"
  Pass the model to the converter as a parameter
  Fix typo in comment
  Limited support for type functions
  • Loading branch information
yuriy-sedinkin committed Sep 17, 2020
2 parents 23f388e + 17b115b commit 303f89a
Show file tree
Hide file tree
Showing 14 changed files with 309 additions and 114 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
This log documents significant changes for each release. This project follows
[Semantic Versioning](http://semver.org/).

## [2.6.0] - 2020-09-01
### Added
Limited support for types (see README.md for details):
- Function is(type) and operator "is"
- Function ofType(type)

## [2.5.0] - 2020-08-26
### Added
- Function union(other: collection)
Expand Down
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ some behavior may still be following the previous version, STU1.
The core parser was generated from the FHIRPath ANTLR grammar.

Completed sections:
- 3 (Path selection) - except that "is" and "as" are not supported yet
- 3 (Path selection)
- 5.1 (Existence)
- 5.2 (Filtering and Projection) "ofType" - basic support for primitives
- 5.2 (Filtering and Projection) "ofType" - limited support for types (see below)
- 5.3 (Subsetting)
- 5.4 (Combining)
- 5.6 (String Manipulation)
Expand All @@ -149,13 +149,23 @@ Almost completed sections:
We are deferring handling information about FHIR resources, as much as
possible, with the exception of support for choice types. This affects
implementation of the following sections:
- 6.3 (Types) - deferred
- 6.3 (Types) - "is" - limited support for types(see below),
"as" is not supported yet

Also, because in JSON DateTime and Time types are represented as strings, if a
string in a resource looks like a DateTime or Time (matches the regular
expression defined for those types in FHIR), the string will be interpreted as a
DateTime or Time.

### Limited support for types:
Currently, the type of the resource property value is used to determine the type,
without using the FHIR specification. This shortcut causes the following issues:
- Type hierarchy is not supported;
- FHIR.uri, FHIR.code, FHIR.oid, FHIR.id, FHIR.uuid, FHIR.sid, FHIR.markdown, FHIR.base64Binary are treated as FHIR.string;
- FHIR.unsignedInt, FHIR.positiveInt are treated as FHIR.integer;
- Also, a property could be specified as FHIR.decimal, but treated as FHIR.integer;
- For date-time related types, only FHIR.dateTime, FHIR.time, System.DateTime and System.Time are supported.

## Development Notes

This section is for people doing development on this package (as opposed to
Expand Down
4 changes: 3 additions & 1 deletion converter/bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const sourceDir = __dirname + '/../dataset/';
// Directory for converter output(for YAML and JSON files)
const destDir = __dirname + '/../../test/';

// FHIR model used for test cases
const model = 'r4';
// Descriptions for file generation:
// [<relative path to XML source file>, <relative path to YAML/JSON output file>, <download URL>]
const sources = [
Expand Down Expand Up @@ -64,7 +66,7 @@ commander

for (let i = 0; i < testFiles.length; i++) {
const [xmlFilename, yamlFilename] = testFiles[i];
await convert.testsXmlFileToYamlFile(sourceDir + xmlFilename, destDir + yamlFilename);
await convert.testsXmlFileToYamlFile(sourceDir + xmlFilename, destDir + yamlFilename, model);
}
} catch(e) {
console.error(e);
Expand Down
22 changes: 15 additions & 7 deletions converter/converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,35 @@ const castValue = (value, type) => mapper[type](value);
/**
* Converts Object representing test cases from XML to Object that can be serialized to YAML
* @param {Object} node - result of xml2js.parseString
* @param {string} model - model name, e.g. 'r4','stu3', 'dstu2'
* @return {Object}
*/
const transform = (node) => {
const transform = (node, model = null) => {

return Object.keys(node).reduce((acc, key) => {

switch(key) {
case 'tests':
return { tests: transform(_.pick(node[key], 'group')) };
return { tests: transform(_.pick(node[key], 'group'), model) };

case 'group':
return [...acc, ...node[key].map(item =>
({ [`group: ${item['$'].description || item['$'].name}`]: transform(_.pick(item, 'test')) }))];
({ [`group: ${item['$'].description || item['$'].name}`]: transform(_.pick(item, 'test'), model) }))];

case 'test':
return [...acc, ...node[key].map(item => {
let test = transform(item);
let test = transform(item, model);
if (!test.hasOwnProperty('result') && !test.error) {
test.result = [];
}
if (!validateTest(test)) {
test.disable = true;
if (model && validateTest(Object.assign({}, test, { model }))) {
// if the test cannot be passed without set the model, we set the model
test.model = model;
} else {
// if the test cannot be passed at all, we disable it
test.disable = true;
}
}
return test;
})];
Expand Down Expand Up @@ -120,14 +127,15 @@ module.exports = {
/**
* Serializes an XML test cases to YAML
* @param {string} xmlData
* @param {string} model - model name, e.g. 'r4','stu3', 'dstu2'
* @returns {string}
*/
testsXmlStringToYamlString: async (xmlData) => {
testsXmlStringToYamlString: async (xmlData, model) => {
const parser = new xml2js.Parser({ explicitCharkey: true });
const parseString = util.promisify(parser.parseString);

const parsed = await parseString(xmlData);
const transformed = transform(parsed);
const transformed = transform(parsed, model);
transformed.tests.forEach(group => {
const groupKey = Object.keys(group)[0];
const groupTests = group[groupKey];
Expand Down
17 changes: 15 additions & 2 deletions converter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,27 @@ const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);

module.exports = {
/**
* Converts XML resource to the YAML format
* @param {string} from - path to XML file
* @param {string} to - path to YAML file
*/
resourceXmlFileToJsonFile: async (from, to) => {
const xmlData = await readFile(from);
const ymlData = await convert.resourceXmlStringToJsonString(xmlData);
await writeFile(to, ymlData);
},
testsXmlFileToYamlFile: async (from, to) => {


/**
* Converts XML test cases to the YAML format
* @param {string} from - path to XML file
* @param {string} to - path to YAML file
* @param {string} model - model name, e.g. 'r4','stu3', 'dstu2'
*/
testsXmlFileToYamlFile: async (from, to, model) => {
const xmlData = await readFile(from);
const ymlData = await convert.testsXmlStringToYamlString(xmlData);
const ymlData = await convert.testsXmlStringToYamlString(xmlData, model);
await writeFile(to, ymlData);
}
};
72 changes: 42 additions & 30 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fhirpath",
"version": "2.5.0",
"version": "2.6.0",
"description": "A FHIRPath engine",
"main": "src/fhirpath.js",
"dependencies": {
Expand All @@ -15,7 +15,7 @@
"@babel/preset-env": "^7.5.5",
"babel-eslint": "^9.0.0",
"babel-loader": "^8.0.6",
"bestzip": "^2.1.4",
"bestzip": "^2.1.7",
"copy-webpack-plugin": "^6.0.3",
"eslint": "^5.2.0",
"fhir": "^4.7.10",
Expand Down
Loading

0 comments on commit 303f89a

Please sign in to comment.