From c10b13b8d943473de422a309bbe585deb5daac69 Mon Sep 17 00:00:00 2001 From: Mint Thompson Date: Fri, 18 Oct 2024 10:45:43 -0400 Subject: [PATCH] Construct inline instances from within more FSH entity types (#270) Logical models, Resources, CodeSystems, and ValueSets are now checked for rules that could be used to define an inline instance. Since this means those entities will now contain rules that assign inline instances, check those entity types when simplifying instance names. --- .../ConstructInlineInstanceOptimizer.ts | 11 +- .../plugins/SimplifyInstanceNameOptimizer.ts | 10 +- .../ConstructInlineInstanceOptimizer.test.ts | 274 +++++++++++- .../SimplifyInstanceNameOptimizer.test.ts | 406 ++++++++++++++++-- 4 files changed, 669 insertions(+), 32 deletions(-) diff --git a/src/optimizer/plugins/ConstructInlineInstanceOptimizer.ts b/src/optimizer/plugins/ConstructInlineInstanceOptimizer.ts index 89ad9d7b..f610f081 100644 --- a/src/optimizer/plugins/ConstructInlineInstanceOptimizer.ts +++ b/src/optimizer/plugins/ConstructInlineInstanceOptimizer.ts @@ -31,9 +31,16 @@ export default { optimize(pkg: Package, fisher: MasterFisher, options: ProcessingOptions = {}): void { const inlineInstances: ExportableInstance[] = []; - // Only construct inline instances in instances, profiles, and extensions. // If we add support for other types, update SimplifyInstanceNameOptimizer as well. - [...pkg.instances, ...pkg.profiles, ...pkg.extensions].forEach(resource => { + [ + ...pkg.instances, + ...pkg.profiles, + ...pkg.extensions, + ...pkg.logicals, + ...pkg.resources, + ...pkg.codeSystems, + ...pkg.valueSets + ].forEach(resource => { const ruleType = resource instanceof ExportableInstance ? ExportableAssignmentRule diff --git a/src/optimizer/plugins/SimplifyInstanceNameOptimizer.ts b/src/optimizer/plugins/SimplifyInstanceNameOptimizer.ts index d469c08e..74235088 100644 --- a/src/optimizer/plugins/SimplifyInstanceNameOptimizer.ts +++ b/src/optimizer/plugins/SimplifyInstanceNameOptimizer.ts @@ -35,7 +35,15 @@ export default { }); // Fix up inline assignments in types that may have them (see: ConstructInlineInstanceOptimizer) - [...pkg.instances, ...pkg.profiles, ...pkg.extensions].forEach(resource => { + [ + ...pkg.instances, + ...pkg.profiles, + ...pkg.extensions, + ...pkg.logicals, + ...pkg.resources, + ...pkg.codeSystems, + ...pkg.valueSets + ].forEach(resource => { const inlineAssignmentRules = resource.rules.filter( rule => (rule instanceof ExportableAssignmentRule || rule instanceof ExportableCaretValueRule) && diff --git a/test/optimizer/plugins/ConstructInlineInstanceOptimizer.test.ts b/test/optimizer/plugins/ConstructInlineInstanceOptimizer.test.ts index 30290e07..97dc604b 100644 --- a/test/optimizer/plugins/ConstructInlineInstanceOptimizer.test.ts +++ b/test/optimizer/plugins/ConstructInlineInstanceOptimizer.test.ts @@ -5,9 +5,13 @@ import { Package } from '../../../src/processor'; import { ExportableAssignmentRule, ExportableCaretValueRule, + ExportableCodeSystem, ExportableExtension, ExportableInstance, - ExportableProfile + ExportableLogical, + ExportableProfile, + ExportableResource, + ExportableValueSet } from '../../../src/exportable'; import optimizer from '../../../src/optimizer/plugins/ConstructInlineInstanceOptimizer'; import { @@ -451,6 +455,8 @@ describe('optimizer', () => { let containedId2: ExportableCaretValueRule; let profile: ExportableProfile; let extension: ExportableExtension; + let logical: ExportableLogical; + let resource: ExportableResource; beforeEach(() => { containedResourceType1 = new ExportableCaretValueRule(''); @@ -471,6 +477,10 @@ describe('optimizer', () => { profile.parent = 'Patient'; extension = new ExportableExtension('FooExtension'); + + logical = new ExportableLogical('Pretzel'); + + resource = new ExportableResource('Toast'); }); it('should create inline instances from contained resources on a profile', () => { @@ -557,6 +567,90 @@ describe('optimizer', () => { expect(extension.rules).toEqual([inlineInstanceRule1, inlineInstanceRule2]); }); + it('should create inline instances from contained resources on a logical', () => { + logical.rules = [ + containedResourceType1, + containedId1, + containedResourceType2, + containedId2 + ]; + const myPackage = new Package(); + myPackage.add(logical); + optimizer.optimize(myPackage, fisher); + + expect(myPackage.instances).toHaveLength(2); + assertExportableInstance( + myPackage.instances[0], + 'Bar', + 'Observation', + 'Inline', + undefined, + undefined, + [] + ); + assertExportableInstance( + myPackage.instances[1], + 'Baz', + 'ValueSet', + 'Inline', + undefined, + undefined, + [] + ); + + const inlineInstanceRule1 = new ExportableCaretValueRule(''); + inlineInstanceRule1.caretPath = 'contained[0]'; + inlineInstanceRule1.value = 'Bar'; + inlineInstanceRule1.isInstance = true; + const inlineInstanceRule2 = new ExportableCaretValueRule(''); + inlineInstanceRule2.caretPath = 'contained[1]'; + inlineInstanceRule2.value = 'Baz'; + inlineInstanceRule2.isInstance = true; + expect(logical.rules).toEqual([inlineInstanceRule1, inlineInstanceRule2]); + }); + + it('should create inline instances from contained resources on a resource', () => { + resource.rules = [ + containedResourceType1, + containedId1, + containedResourceType2, + containedId2 + ]; + const myPackage = new Package(); + myPackage.add(resource); + optimizer.optimize(myPackage, fisher); + + expect(myPackage.instances).toHaveLength(2); + assertExportableInstance( + myPackage.instances[0], + 'Bar', + 'Observation', + 'Inline', + undefined, + undefined, + [] + ); + assertExportableInstance( + myPackage.instances[1], + 'Baz', + 'ValueSet', + 'Inline', + undefined, + undefined, + [] + ); + + const inlineInstanceRule1 = new ExportableCaretValueRule(''); + inlineInstanceRule1.caretPath = 'contained[0]'; + inlineInstanceRule1.value = 'Bar'; + inlineInstanceRule1.isInstance = true; + const inlineInstanceRule2 = new ExportableCaretValueRule(''); + inlineInstanceRule2.caretPath = 'contained[1]'; + inlineInstanceRule2.value = 'Baz'; + inlineInstanceRule2.isInstance = true; + expect(resource.rules).toEqual([inlineInstanceRule1, inlineInstanceRule2]); + }); + it('should create an inline instance from a contained resource with non-numeric paths', () => { containedResourceType1.caretPath = 'contained.resourceType'; containedId1.caretPath = 'contained.id'; @@ -1118,5 +1212,183 @@ describe('optimizer', () => { expect(profile.rules).toEqual([inlineInstanceRule1]); }); }); + + describe('ValueSets', () => { + // All FSH types that use Caret rules work the same in this optimizer, + // so a limited subset of the tests from StructureDefinitions are used here. + let containedResourceType1: ExportableCaretValueRule; + let containedId1: ExportableCaretValueRule; + let valueSet: ExportableValueSet; + + beforeEach(() => { + containedResourceType1 = new ExportableCaretValueRule(''); + containedResourceType1.caretPath = 'contained[0].resourceType'; + containedResourceType1.value = 'Observation'; + containedId1 = new ExportableCaretValueRule(''); + containedId1.caretPath = 'contained[0].id'; + containedId1.value = 'Bar'; + + valueSet = new ExportableValueSet('MyValueSet'); + }); + + it('should create an inline instance from a contained resource with additional rules', () => { + const containedString = new ExportableCaretValueRule(''); + containedString.caretPath = 'contained[0].valueString'; + containedString.value = 'string value'; + valueSet.rules = [containedResourceType1, containedId1, containedString]; + const myPackage = new Package(); + myPackage.add(valueSet); + optimizer.optimize(myPackage, fisher); + + expect(myPackage.instances).toHaveLength(1); + const expectedRule = new ExportableAssignmentRule('valueString'); + expectedRule.value = 'string value'; + assertExportableInstance( + myPackage.instances[0], + 'Bar', + 'Observation', + 'Inline', + undefined, + undefined, + [expectedRule] + ); + + const inlineInstanceRule1 = new ExportableCaretValueRule(''); + inlineInstanceRule1.caretPath = 'contained[0]'; + inlineInstanceRule1.value = 'Bar'; + inlineInstanceRule1.isInstance = true; + expect(valueSet.rules).toEqual([inlineInstanceRule1]); + }); + + it('should create a profiled inline instance from a contained resource with multiple profiles when the metaProfile option is first', () => { + const containedProfile1 = new ExportableCaretValueRule(''); + containedProfile1.caretPath = 'contained[0].meta.profile[0]'; + containedProfile1.value = 'http://hl7.org/fhir/StructureDefinition/vitalsigns'; + const containedProfile2 = new ExportableCaretValueRule(''); + containedProfile2.caretPath = 'contained[0].meta.profile[1]'; + containedProfile2.value = + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab'; + valueSet.rules = [ + containedResourceType1, + containedId1, + containedProfile1, + containedProfile2 + ]; + const myPackage = new Package(); + myPackage.add(valueSet); + optimizer.optimize(myPackage, fisher, { metaProfile: 'first' }); + + expect(myPackage.instances).toHaveLength(1); + const profileRule1 = new ExportableAssignmentRule('meta.profile[0]'); + profileRule1.value = + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab'; + assertExportableInstance( + myPackage.instances[0], + 'Bar', + 'http://hl7.org/fhir/StructureDefinition/vitalsigns', + 'Inline', + undefined, + undefined, + [profileRule1] + ); + // Even though the second entry in meta.profile won't resolve, we don't try to resolve them when there is more than one. + expect(loggerSpy.getAllMessages('warn')).toHaveLength(0); + + const inlineInstanceRule1 = new ExportableCaretValueRule(''); + inlineInstanceRule1.caretPath = 'contained[0]'; + inlineInstanceRule1.value = 'Bar'; + inlineInstanceRule1.isInstance = true; + expect(valueSet.rules).toEqual([inlineInstanceRule1]); + }); + }); + + describe('CodeSystems', () => { + // All FSH types that use Caret rules work the same in this optimizer, + // so a limited subset of the tests from StructureDefinitions are used here. + let containedResourceType1: ExportableCaretValueRule; + let containedId1: ExportableCaretValueRule; + let codeSystem: ExportableCodeSystem; + + beforeEach(() => { + containedResourceType1 = new ExportableCaretValueRule(''); + containedResourceType1.caretPath = 'contained[0].resourceType'; + containedResourceType1.value = 'Observation'; + containedId1 = new ExportableCaretValueRule(''); + containedId1.caretPath = 'contained[0].id'; + containedId1.value = 'Bar'; + + codeSystem = new ExportableCodeSystem('MyCodeSystem'); + }); + + it('should create an inline instance from a contained resource with additional rules', () => { + const containedString = new ExportableCaretValueRule(''); + containedString.caretPath = 'contained[0].valueString'; + containedString.value = 'string value'; + codeSystem.rules = [containedResourceType1, containedId1, containedString]; + const myPackage = new Package(); + myPackage.add(codeSystem); + optimizer.optimize(myPackage, fisher); + + expect(myPackage.instances).toHaveLength(1); + const expectedRule = new ExportableAssignmentRule('valueString'); + expectedRule.value = 'string value'; + assertExportableInstance( + myPackage.instances[0], + 'Bar', + 'Observation', + 'Inline', + undefined, + undefined, + [expectedRule] + ); + + const inlineInstanceRule1 = new ExportableCaretValueRule(''); + inlineInstanceRule1.caretPath = 'contained[0]'; + inlineInstanceRule1.value = 'Bar'; + inlineInstanceRule1.isInstance = true; + expect(codeSystem.rules).toEqual([inlineInstanceRule1]); + }); + + it('should create a profiled inline instance from a contained resource with multiple profiles when the metaProfile option is first', () => { + const containedProfile1 = new ExportableCaretValueRule(''); + containedProfile1.caretPath = 'contained[0].meta.profile[0]'; + containedProfile1.value = 'http://hl7.org/fhir/StructureDefinition/vitalsigns'; + const containedProfile2 = new ExportableCaretValueRule(''); + containedProfile2.caretPath = 'contained[0].meta.profile[1]'; + containedProfile2.value = + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab'; + codeSystem.rules = [ + containedResourceType1, + containedId1, + containedProfile1, + containedProfile2 + ]; + const myPackage = new Package(); + myPackage.add(codeSystem); + optimizer.optimize(myPackage, fisher, { metaProfile: 'first' }); + + expect(myPackage.instances).toHaveLength(1); + const profileRule1 = new ExportableAssignmentRule('meta.profile[0]'); + profileRule1.value = + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab'; + assertExportableInstance( + myPackage.instances[0], + 'Bar', + 'http://hl7.org/fhir/StructureDefinition/vitalsigns', + 'Inline', + undefined, + undefined, + [profileRule1] + ); + // Even though the second entry in meta.profile won't resolve, we don't try to resolve them when there is more than one. + expect(loggerSpy.getAllMessages('warn')).toHaveLength(0); + + const inlineInstanceRule1 = new ExportableCaretValueRule(''); + inlineInstanceRule1.caretPath = 'contained[0]'; + inlineInstanceRule1.value = 'Bar'; + inlineInstanceRule1.isInstance = true; + expect(codeSystem.rules).toEqual([inlineInstanceRule1]); + }); + }); }); }); diff --git a/test/optimizer/plugins/SimplifyInstanceNameOptimizer.test.ts b/test/optimizer/plugins/SimplifyInstanceNameOptimizer.test.ts index 369f8b89..97a64b41 100644 --- a/test/optimizer/plugins/SimplifyInstanceNameOptimizer.test.ts +++ b/test/optimizer/plugins/SimplifyInstanceNameOptimizer.test.ts @@ -4,8 +4,13 @@ import { Package } from '../../../src/processor/Package'; import { ExportableAssignmentRule, ExportableCaretValueRule, + ExportableCodeSystem, + ExportableExtension, ExportableInstance, - ExportableProfile + ExportableLogical, + ExportableProfile, + ExportableResource, + ExportableValueSet } from '../../../src/exportable'; import { MasterFisher } from '../../../src/utils'; import { loadTestDefinitions, stockLake } from '../../helpers'; @@ -134,7 +139,7 @@ describe('optimizer', () => { expect(myPackage.instances).toEqual([expectedInstance1, expectedInstance2, expectedBundle]); }); - it('should update inlined contained rules in a profile when the inline instance name changes', () => { + it('should update inlined contained rules in a profile when the contained instance name changes', () => { const instance1 = new ExportableInstance('AmazingThings'); instance1.instanceOf = 'CodeSystem'; instance1.name = 'AmazingThings-of-CodeSystem'; @@ -146,19 +151,19 @@ describe('optimizer', () => { instance3.name = 'OtherAmazingThings-of-ValueSet'; const profile = new ExportableProfile('MyObservationProfile'); profile.parent = 'Observation'; - const profileContainedRule1 = new ExportableCaretValueRule(''); - profileContainedRule1.caretPath = 'contained[0]'; - profileContainedRule1.isInstance = true; - profileContainedRule1.value = 'AmazingThings-of-CodeSystem'; - const profileContainedRule2 = new ExportableCaretValueRule(''); - profileContainedRule2.caretPath = 'contained[1]'; - profileContainedRule2.isInstance = true; - profileContainedRule2.value = 'AmazingThings-of-ValueSet'; - const profileContainedRule3 = new ExportableCaretValueRule(''); - profileContainedRule3.caretPath = 'contained[2]'; - profileContainedRule3.isInstance = true; - profileContainedRule3.value = 'OtherAmazingThings-of-ValueSet'; - profile.rules = [profileContainedRule1, profileContainedRule2, profileContainedRule3]; + const containedRule1 = new ExportableCaretValueRule(''); + containedRule1.caretPath = 'contained[0]'; + containedRule1.isInstance = true; + containedRule1.value = 'AmazingThings-of-CodeSystem'; + const containedRule2 = new ExportableCaretValueRule(''); + containedRule2.caretPath = 'contained[1]'; + containedRule2.isInstance = true; + containedRule2.value = 'AmazingThings-of-ValueSet'; + const containedRule3 = new ExportableCaretValueRule(''); + containedRule3.caretPath = 'contained[2]'; + containedRule3.isInstance = true; + containedRule3.value = 'OtherAmazingThings-of-ValueSet'; + profile.rules = [containedRule1, containedRule2, containedRule3]; const myPackage = new Package(); myPackage.add(instance1); myPackage.add(instance2); @@ -184,19 +189,19 @@ describe('optimizer', () => { // Now check that names have been updated in inline assignments const expectedProfile = new ExportableProfile('MyObservationProfile'); expectedProfile.parent = 'Observation'; - const expectedProfileRule1 = new ExportableCaretValueRule(''); - expectedProfileRule1.caretPath = 'contained[0]'; - expectedProfileRule1.isInstance = true; - expectedProfileRule1.value = 'AmazingThings-of-CodeSystem'; // This name did not get changed - const expectedProfileRule2 = new ExportableCaretValueRule(''); - expectedProfileRule2.caretPath = 'contained[1]'; - expectedProfileRule2.isInstance = true; - expectedProfileRule2.value = 'AmazingThings-of-ValueSet'; // This name did not get changed - const expectedProfileRule3 = new ExportableCaretValueRule(''); - expectedProfileRule3.caretPath = 'contained[2]'; - expectedProfileRule3.isInstance = true; - expectedProfileRule3.value = 'OtherAmazingThings'; // This name was changed - expectedProfile.rules = [expectedProfileRule1, expectedProfileRule2, expectedProfileRule3]; + const expectedRule1 = new ExportableCaretValueRule(''); + expectedRule1.caretPath = 'contained[0]'; + expectedRule1.isInstance = true; + expectedRule1.value = 'AmazingThings-of-CodeSystem'; // This name did not get changed + const expectedRule2 = new ExportableCaretValueRule(''); + expectedRule2.caretPath = 'contained[1]'; + expectedRule2.isInstance = true; + expectedRule2.value = 'AmazingThings-of-ValueSet'; // This name did not get changed + const expectedRule3 = new ExportableCaretValueRule(''); + expectedRule3.caretPath = 'contained[2]'; + expectedRule3.isInstance = true; + expectedRule3.value = 'OtherAmazingThings'; // This name was changed + expectedProfile.rules = [expectedRule1, expectedRule2, expectedRule3]; expect(myPackage.instances).toEqual([ expectedInstance1, expectedInstance2, @@ -205,6 +210,351 @@ describe('optimizer', () => { expect(myPackage.profiles).toEqual([expectedProfile]); }); + it('should update inlined contained rules in an extension when the contained instance name changes', () => { + const instance1 = new ExportableInstance('AmazingThings'); + instance1.instanceOf = 'CodeSystem'; + instance1.name = 'AmazingThings-of-CodeSystem'; + const instance2 = new ExportableInstance('AmazingThings'); + instance2.instanceOf = 'ValueSet'; + instance2.name = 'AmazingThings-of-ValueSet'; + const instance3 = new ExportableInstance('OtherAmazingThings'); + instance3.instanceOf = 'ValueSet'; + instance3.name = 'OtherAmazingThings-of-ValueSet'; + const extension = new ExportableExtension('MyUsefulExtension'); + const containedRule1 = new ExportableCaretValueRule(''); + containedRule1.caretPath = 'contained[0]'; + containedRule1.isInstance = true; + containedRule1.value = 'AmazingThings-of-CodeSystem'; + const containedRule2 = new ExportableCaretValueRule(''); + containedRule2.caretPath = 'contained[1]'; + containedRule2.isInstance = true; + containedRule2.value = 'AmazingThings-of-ValueSet'; + const containedRule3 = new ExportableCaretValueRule(''); + containedRule3.caretPath = 'contained[2]'; + containedRule3.isInstance = true; + containedRule3.value = 'OtherAmazingThings-of-ValueSet'; + extension.rules = [containedRule1, containedRule2, containedRule3]; + const myPackage = new Package(); + myPackage.add(instance1); + myPackage.add(instance2); + myPackage.add(instance3); + myPackage.add(extension); + optimizer.optimize(myPackage); + + // Check that instances were renamed when appropriate + const amazingThingsIdRule = new ExportableAssignmentRule('id'); + amazingThingsIdRule.value = 'AmazingThings'; + const expectedInstance1 = new ExportableInstance('AmazingThings'); + expectedInstance1.instanceOf = 'CodeSystem'; + expectedInstance1.name = 'AmazingThings-of-CodeSystem'; // No name simplification since it will conflict with other instances + expectedInstance1.rules = [amazingThingsIdRule]; + const expectedInstance2 = new ExportableInstance('AmazingThings'); + expectedInstance2.instanceOf = 'ValueSet'; + expectedInstance2.name = 'AmazingThings-of-ValueSet'; // No name simplification since it will conflict with other instances + expectedInstance2.rules = [amazingThingsIdRule]; + const expectedInstance3 = new ExportableInstance('OtherAmazingThings'); + expectedInstance3.instanceOf = 'ValueSet'; + expectedInstance3.name = 'OtherAmazingThings'; // Name simplification since it didn't conflict with any other instances + // NOTE: expectedInstance3 should not have id rule since name matches id + // Now check that names have been updated in inline assignments + const expectedExtension = new ExportableExtension('MyUsefulExtension'); + const expectedRule1 = new ExportableCaretValueRule(''); + expectedRule1.caretPath = 'contained[0]'; + expectedRule1.isInstance = true; + expectedRule1.value = 'AmazingThings-of-CodeSystem'; // This name did not get changed + const expectedRule2 = new ExportableCaretValueRule(''); + expectedRule2.caretPath = 'contained[1]'; + expectedRule2.isInstance = true; + expectedRule2.value = 'AmazingThings-of-ValueSet'; // This name did not get changed + const expectedRule3 = new ExportableCaretValueRule(''); + expectedRule3.caretPath = 'contained[2]'; + expectedRule3.isInstance = true; + expectedRule3.value = 'OtherAmazingThings'; // This name was changed + expectedExtension.rules = [expectedRule1, expectedRule2, expectedRule3]; + expect(myPackage.instances).toEqual([ + expectedInstance1, + expectedInstance2, + expectedInstance3 + ]); + expect(myPackage.extensions).toEqual([expectedExtension]); + }); + + it('should update inlined contained rules in a logical when the contained instance name changes', () => { + const instance1 = new ExportableInstance('AmazingThings'); + instance1.instanceOf = 'CodeSystem'; + instance1.name = 'AmazingThings-of-CodeSystem'; + const instance2 = new ExportableInstance('AmazingThings'); + instance2.instanceOf = 'ValueSet'; + instance2.name = 'AmazingThings-of-ValueSet'; + const instance3 = new ExportableInstance('OtherAmazingThings'); + instance3.instanceOf = 'ValueSet'; + instance3.name = 'OtherAmazingThings-of-ValueSet'; + const logical = new ExportableLogical('PretzelLogical'); + const containedRule1 = new ExportableCaretValueRule(''); + containedRule1.caretPath = 'contained[0]'; + containedRule1.isInstance = true; + containedRule1.value = 'AmazingThings-of-CodeSystem'; + const containedRule2 = new ExportableCaretValueRule(''); + containedRule2.caretPath = 'contained[1]'; + containedRule2.isInstance = true; + containedRule2.value = 'AmazingThings-of-ValueSet'; + const containedRule3 = new ExportableCaretValueRule(''); + containedRule3.caretPath = 'contained[2]'; + containedRule3.isInstance = true; + containedRule3.value = 'OtherAmazingThings-of-ValueSet'; + logical.rules = [containedRule1, containedRule2, containedRule3]; + const myPackage = new Package(); + myPackage.add(instance1); + myPackage.add(instance2); + myPackage.add(instance3); + myPackage.add(logical); + optimizer.optimize(myPackage); + + // Check that instances were renamed when appropriate + const amazingThingsIdRule = new ExportableAssignmentRule('id'); + amazingThingsIdRule.value = 'AmazingThings'; + const expectedInstance1 = new ExportableInstance('AmazingThings'); + expectedInstance1.instanceOf = 'CodeSystem'; + expectedInstance1.name = 'AmazingThings-of-CodeSystem'; // No name simplification since it will conflict with other instances + expectedInstance1.rules = [amazingThingsIdRule]; + const expectedInstance2 = new ExportableInstance('AmazingThings'); + expectedInstance2.instanceOf = 'ValueSet'; + expectedInstance2.name = 'AmazingThings-of-ValueSet'; // No name simplification since it will conflict with other instances + expectedInstance2.rules = [amazingThingsIdRule]; + const expectedInstance3 = new ExportableInstance('OtherAmazingThings'); + expectedInstance3.instanceOf = 'ValueSet'; + expectedInstance3.name = 'OtherAmazingThings'; // Name simplification since it didn't conflict with any other instances + // NOTE: expectedInstance3 should not have id rule since name matches id + // Now check that names have been updated in inline assignments + const expectedLogical = new ExportableLogical('PretzelLogical'); + const expectedRule1 = new ExportableCaretValueRule(''); + expectedRule1.caretPath = 'contained[0]'; + expectedRule1.isInstance = true; + expectedRule1.value = 'AmazingThings-of-CodeSystem'; // This name did not get changed + const expectedRule2 = new ExportableCaretValueRule(''); + expectedRule2.caretPath = 'contained[1]'; + expectedRule2.isInstance = true; + expectedRule2.value = 'AmazingThings-of-ValueSet'; // This name did not get changed + const expectedRule3 = new ExportableCaretValueRule(''); + expectedRule3.caretPath = 'contained[2]'; + expectedRule3.isInstance = true; + expectedRule3.value = 'OtherAmazingThings'; // This name was changed + expectedLogical.rules = [expectedRule1, expectedRule2, expectedRule3]; + expect(myPackage.instances).toEqual([ + expectedInstance1, + expectedInstance2, + expectedInstance3 + ]); + expect(myPackage.logicals).toEqual([expectedLogical]); + }); + + it('should update inlined contained rules in a resource when the contained instance name changes', () => { + const instance1 = new ExportableInstance('AmazingThings'); + instance1.instanceOf = 'CodeSystem'; + instance1.name = 'AmazingThings-of-CodeSystem'; + const instance2 = new ExportableInstance('AmazingThings'); + instance2.instanceOf = 'ValueSet'; + instance2.name = 'AmazingThings-of-ValueSet'; + const instance3 = new ExportableInstance('OtherAmazingThings'); + instance3.instanceOf = 'ValueSet'; + instance3.name = 'OtherAmazingThings-of-ValueSet'; + const resource = new ExportableResource('Toast'); + const containedRule1 = new ExportableCaretValueRule(''); + containedRule1.caretPath = 'contained[0]'; + containedRule1.isInstance = true; + containedRule1.value = 'AmazingThings-of-CodeSystem'; + const containedRule2 = new ExportableCaretValueRule(''); + containedRule2.caretPath = 'contained[1]'; + containedRule2.isInstance = true; + containedRule2.value = 'AmazingThings-of-ValueSet'; + const containedRule3 = new ExportableCaretValueRule(''); + containedRule3.caretPath = 'contained[2]'; + containedRule3.isInstance = true; + containedRule3.value = 'OtherAmazingThings-of-ValueSet'; + resource.rules = [containedRule1, containedRule2, containedRule3]; + const myPackage = new Package(); + myPackage.add(instance1); + myPackage.add(instance2); + myPackage.add(instance3); + myPackage.add(resource); + optimizer.optimize(myPackage); + + // Check that instances were renamed when appropriate + const amazingThingsIdRule = new ExportableAssignmentRule('id'); + amazingThingsIdRule.value = 'AmazingThings'; + const expectedInstance1 = new ExportableInstance('AmazingThings'); + expectedInstance1.instanceOf = 'CodeSystem'; + expectedInstance1.name = 'AmazingThings-of-CodeSystem'; // No name simplification since it will conflict with other instances + expectedInstance1.rules = [amazingThingsIdRule]; + const expectedInstance2 = new ExportableInstance('AmazingThings'); + expectedInstance2.instanceOf = 'ValueSet'; + expectedInstance2.name = 'AmazingThings-of-ValueSet'; // No name simplification since it will conflict with other instances + expectedInstance2.rules = [amazingThingsIdRule]; + const expectedInstance3 = new ExportableInstance('OtherAmazingThings'); + expectedInstance3.instanceOf = 'ValueSet'; + expectedInstance3.name = 'OtherAmazingThings'; // Name simplification since it didn't conflict with any other instances + // NOTE: expectedInstance3 should not have id rule since name matches id + // Now check that names have been updated in inline assignments + const expectedResource = new ExportableResource('Toast'); + const expectedRule1 = new ExportableCaretValueRule(''); + expectedRule1.caretPath = 'contained[0]'; + expectedRule1.isInstance = true; + expectedRule1.value = 'AmazingThings-of-CodeSystem'; // This name did not get changed + const expectedRule2 = new ExportableCaretValueRule(''); + expectedRule2.caretPath = 'contained[1]'; + expectedRule2.isInstance = true; + expectedRule2.value = 'AmazingThings-of-ValueSet'; // This name did not get changed + const expectedRule3 = new ExportableCaretValueRule(''); + expectedRule3.caretPath = 'contained[2]'; + expectedRule3.isInstance = true; + expectedRule3.value = 'OtherAmazingThings'; // This name was changed + expectedResource.rules = [expectedRule1, expectedRule2, expectedRule3]; + expect(myPackage.instances).toEqual([ + expectedInstance1, + expectedInstance2, + expectedInstance3 + ]); + expect(myPackage.resources).toEqual([expectedResource]); + }); + + it('should update inlined contained rules in a code system when the contained instance name changes', () => { + const instance1 = new ExportableInstance('AmazingThings'); + instance1.instanceOf = 'CodeSystem'; + instance1.name = 'AmazingThings-of-CodeSystem'; + const instance2 = new ExportableInstance('AmazingThings'); + instance2.instanceOf = 'ValueSet'; + instance2.name = 'AmazingThings-of-ValueSet'; + const instance3 = new ExportableInstance('OtherAmazingThings'); + instance3.instanceOf = 'ValueSet'; + instance3.name = 'OtherAmazingThings-of-ValueSet'; + const codeSystem = new ExportableCodeSystem('MyCodeSystem'); + const containedRule1 = new ExportableCaretValueRule(''); + containedRule1.caretPath = 'contained[0]'; + containedRule1.isInstance = true; + containedRule1.value = 'AmazingThings-of-CodeSystem'; + const containedRule2 = new ExportableCaretValueRule(''); + containedRule2.caretPath = 'contained[1]'; + containedRule2.isInstance = true; + containedRule2.value = 'AmazingThings-of-ValueSet'; + const containedRule3 = new ExportableCaretValueRule(''); + containedRule3.caretPath = 'contained[2]'; + containedRule3.isInstance = true; + containedRule3.value = 'OtherAmazingThings-of-ValueSet'; + codeSystem.rules = [containedRule1, containedRule2, containedRule3]; + const myPackage = new Package(); + myPackage.add(instance1); + myPackage.add(instance2); + myPackage.add(instance3); + myPackage.add(codeSystem); + optimizer.optimize(myPackage); + + // Check that instances were renamed when appropriate + const amazingThingsIdRule = new ExportableAssignmentRule('id'); + amazingThingsIdRule.value = 'AmazingThings'; + const expectedInstance1 = new ExportableInstance('AmazingThings'); + expectedInstance1.instanceOf = 'CodeSystem'; + expectedInstance1.name = 'AmazingThings-of-CodeSystem'; // No name simplification since it will conflict with other instances + expectedInstance1.rules = [amazingThingsIdRule]; + const expectedInstance2 = new ExportableInstance('AmazingThings'); + expectedInstance2.instanceOf = 'ValueSet'; + expectedInstance2.name = 'AmazingThings-of-ValueSet'; // No name simplification since it will conflict with other instances + expectedInstance2.rules = [amazingThingsIdRule]; + const expectedInstance3 = new ExportableInstance('OtherAmazingThings'); + expectedInstance3.instanceOf = 'ValueSet'; + expectedInstance3.name = 'OtherAmazingThings'; // Name simplification since it didn't conflict with any other instances + // NOTE: expectedInstance3 should not have id rule since name matches id + // Now check that names have been updated in inline assignments + const expectedCodeSystem = new ExportableCodeSystem('MyCodeSystem'); + const expectedRule1 = new ExportableCaretValueRule(''); + expectedRule1.caretPath = 'contained[0]'; + expectedRule1.isInstance = true; + expectedRule1.value = 'AmazingThings-of-CodeSystem'; // This name did not get changed + const expectedRule2 = new ExportableCaretValueRule(''); + expectedRule2.caretPath = 'contained[1]'; + expectedRule2.isInstance = true; + expectedRule2.value = 'AmazingThings-of-ValueSet'; // This name did not get changed + const expectedRule3 = new ExportableCaretValueRule(''); + expectedRule3.caretPath = 'contained[2]'; + expectedRule3.isInstance = true; + expectedRule3.value = 'OtherAmazingThings'; // This name was changed + expectedCodeSystem.rules = [expectedRule1, expectedRule2, expectedRule3]; + expect(myPackage.instances).toEqual([ + expectedInstance1, + expectedInstance2, + expectedInstance3 + ]); + expect(myPackage.codeSystems).toEqual([expectedCodeSystem]); + }); + + it('should update inlined contained rules in a value set when the contained instance name changes', () => { + const instance1 = new ExportableInstance('AmazingThings'); + instance1.instanceOf = 'CodeSystem'; + instance1.name = 'AmazingThings-of-CodeSystem'; + const instance2 = new ExportableInstance('AmazingThings'); + instance2.instanceOf = 'ValueSet'; + instance2.name = 'AmazingThings-of-ValueSet'; + const instance3 = new ExportableInstance('OtherAmazingThings'); + instance3.instanceOf = 'ValueSet'; + instance3.name = 'OtherAmazingThings-of-ValueSet'; + const valueSet = new ExportableValueSet('MyValueSet'); + const containedRule1 = new ExportableCaretValueRule(''); + containedRule1.caretPath = 'contained[0]'; + containedRule1.isInstance = true; + containedRule1.value = 'AmazingThings-of-CodeSystem'; + const containedRule2 = new ExportableCaretValueRule(''); + containedRule2.caretPath = 'contained[1]'; + containedRule2.isInstance = true; + containedRule2.value = 'AmazingThings-of-ValueSet'; + const containedRule3 = new ExportableCaretValueRule(''); + containedRule3.caretPath = 'contained[2]'; + containedRule3.isInstance = true; + containedRule3.value = 'OtherAmazingThings-of-ValueSet'; + valueSet.rules = [containedRule1, containedRule2, containedRule3]; + const myPackage = new Package(); + myPackage.add(instance1); + myPackage.add(instance2); + myPackage.add(instance3); + myPackage.add(valueSet); + optimizer.optimize(myPackage); + + // Check that instances were renamed when appropriate + const amazingThingsIdRule = new ExportableAssignmentRule('id'); + amazingThingsIdRule.value = 'AmazingThings'; + const expectedInstance1 = new ExportableInstance('AmazingThings'); + expectedInstance1.instanceOf = 'CodeSystem'; + expectedInstance1.name = 'AmazingThings-of-CodeSystem'; // No name simplification since it will conflict with other instances + expectedInstance1.rules = [amazingThingsIdRule]; + const expectedInstance2 = new ExportableInstance('AmazingThings'); + expectedInstance2.instanceOf = 'ValueSet'; + expectedInstance2.name = 'AmazingThings-of-ValueSet'; // No name simplification since it will conflict with other instances + expectedInstance2.rules = [amazingThingsIdRule]; + const expectedInstance3 = new ExportableInstance('OtherAmazingThings'); + expectedInstance3.instanceOf = 'ValueSet'; + expectedInstance3.name = 'OtherAmazingThings'; // Name simplification since it didn't conflict with any other instances + // NOTE: expectedInstance3 should not have id rule since name matches id + // Now check that names have been updated in inline assignments + const expectedValueSet = new ExportableValueSet('MyValueSet'); + const expectedRule1 = new ExportableCaretValueRule(''); + expectedRule1.caretPath = 'contained[0]'; + expectedRule1.isInstance = true; + expectedRule1.value = 'AmazingThings-of-CodeSystem'; // This name did not get changed + const expectedRule2 = new ExportableCaretValueRule(''); + expectedRule2.caretPath = 'contained[1]'; + expectedRule2.isInstance = true; + expectedRule2.value = 'AmazingThings-of-ValueSet'; // This name did not get changed + const expectedRule3 = new ExportableCaretValueRule(''); + expectedRule3.caretPath = 'contained[2]'; + expectedRule3.isInstance = true; + expectedRule3.value = 'OtherAmazingThings'; // This name was changed + expectedValueSet.rules = [expectedRule1, expectedRule2, expectedRule3]; + expect(myPackage.instances).toEqual([ + expectedInstance1, + expectedInstance2, + expectedInstance3 + ]); + expect(myPackage.valueSets).toEqual([expectedValueSet]); + }); + it('should still rename instances when there is a different instance with the same id but it is inline only', () => { const instance1 = new ExportableInstance('AmazingThings'); instance1.instanceOf = 'CodeSystem';