Skip to content

Commit

Permalink
Merge branch 'master' into support-fhir-r6
Browse files Browse the repository at this point in the history
  • Loading branch information
cmoesel authored Oct 18, 2024
2 parents 3417bd3 + c10b13b commit 72ea8f5
Show file tree
Hide file tree
Showing 4 changed files with 669 additions and 32 deletions.
11 changes: 9 additions & 2 deletions src/optimizer/plugins/ConstructInlineInstanceOptimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion src/optimizer/plugins/SimplifyInstanceNameOptimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
Expand Down
274 changes: 273 additions & 1 deletion test/optimizer/plugins/ConstructInlineInstanceOptimizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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('');
Expand All @@ -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', () => {
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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]);
});
});
});
});
Loading

0 comments on commit 72ea8f5

Please sign in to comment.