Skip to content

Commit

Permalink
Merge pull request #34 from HL7/bugfix/LF-1338-fix-choice-type-suppor…
Browse files Browse the repository at this point in the history
…t-for-resursive

Bugfix/lf 1338 fix choice type support for resursive
  • Loading branch information
plynchnlm committed Jan 17, 2020
2 parents 136c6b0 + 021696a commit 259f7aa
Show file tree
Hide file tree
Showing 15 changed files with 738 additions and 141 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
This log documents significant changes for each release. This project follows
[Semantic Versioning](http://semver.org/).

## [1.0.1] - 2020-01-17
### Fixed
- Issues with the new choice type support raised in
https://github.com/HL7/fhirpath.js/pull/34.

## [1.0.0] - 2019-12-19
### Added
- Support for FHIR "choice types" (e.g. Observation.value). The support is
Expand Down
65 changes: 0 additions & 65 deletions fhir-context/extract-choice-types.js

This file was deleted.

86 changes: 86 additions & 0 deletions fhir-context/extract-model-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Takes a directory of JSON FHIR definitions (STU3 or R4) and extracts the information
// about "choice types" (polymorphic fields).

const process = require('process');
const childProcess = require('child_process');
const path = require('path');
const fs = require('fs');

const args = require('yargs')
.nargs('fhirDefDir', 1)
.describe('fhirDefDir', 'the directory pathname for the JSON FHIR '+
'definitions to be processed')
.nargs('outputDir', 1)
.describe('outputDir', 'the directory into which the output files should be written')
.demandOption(['fhirDefDir', 'outputDir'])
.help('h')
.alias('h', 'help')
.argv;

const fhirDefDir = args.fhirDefDir;
const outputDir = args.outputDir;

// Files defining choice type fields
const choiceTypeFiles = ['profiles-types.json', 'profiles-resources.json',
'profiles-others.json'];

let choiceTypePaths = {};
let pathsDefinedElsewhere = {};
for (let f of choiceTypeFiles) {
// File all of the "path" information from the file. There will be
// duplicates, but we will use a hash to get a unique list.
let fData = JSON.parse(fs.readFileSync(path.join(fhirDefDir, f)));
// Walk the tree, looking for "path", and then finding the associated "type"
// field, if any.
let currentResource;
function visitNode(n) {
if (n.kind === "resource") {
currentResource = n;
// Only process definitions that are not constraints on resources defined
// elsewhere.
if (currentResource.derivation !== "constraint") {
// Only process snapshot definitions (not differentials)
visitNode(currentResource.snapshot);
}
}
else {
if (n.id && n.path && n.contentReference) {
if (n.contentReference[0] !== '#')
throw new Error('Unhandled new type of contentReference');
let refID = n.contentReference.slice(1);
// The content references point to IDs, and we want the path, which is
// the same except for slice labels. Remove those.
refID = refID.replace(/:[^\.]+/g, '');
pathsDefinedElsewhere[n.path] = refID;
}
if (n.id && n.path && n.type && n.path.match(/\[x\]$/)) {
// Uppercase the first letter of the type codes so they can be appended to
// the choice field name.
let types = n.type.map(t=>t.code[0].toUpperCase() + t.code.slice(1));
// Remove the [x] from end of the path
choiceTypePaths[n.path.slice(0, -3)] = types;
}
else {
// Check sub-nodes that are objects or arrays
if (Array.isArray(n)) {
for (let e of n)
visitNode(e);
}
else if (typeof n === "object") {
for (let k of Object.keys(n))
visitNode(n[k]);
}
}
}
}
visitNode(fData);
}

// Output the results as JSON hash for ease of import and ease of checking
// whether a path is a choice type path.
// Since there are no nested objects, we can easily sort the output keys.
fs.writeFileSync(path.join(outputDir, 'choiceTypePaths.json'),
JSON.stringify(choiceTypePaths, Object.keys(choiceTypePaths).sort(), 2));
fs.writeFileSync(path.join(outputDir, 'pathsDefinedElsewhere.json'),
JSON.stringify(pathsDefinedElsewhere, Object.keys(pathsDefinedElsewhere).sort(), 2));

2 changes: 1 addition & 1 deletion fhir-context/r4/choiceTypePaths.json
Original file line number Diff line number Diff line change
Expand Up @@ -1362,4 +1362,4 @@
"Code",
"DateTime"
]
}
}
8 changes: 7 additions & 1 deletion fhir-context/r4/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,11 @@ module.exports = {
* A hash of resource element paths (e.g. Observation.value) that are known
* to point to fiels that are choice types.
*/
choiceTypePaths: require('./choiceTypePaths')
choiceTypePaths: require('./choiceTypePaths'),

/**
* A hash from paths to the path for which their content is defined, e.g.
* Questionnaire.item.item -> Questionnaire.item.
*/
pathsDefinedElsewhere: require('./pathsDefinedElsewhere')
}
57 changes: 57 additions & 0 deletions fhir-context/r4/pathsDefinedElsewhere.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"Bundle.entry.link": "Bundle.link",
"CapabilityStatement.rest.operation": "CapabilityStatement.rest.resource.operation",
"CapabilityStatement.rest.searchParam": "CapabilityStatement.rest.resource.searchParam",
"ChargeItemDefinition.propertyGroup.applicability": "ChargeItemDefinition.applicability",
"ClaimResponse.addItem.adjudication": "ClaimResponse.item.adjudication",
"ClaimResponse.addItem.detail.adjudication": "ClaimResponse.item.adjudication",
"ClaimResponse.addItem.detail.subDetail.adjudication": "ClaimResponse.item.adjudication",
"ClaimResponse.adjudication": "ClaimResponse.item.adjudication",
"ClaimResponse.item.detail.adjudication": "ClaimResponse.item.adjudication",
"ClaimResponse.item.detail.subDetail.adjudication": "ClaimResponse.item.adjudication",
"CodeSystem.concept.concept": "CodeSystem.concept",
"Composition.section.section": "Composition.section",
"ConceptMap.group.element.target.product": "ConceptMap.group.element.target.dependsOn",
"Consent.provision.provision": "Consent.provision",
"Contract.term.asset.answer": "Contract.term.offer.answer",
"Contract.term.group": "Contract.term",
"ExampleScenario.process.step.alternative.step": "ExampleScenario.process.step",
"ExampleScenario.process.step.operation.request": "ExampleScenario.instance.containedInstance",
"ExampleScenario.process.step.operation.response": "ExampleScenario.instance.containedInstance",
"ExampleScenario.process.step.process": "ExampleScenario.process",
"ExplanationOfBenefit.addItem.adjudication": "ExplanationOfBenefit.item.adjudication",
"ExplanationOfBenefit.addItem.detail.adjudication": "ExplanationOfBenefit.item.adjudication",
"ExplanationOfBenefit.addItem.detail.subDetail.adjudication": "ExplanationOfBenefit.item.adjudication",
"ExplanationOfBenefit.adjudication": "ExplanationOfBenefit.item.adjudication",
"ExplanationOfBenefit.item.detail.adjudication": "ExplanationOfBenefit.item.adjudication",
"ExplanationOfBenefit.item.detail.subDetail.adjudication": "ExplanationOfBenefit.item.adjudication",
"GraphDefinition.link.target.link": "GraphDefinition.link",
"ImplementationGuide.definition.page.page": "ImplementationGuide.definition.page",
"Invoice.totalPriceComponent": "Invoice.lineItem.priceComponent",
"MedicinalProductAuthorization.procedure.application": "MedicinalProductAuthorization.procedure",
"MedicinalProductIngredient.substance.strength": "MedicinalProductIngredient.specifiedSubstance.strength",
"MedicinalProductPackaged.packageItem.packageItem": "MedicinalProductPackaged.packageItem",
"Observation.component.referenceRange": "Observation.referenceRange",
"OperationDefinition.parameter.part": "OperationDefinition.parameter",
"Parameters.parameter.part": "Parameters.parameter",
"PlanDefinition.action.action": "PlanDefinition.action",
"Provenance.entity.agent": "Provenance.agent",
"Questionnaire.item.item": "Questionnaire.item",
"QuestionnaireResponse.item.answer.item": "QuestionnaireResponse.item",
"QuestionnaireResponse.item.item": "QuestionnaireResponse.item",
"RequestGroup.action.action": "RequestGroup.action",
"StructureMap.group.rule.rule": "StructureMap.group.rule",
"SubstanceSpecification.molecularWeight": "SubstanceSpecification.structure.isotope.molecularWeight",
"SubstanceSpecification.name.synonym": "SubstanceSpecification.name",
"SubstanceSpecification.name.translation": "SubstanceSpecification.name",
"SubstanceSpecification.structure.molecularWeight": "SubstanceSpecification.structure.isotope.molecularWeight",
"TestReport.teardown.action.operation": "TestReport.setup.action.operation",
"TestReport.test.action.assert": "TestReport.setup.action.assert",
"TestReport.test.action.operation": "TestReport.setup.action.operation",
"TestScript.teardown.action.operation": "TestScript.setup.action.operation",
"TestScript.test.action.assert": "TestScript.setup.action.assert",
"TestScript.test.action.operation": "TestScript.setup.action.operation",
"ValueSet.compose.exclude": "ValueSet.compose.include",
"ValueSet.expansion.contains.contains": "ValueSet.expansion.contains",
"ValueSet.expansion.contains.designation": "ValueSet.compose.include.concept.designation"
}
2 changes: 1 addition & 1 deletion fhir-context/stu3/choiceTypePaths.json
Original file line number Diff line number Diff line change
Expand Up @@ -1023,4 +1023,4 @@
"CodeableConcept",
"Reference"
]
}
}
8 changes: 7 additions & 1 deletion fhir-context/stu3/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,11 @@ module.exports = {
* A hash of resource element paths (e.g. Observation.value) that are known
* to point to fiels that are choice types.
*/
choiceTypePaths: require('./choiceTypePaths')
choiceTypePaths: require('./choiceTypePaths'),

/**
* A hash from paths to the path for which their content is defined, e.g.
* Questionnaire.item.item -> Questionnaire.item.
*/
pathsDefinedElsewhere: require('./pathsDefinedElsewhere')
}
37 changes: 37 additions & 0 deletions fhir-context/stu3/pathsDefinedElsewhere.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"Bundle.entry.link": "Bundle.link",
"CapabilityStatement.rest.searchParam": "CapabilityStatement.rest.resource.searchParam",
"ClaimResponse.addItem.adjudication": "ClaimResponse.item.adjudication",
"ClaimResponse.addItem.detail.adjudication": "ClaimResponse.item.adjudication",
"ClaimResponse.item.detail.adjudication": "ClaimResponse.item.adjudication",
"ClaimResponse.item.detail.subDetail.adjudication": "ClaimResponse.item.adjudication",
"CodeSystem.concept.concept": "CodeSystem.concept",
"Composition.section.section": "Composition.section",
"ConceptMap.group.element.target.product": "ConceptMap.group.element.target.dependsOn",
"Contract.term.group": "Contract.term",
"ExplanationOfBenefit.addItem.adjudication": "ExplanationOfBenefit.item.adjudication",
"ExplanationOfBenefit.addItem.detail.adjudication": "ExplanationOfBenefit.item.adjudication",
"ExplanationOfBenefit.item.detail.adjudication": "ExplanationOfBenefit.item.adjudication",
"ExplanationOfBenefit.item.detail.subDetail.adjudication": "ExplanationOfBenefit.item.adjudication",
"GraphDefinition.link.target.link": "GraphDefinition.link",
"ImplementationGuide.page.page": "ImplementationGuide.page",
"Observation.component.referenceRange": "Observation.referenceRange",
"OperationDefinition.parameter.part": "OperationDefinition.parameter",
"Parameters.parameter.part": "Parameters.parameter",
"PlanDefinition.action.action": "PlanDefinition.action",
"Provenance.entity.agent": "Provenance.agent",
"Questionnaire.item.item": "Questionnaire.item",
"QuestionnaireResponse.item.answer.item": "QuestionnaireResponse.item",
"QuestionnaireResponse.item.item": "QuestionnaireResponse.item",
"RequestGroup.action.action": "RequestGroup.action",
"StructureMap.group.rule.rule": "StructureMap.group.rule",
"TestReport.teardown.action.operation": "TestReport.setup.action.operation",
"TestReport.test.action.assert": "TestReport.setup.action.assert",
"TestReport.test.action.operation": "TestReport.setup.action.operation",
"TestScript.teardown.action.operation": "TestScript.setup.action.operation",
"TestScript.test.action.assert": "TestScript.setup.action.assert",
"TestScript.test.action.operation": "TestScript.setup.action.operation",
"ValueSet.compose.exclude": "ValueSet.compose.include",
"ValueSet.expansion.contains.contains": "ValueSet.expansion.contains",
"ValueSet.expansion.contains.designation": "ValueSet.compose.include.concept.designation"
}
Loading

0 comments on commit 259f7aa

Please sign in to comment.