From 69e6238c1fdeed3e9e57794363a02d7b44fc57c3 Mon Sep 17 00:00:00 2001 From: Mint Thompson Date: Fri, 18 Aug 2023 09:00:37 -0400 Subject: [PATCH] Don't combine rules on inline ValueSet (#231) Construct inline instances before combining coding and Quantity values. This way, rules that assign to elements in a ValueSet instance that shouldn't be combined will be correctly detected as having a type that does not use combined rules. --- .../ConstructInlineInstanceOptimizer.ts | 4 ++- .../ConstructInlineInstanceOptimizer.test.ts | 4 ++- test/utils/Processing.test.ts | 30 ++++++++++++++++ .../contained-valueset/Observation-MyObs.json | 36 +++++++++++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 test/utils/fixtures/contained-valueset/Observation-MyObs.json diff --git a/src/optimizer/plugins/ConstructInlineInstanceOptimizer.ts b/src/optimizer/plugins/ConstructInlineInstanceOptimizer.ts index 7ae11ebf..89ad9d7b 100644 --- a/src/optimizer/plugins/ConstructInlineInstanceOptimizer.ts +++ b/src/optimizer/plugins/ConstructInlineInstanceOptimizer.ts @@ -15,6 +15,7 @@ import AddReferenceKeywordOptimizer from './AddReferenceKeywordOptimizer'; import SimplifyInstanceNameOptimizer from './SimplifyInstanceNameOptimizer'; import { MasterFisher, logger, ProcessingOptions } from '../../utils'; import { utils } from 'fsh-sushi'; +import CombineCodingAndQuantityValuesOptimizer from './CombineCodingAndQuantityValuesOptimizer'; export default { name: 'construct_inline_instance', @@ -24,7 +25,8 @@ export default { RemoveGeneratedTextRulesOptimizer.name, ResolveInstanceOfURLsOptimizer.name, AddReferenceKeywordOptimizer.name, - SimplifyInstanceNameOptimizer.name + SimplifyInstanceNameOptimizer.name, + CombineCodingAndQuantityValuesOptimizer.name ], optimize(pkg: Package, fisher: MasterFisher, options: ProcessingOptions = {}): void { diff --git a/test/optimizer/plugins/ConstructInlineInstanceOptimizer.test.ts b/test/optimizer/plugins/ConstructInlineInstanceOptimizer.test.ts index a0f005f0..30290e07 100644 --- a/test/optimizer/plugins/ConstructInlineInstanceOptimizer.test.ts +++ b/test/optimizer/plugins/ConstructInlineInstanceOptimizer.test.ts @@ -20,6 +20,7 @@ import AddReferenceKeywordOptimizer from '../../../src/optimizer/plugins/AddRefe import SimplifyInstanceNameOptimizer from '../../../src/optimizer/plugins/SimplifyInstanceNameOptimizer'; import { MasterFisher } from '../../../src/utils'; import { loadTestDefinitions, stockLake } from '../../helpers'; +import { CombineCodingAndQuantityValuesOptimizer } from '../../../src/optimizer/plugins'; describe('optimizer', () => { describe('#construct_inline_instance', () => { @@ -71,7 +72,8 @@ describe('optimizer', () => { RemoveGeneratedTextRulesOptimizer.name, ResolveInstanceOfURLsOptimizer.name, AddReferenceKeywordOptimizer.name, - SimplifyInstanceNameOptimizer.name + SimplifyInstanceNameOptimizer.name, + CombineCodingAndQuantityValuesOptimizer.name ]); expect(optimizer.runAfter).toBeUndefined(); }); diff --git a/test/utils/Processing.test.ts b/test/utils/Processing.test.ts index 5d11bcc2..cb944765 100644 --- a/test/utils/Processing.test.ts +++ b/test/utils/Processing.test.ts @@ -27,6 +27,7 @@ import { } from '../../src/exportable'; import { FHIRDefinitions, loadExternalDependencies } from '../../src/utils'; import * as loadOptimizers from '../../src/optimizer/loadOptimizers'; +import { FshCode } from 'fsh-sushi/dist/fshtypes'; let loadedPackages: string[] = []; @@ -384,6 +385,35 @@ describe('Processing', () => { expect(patient.usage).toBe('Example'); }); + it('should not combine rules on contained ValueSet.compose.include.concept', async () => { + const inDir = path.join(__dirname, 'fixtures', 'contained-valueset'); + const processor = await getFhirProcessor(inDir, loadTestDefinitions(), 'json-only'); + const config = processor.processConfig(); + const result = await getResources(processor, config); + expect(result.instances).toHaveLength(2); + const inlineVS = result.instances.find(i => i.id === 'MyInlineVS'); + expect(inlineVS?.usage).toBe('Inline'); + // * status = #active + // * compose.include.system = "http://example.org" + // * compose.include.concept[0].code = #123 + // * compose.include.concept[=].display = "one two three" + // * compose.include.concept[+].code = #456 + // * compose.include.concept[=].display = "four five six" + const statusRule = new ExportableAssignmentRule('status'); + statusRule.value = new FshCode('active'); + const systemRule = new ExportableAssignmentRule('compose.include.system'); + systemRule.value = 'http://example.org'; + const code0 = new ExportableAssignmentRule('compose.include.concept[0].code'); + code0.value = new FshCode('123'); + const display0 = new ExportableAssignmentRule('compose.include.concept[=].display'); + display0.value = 'one two three'; + const code1 = new ExportableAssignmentRule('compose.include.concept[+].code'); + code1.value = new FshCode('456'); + const display1 = new ExportableAssignmentRule('compose.include.concept[=].display'); + display1.value = 'four five six'; + expect(inlineVS.rules).toEqual([statusRule, systemRule, code0, display0, code1, display1]); + }); + describe('#enableOptimizers', () => { beforeEach(() => { // mock the call to loadOptimizers so that we get a custom set diff --git a/test/utils/fixtures/contained-valueset/Observation-MyObs.json b/test/utils/fixtures/contained-valueset/Observation-MyObs.json new file mode 100644 index 00000000..1a0b8605 --- /dev/null +++ b/test/utils/fixtures/contained-valueset/Observation-MyObs.json @@ -0,0 +1,36 @@ +{ + "resourceType": "Observation", + "id": "MyObs", + "status": "final", + "code": { + "coding": [ + { + "code": "something" + } + ] + }, + "contained": [ + { + "resourceType": "ValueSet", + "id": "MyInlineVS", + "status": "active", + "compose": { + "include": [ + { + "system": "http://example.org", + "concept": [ + { + "code": "123", + "display": "one two three" + }, + { + "code": "456", + "display": "four five six" + } + ] + } + ] + } + } + ] +}