diff --git a/src/extractor/OnlyRuleExtractor.ts b/src/extractor/OnlyRuleExtractor.ts index 878f7143..8b2450ed 100644 --- a/src/extractor/OnlyRuleExtractor.ts +++ b/src/extractor/OnlyRuleExtractor.ts @@ -1,3 +1,4 @@ +import { fshrules } from 'fsh-sushi'; import { ProcessableElementDefinition } from '../processor'; import { ExportableOnlyRule } from '../exportable'; import { getPath } from '../utils'; @@ -9,9 +10,17 @@ export class OnlyRuleExtractor { } const onlyRule = new ExportableOnlyRule(getPath(input)); input.type.forEach((t, i) => { - if (['Reference', 'CodeableReference'].includes(t.code) && t.targetProfile) { + if (['Reference', 'CodeableReference', 'canonical'].includes(t.code) && t.targetProfile) { + const targeting: Partial = {}; + if (t.code === 'Reference') { + targeting.isReference = true; + } else if (t.code === 'CodeableReference') { + targeting.isCodeableReference = true; + } else { + targeting.isCanonical = true; + } t.targetProfile.forEach((tp, tpi) => { - onlyRule.types.push({ type: tp, isReference: true }); + onlyRule.types.push(Object.assign({ type: tp }, targeting)); input.processedPaths.push(`type[${i}].targetProfile[${tpi}]`); }); } else if (t.profile) { diff --git a/test/extractor/OnlyRuleExtractor.test.ts b/test/extractor/OnlyRuleExtractor.test.ts index 1cf50385..0978482b 100644 --- a/test/extractor/OnlyRuleExtractor.test.ts +++ b/test/extractor/OnlyRuleExtractor.test.ts @@ -82,8 +82,11 @@ describe('OnlyRuleExtractor', () => { const onlyRule = OnlyRuleExtractor.process(element); const expectedRule = new ExportableOnlyRule('activity.performedActivity'); expectedRule.types = [ - { type: 'http://hl7.org/fhir/StructureDefinition/Observation', isReference: true }, - { type: 'http://hl7.org/fhir/StructureDefinition/MolecularSequence', isReference: true } + { type: 'http://hl7.org/fhir/StructureDefinition/Observation', isCodeableReference: true }, + { + type: 'http://hl7.org/fhir/StructureDefinition/MolecularSequence', + isCodeableReference: true + } ]; expect(onlyRule).toEqual(expectedRule); expect(element.processedPaths).toEqual([ @@ -107,6 +110,99 @@ describe('OnlyRuleExtractor', () => { expect(element.processedPaths).toEqual(['type[0].code', 'type[1].profile[0]', 'type[1].code']); }); + it('should extract an only rule with a subset of a canonical type', () => { + const element = ProcessableElementDefinition.fromJSON( + sdWithCodeableReference.differential.element[4] + ); + const onlyRule = OnlyRuleExtractor.process(element); + const expectedRule = new ExportableOnlyRule('extension[toast].value[x]'); + expectedRule.types = [ + { type: 'http://hl7.org/fhir/StructureDefinition/Observation', isCanonical: true }, + { + type: 'http://hl7.org/fhir/StructureDefinition/Patient', + isCanonical: true + } + ]; + expect(onlyRule).toEqual(expectedRule); + expect(element.processedPaths).toEqual([ + 'type[0].targetProfile[0]', + 'type[0].targetProfile[1]', + 'type[0].code' + ]); + }); + + it('should extract an only rule on an element with both Reference and CodeableReference types', () => { + const element = ProcessableElementDefinition.fromJSON( + sdWithCodeableReference.differential.element[2] + ); + const onlyRule = OnlyRuleExtractor.process(element); + const expectedRule = new ExportableOnlyRule('extension[bar].value[x]'); + expectedRule.types = [ + { type: 'http://hl7.org/fhir/StructureDefinition/Practitioner', isReference: true }, + { type: 'http://hl7.org/fhir/StructureDefinition/Patient', isReference: true }, + { type: 'http://hl7.org/fhir/StructureDefinition/Observation', isCodeableReference: true }, + { + type: 'http://hl7.org/fhir/StructureDefinition/DiagnosticReport', + isCodeableReference: true + } + ]; + expect(onlyRule).toEqual(expectedRule); + expect(element.processedPaths).toEqual([ + 'type[0].targetProfile[0]', + 'type[0].targetProfile[1]', + 'type[0].code', + 'type[1].targetProfile[0]', + 'type[1].targetProfile[1]', + 'type[1].code' + ]); + }); + + it('should extract an only rule on an element with CodeableReference and non-Reference types', () => { + const element = ProcessableElementDefinition.fromJSON( + sdWithCodeableReference.differential.element[3] + ); + const onlyRule = OnlyRuleExtractor.process(element); + const expectedRule = new ExportableOnlyRule('extension[cookie].value[x]'); + expectedRule.types = [ + { type: 'string' }, + { type: 'http://hl7.org/fhir/StructureDefinition/Observation', isCodeableReference: true }, + { + type: 'http://hl7.org/fhir/StructureDefinition/DiagnosticReport', + isCodeableReference: true + } + ]; + expect(onlyRule).toEqual(expectedRule); + expect(element.processedPaths).toEqual([ + 'type[0].code', + 'type[1].targetProfile[0]', + 'type[1].targetProfile[1]', + 'type[1].code' + ]); + }); + + it('should extract an only rule with canonical and non-canonical types', () => { + const element = ProcessableElementDefinition.fromJSON( + sdWithCodeableReference.differential.element[5] + ); + const onlyRule = OnlyRuleExtractor.process(element); + const expectedRule = new ExportableOnlyRule('extension[blank].value[x]'); + expectedRule.types = [ + { type: 'http://hl7.org/fhir/StructureDefinition/Patient', isCanonical: true }, + { + type: 'http://hl7.org/fhir/StructureDefinition/Observation', + isCanonical: true + }, + { type: 'Annotation' } + ]; + expect(onlyRule).toEqual(expectedRule); + expect(element.processedPaths).toEqual([ + 'type[0].targetProfile[0]', + 'type[0].targetProfile[1]', + 'type[0].code', + 'type[1].code' + ]); + }); + it('should return null when the element has no type info', () => { const element = ProcessableElementDefinition.fromJSON(looseSD.differential.element[4]); const onlyRule = OnlyRuleExtractor.process(element); diff --git a/test/extractor/fixtures/only-profile-with-codeablereference.json b/test/extractor/fixtures/only-profile-with-codeablereference.json index a086e774..3d9a88bf 100644 --- a/test/extractor/fixtures/only-profile-with-codeablereference.json +++ b/test/extractor/fixtures/only-profile-with-codeablereference.json @@ -34,6 +34,71 @@ "versioning": "either" } ] + }, + { + "id": "CarePlan.extension:bar.value[x]", + "path": "CarePlan.extension:bar.value[x]", + "type": [ + { + "code": "Reference", + "targetProfile": [ + "http://hl7.org/fhir/StructureDefinition/Practitioner", + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "code": "CodeableReference", + "targetProfile": [ + "http://hl7.org/fhir/StructureDefinition/Observation", + "http://hl7.org/fhir/StructureDefinition/DiagnosticReport" + ] + } + ] + }, + { + "id": "CarePlan.extension:cookie.value[x]", + "path": "CarePlan.extension:cookie.value[x]", + "type": [ + { + "code": "string" + }, + { + "code": "CodeableReference", + "targetProfile": [ + "http://hl7.org/fhir/StructureDefinition/Observation", + "http://hl7.org/fhir/StructureDefinition/DiagnosticReport" + ] + } + ] + }, + { + "id": "CarePlan.extension:toast.value[x]", + "path": "CarePlan.extension:toast.value[x]", + "type": [ + { + "code": "canonical", + "targetProfile": [ + "http://hl7.org/fhir/StructureDefinition/Observation", + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + } + ] + }, + { + "id": "CarePlan.extension:blank.value[x]", + "path": "CarePlan.extension:blank.value[x]", + "type": [ + { + "code": "canonical", + "targetProfile": [ + "http://hl7.org/fhir/StructureDefinition/Patient", + "http://hl7.org/fhir/StructureDefinition/Observation" + ] + }, + { + "code": "Annotation" + } + ] } ] }