From bbcd9a7628f06d23e63111ba33846084183790f8 Mon Sep 17 00:00:00 2001 From: Julia Afeltra <30803904+jafeltra@users.noreply.github.com> Date: Thu, 14 Nov 2019 20:29:36 -0500 Subject: [PATCH] Add OnlyRule and corresponding tests (#21) --- src/export/StructureDefinitionExporter.ts | 5 +- .../StructureDefinitionExporter.test.ts | 124 +++++++++++++++++- 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/src/export/StructureDefinitionExporter.ts b/src/export/StructureDefinitionExporter.ts index 79106bad2..d68c57a92 100644 --- a/src/export/StructureDefinitionExporter.ts +++ b/src/export/StructureDefinitionExporter.ts @@ -3,7 +3,7 @@ import { StructureDefinition, ElementDefinitionBindingStrength } from '../fhirty import { Profile, Extension } from '../fshtypes'; import { FSHTank } from '../import'; import { ParentNotDefinedError } from '../errors/ParentNotDefinedError'; -import { CardRule, FlagRule, ValueSetRule } from '../fshtypes/rules'; +import { CardRule, FlagRule, OnlyRule, ValueSetRule } from '../fshtypes/rules'; import { logger } from '../utils/FSHLogger'; /** @@ -46,6 +46,9 @@ export class StructureDefinitionExporter { element.constrainCardinality(rule.min, rule.max); } else if (rule instanceof FlagRule) { element.applyFlags(rule.mustSupport, rule.summary, rule.modifier); + } else if (rule instanceof OnlyRule) { + const target = structDef.getReferenceName(rule.path, element); + element.constrainType(rule.types, this.resolve.bind(this), target); } else if (rule instanceof ValueSetRule) { element.bindToVS(rule.valueSet, rule.strength as ElementDefinitionBindingStrength); } diff --git a/test/export/StructureDefinitionExporter.test.ts b/test/export/StructureDefinitionExporter.test.ts index ea8041490..ed93dd11f 100644 --- a/test/export/StructureDefinitionExporter.test.ts +++ b/test/export/StructureDefinitionExporter.test.ts @@ -2,7 +2,7 @@ import { StructureDefinitionExporter } from '../../src/export'; import { FSHTank, FSHDocument } from '../../src/import'; import { FHIRDefinitions, load } from '../../src/fhirdefs'; import { Profile, Extension } from '../../src/fshtypes'; -import { CardRule, FlagRule, ValueSetRule } from '../../src/fshtypes/rules'; +import { CardRule, FlagRule, OnlyRule, ValueSetRule } from '../../src/fshtypes/rules'; describe('StructureDefinitionExporter', () => { let defs: FHIRDefinitions; @@ -296,6 +296,128 @@ describe('StructureDefinitionExporter', () => { expect(changedElement.binding.strength).toBe('preferred'); }); + // Only Rule + it('should apply a correct OnlyRule on a non-reference choice', () => { + const profile = new Profile('Foo'); + profile.parent = 'Observation'; + + const rule = new OnlyRule('value[x]'); + rule.types = [{ type: 'string' }]; + profile.rules.push(rule); + + const sd = exporter.exportStructDef(profile, input); + const baseStructDef = sd.getBaseStructureDefinition(); + + const baseValue = baseStructDef.findElement('Observation.value[x]'); + const constrainedValue = sd.findElement('Observation.value[x]'); + + expect(baseValue.type).toHaveLength(11); + expect(baseValue.type[0]).toEqual({ code: 'Quantity' }); + expect(baseValue.type[1]).toEqual({ code: 'CodeableConcept' }); + expect(baseValue.type[2]).toEqual({ code: 'string' }); + + expect(constrainedValue.type).toHaveLength(1); + expect(constrainedValue.type[0]).toEqual({ code: 'string' }); + }); + + it('should apply a correct OnlyRule on a reference', () => { + const profile = new Profile('Foo'); + profile.parent = 'Observation'; + + const rule = new OnlyRule('subject'); + rule.types = [{ type: 'Device', isReference: true }]; + profile.rules.push(rule); + + const sd = exporter.exportStructDef(profile, input); + const baseStructDef = sd.getBaseStructureDefinition(); + + const baseSubject = baseStructDef.findElement('Observation.subject'); + const constrainedSubject = sd.findElement('Observation.subject'); + + expect(baseSubject.type).toHaveLength(1); + expect(baseSubject.type).toEqual([ + { + code: 'Reference', + targetProfile: [ + 'http://hl7.org/fhir/StructureDefinition/Patient', + 'http://hl7.org/fhir/StructureDefinition/Group', + 'http://hl7.org/fhir/StructureDefinition/Device', + 'http://hl7.org/fhir/StructureDefinition/Location' + ] + } + ]); + + expect(constrainedSubject.type).toHaveLength(1); + expect(constrainedSubject.type).toEqual([ + { + code: 'Reference', + targetProfile: ['http://hl7.org/fhir/StructureDefinition/Device'] + } + ]); + }); + + it('should apply a correct OnlyRule with a specific target constrained', () => { + const profile = new Profile('Foo'); + profile.parent = 'Observation'; + + const rule = new OnlyRule('hasMember[Observation]'); + rule.types = [ + { type: 'http://hl7.org/fhir/StructureDefinition/bodyheight', isReference: true }, + { type: 'http://hl7.org/fhir/StructureDefinition/bodyweight', isReference: true } + ]; + profile.rules.push(rule); + + const sd = exporter.exportStructDef(profile, input); + const baseStructDef = sd.getBaseStructureDefinition(); + + const baseHasMember = baseStructDef.findElement('Observation.hasMember'); + const constrainedHasMember = sd.findElement('Observation.hasMember'); + + expect(baseHasMember.type).toHaveLength(1); + expect(baseHasMember.type).toEqual([ + { + code: 'Reference', + targetProfile: [ + 'http://hl7.org/fhir/StructureDefinition/Observation', + 'http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse', + 'http://hl7.org/fhir/StructureDefinition/MolecularSequence' + ] + } + ]); + + expect(constrainedHasMember.type).toHaveLength(1); + expect(constrainedHasMember.type).toEqual([ + { + code: 'Reference', + targetProfile: [ + 'http://hl7.org/fhir/StructureDefinition/bodyheight', + 'http://hl7.org/fhir/StructureDefinition/bodyweight', + 'http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse', + 'http://hl7.org/fhir/StructureDefinition/MolecularSequence' + ] + } + ]); + }); + + it('should not apply an incorrect OnlyRule', () => { + // TODO: this should check for emitting an error once logging is set up + const profile = new Profile('Foo'); + profile.parent = 'Observation'; + + const rule = new OnlyRule('value[x]'); + rule.types = [{ type: 'instant' }]; + profile.rules.push(rule); + + const sd = exporter.exportStructDef(profile, input); + const baseStructDef = sd.getBaseStructureDefinition(); + + const baseValue = baseStructDef.findElement('Observation.value[x]'); + const constrainedValue = sd.findElement('Observation.value[x]'); + + expect(baseValue.type).toHaveLength(11); + expect(constrainedValue.type).toHaveLength(11); + }); + // toJSON it('should correctly generate a diff containing only changed elements', () => { // We already have separate tests for the differentials, so this just ensures that the