From 309875ced82ad901747931143d55e4f433f694ee Mon Sep 17 00:00:00 2001 From: Julia Afeltra <30803904+jafeltra@users.noreply.github.com> Date: Wed, 23 Nov 2022 14:31:06 -0500 Subject: [PATCH] Find CodeSystems from packages by id, name, and URL (#1175) * Add tests for using name, id, or URL of CodeSystem in AssignmentRules * Support using id, name, or URL when assigning a CodeSystem from FHIR or dependencies - Use fishForMetadata to search in tank or fhir definitions for the CodeSystem - Assign the system if it is found for local and FHIR definition CodeSystems - Fix a bug in the TestFisher to only return the Package if we are fishing for a defined item --- src/export/Package.ts | 5 +- src/fhirtypes/common.ts | 14 ++-- test/export/InstanceExporter.test.ts | 73 +++++++++++++++++++ .../StructureDefinitionExporter.test.ts | 63 ++++++++++++++++ 4 files changed, 144 insertions(+), 11 deletions(-) diff --git a/src/export/Package.ts b/src/export/Package.ts index b7557d222..5ba8bcada 100644 --- a/src/export/Package.ts +++ b/src/export/Package.ts @@ -143,9 +143,8 @@ export class Package implements Fishable { return metadata; } else if ( // If nothing is returned, perhaps the Package itself is being referenced - item === this.config.packageId || - item === this.config.name || - item === this.config.id + item != null && + (item === this.config.packageId || item === this.config.name || item === this.config.id) ) { const metadata: Metadata = { id: this.config.packageId || this.config.id, diff --git a/src/fhirtypes/common.ts b/src/fhirtypes/common.ts index 81c67da4d..97a2a9a6f 100644 --- a/src/fhirtypes/common.ts +++ b/src/fhirtypes/common.ts @@ -443,17 +443,15 @@ export function replaceReferences( const [system, ...versionParts] = value.system?.split('|') ?? []; const version = versionParts.join('|'); const codeSystem = tank.fish(system, Type.CodeSystem); - const codeSystemMeta = fisher.fishForMetadata(codeSystem?.name, Type.CodeSystem); - if ( - codeSystem && - (codeSystem instanceof FshCodeSystem || codeSystem instanceof Instance) && - codeSystemMeta - ) { + const codeSystemMeta = fisher.fishForMetadata(system, Type.CodeSystem); + if (codeSystemMeta) { clone = cloneDeep(rule); const assignedCode = getRuleValue(clone) as FshCode; assignedCode.system = `${codeSystemMeta.url}${version ? `|${version}` : ''}`; - // if a local system was used, check to make sure the code is actually in that system - listUndefinedLocalCodes(codeSystem, [assignedCode.code], tank, rule); + if (codeSystem && (codeSystem instanceof FshCodeSystem || codeSystem instanceof Instance)) { + // if a local system was used, check to make sure the code is actually in that system + listUndefinedLocalCodes(codeSystem, [assignedCode.code], tank, rule); + } } } return clone ?? rule; diff --git a/test/export/InstanceExporter.test.ts b/test/export/InstanceExporter.test.ts index bc9a53dbd..e9023da0f 100644 --- a/test/export/InstanceExporter.test.ts +++ b/test/export/InstanceExporter.test.ts @@ -2848,6 +2848,79 @@ describe('InstanceExporter', () => { ]); }); + // Assigning codes from systems in the fisher.fhir (core fhir package or dependencies) + it('should assign a code from a CodeSystem in the fisher by id', () => { + // allergyintolerance-clinical is the id of a CodeSystem in the R4 definitions + const observation = new Instance('MyObservation'); + observation.instanceOf = 'Observation'; + const statusRule = new AssignmentRule('status'); + statusRule.value = new FshCode('active'); + const assignedCodeRule = new AssignmentRule('code'); + assignedCodeRule.value = new FshCode('test-code', 'allergyintolerance-clinical'); // id + observation.rules.push(assignedCodeRule, statusRule); + doc.instances.set(observation.name, observation); + + const exported = exportInstance(observation); + expect(exported.code).toEqual({ + coding: [ + { + code: 'test-code', + system: 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical' + } + ] + }); + expect(loggerSpy.getAllMessages('error')).toHaveLength(0); + }); + + it('should assign a code from a CodeSystem in the fisher by name', () => { + // AllergyIntoleranceClinicalStatusCodes is the name of a CodeSystem in the R4 definitions + const observation = new Instance('MyObservation'); + observation.instanceOf = 'Observation'; + const statusRule = new AssignmentRule('status'); + statusRule.value = new FshCode('active'); + const assignedCodeRule = new AssignmentRule('code'); + assignedCodeRule.value = new FshCode('test-code', 'AllergyIntoleranceClinicalStatusCodes'); // name + observation.rules.push(assignedCodeRule, statusRule); + doc.instances.set(observation.name, observation); + + const exported = exportInstance(observation); + expect(exported.code).toEqual({ + coding: [ + { + code: 'test-code', + system: 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical' + } + ] + }); + expect(loggerSpy.getAllMessages('error')).toHaveLength(0); + }); + + it('should assign a code from a CodeSystem in the fisher by url', () => { + // http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical is the url of a CodeSystem in the R4 definitions + const observation = new Instance('MyObservation'); + observation.instanceOf = 'Observation'; + const statusRule = new AssignmentRule('status'); + statusRule.value = new FshCode('active'); + const assignedCodeRule = new AssignmentRule('code'); + assignedCodeRule.value = new FshCode( + 'test-code', + 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical' + ); // url + observation.rules.push(assignedCodeRule, statusRule); + doc.instances.set(observation.name, observation); + + const exported = exportInstance(observation); + expect(exported.code).toEqual({ + coding: [ + { + code: 'test-code', + system: 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical' + } + ] + }); + expect(loggerSpy.getAllMessages('error')).toHaveLength(0); + }); + // Assigning Quantities with value 0 (e.g., Age) it('should assign a Quantity with value 0 (and not drop the 0)', () => { const observationInstance = new Instance('ZeroValueObservation'); diff --git a/test/export/StructureDefinitionExporter.test.ts b/test/export/StructureDefinitionExporter.test.ts index c37873477..dc83fdc0d 100644 --- a/test/export/StructureDefinitionExporter.test.ts +++ b/test/export/StructureDefinitionExporter.test.ts @@ -4281,6 +4281,69 @@ describe('StructureDefinitionExporter R4', () => { ); }); + it('should apply a Code AssignmentRule and replace the id of code system (from the core version fhir or dependency) with its url', () => { + // allergyintolerance-clinical is the id of a CodeSystem in the R4 definitions + const profile = new Profile('LightObservation'); + profile.parent = 'Observation'; + const rule = new AssignmentRule('category'); + rule.value = new FshCode('test-code', 'allergyintolerance-clinical'); // id + profile.rules.push(rule); + + exporter.exportStructDef(profile); + const sd = pkg.profiles[0]; + const assignedElement = sd.findElement('Observation.category'); + expect(assignedElement.patternCodeableConcept.coding).toEqual([ + { + code: 'test-code', + system: 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical' + } + ]); + expect(loggerSpy.getAllMessages('error')).toHaveLength(0); + }); + + it('should apply a Code AssignmentRule and replace the name of code system (from the core version fhir or dependency) with its url', () => { + // AllergyIntoleranceClinicalStatusCodes is the name of a CodeSystem in the R4 definitions + const profile = new Profile('LightObservation'); + profile.parent = 'Observation'; + const rule = new AssignmentRule('category'); + rule.value = new FshCode('test-code', 'AllergyIntoleranceClinicalStatusCodes'); // name + profile.rules.push(rule); + + exporter.exportStructDef(profile); + const sd = pkg.profiles[0]; + const assignedElement = sd.findElement('Observation.category'); + expect(assignedElement.patternCodeableConcept.coding).toEqual([ + { + code: 'test-code', + system: 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical' + } + ]); + expect(loggerSpy.getAllMessages('error')).toHaveLength(0); + }); + + it('should apply a Code AssignmentRule and keep the url of code system (from the core version fhir or dependency) as the system url', () => { + // http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical is the url of a CodeSystem in the R4 definitions + const profile = new Profile('LightObservation'); + profile.parent = 'Observation'; + const rule = new AssignmentRule('category'); + rule.value = new FshCode( + 'test-code', + 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical' + ); // url + profile.rules.push(rule); + + exporter.exportStructDef(profile); + const sd = pkg.profiles[0]; + const assignedElement = sd.findElement('Observation.category'); + expect(assignedElement.patternCodeableConcept.coding).toEqual([ + { + code: 'test-code', + system: 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical' + } + ]); + expect(loggerSpy.getAllMessages('error')).toHaveLength(0); + }); + it('should apply an AssignmentRule with a valid Canonical entity defined in FSH', () => { const profile = new Profile('MyObservation'); profile.parent = 'Observation';