From 04b9d7beb12ad99489e03d0feeb142cf79c09762 Mon Sep 17 00:00:00 2001 From: KaelynJefferson <106775284+KaelynJefferson@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:52:38 -0400 Subject: [PATCH] ValueSet self reference (#1503) * adding initial check for self referencing value sets and logs error * moving self reference check to setCompose * typo in error message * linting files * update to self reference error message * remove self reference null values * adding conditional feedback --- src/export/ValueSetExporter.ts | 12 ++++++- test/export/ValueSetExporter.test.ts | 51 ++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/export/ValueSetExporter.ts b/src/export/ValueSetExporter.ts index e27ca9ed7..19c1b776e 100644 --- a/src/export/ValueSetExporter.ts +++ b/src/export/ValueSetExporter.ts @@ -86,6 +86,14 @@ export class ValueSetExporter { composeElement.valueSet = component.from.valueSets.map(vs => { return this.fisher.fishForMetadata(vs, Type.ValueSet)?.url ?? vs; }); + composeElement.valueSet = composeElement.valueSet.filter(vs => { + if (vs == valueSet.url) { + logger.error( + `Value set with id ${valueSet.id} has component rule with self-referencing value set (by id, value set name, or url). Removing self-reference.` + ); + } + return vs != valueSet.url; + }); composeElement.valueSet.forEach(vs => { // Canonical URI may include | to specify version: https://www.hl7.org/fhir/references.html#canonical if (!isUri(vs.split('|')[0])) { @@ -176,7 +184,9 @@ export class ValueSetExporter { this.addConceptComposeElement(composeElement, valueSet.compose.include); } } else { - valueSet.compose.include.push(composeElement); + if (composeElement.valueSet?.length !== 0 || composeElement.system != undefined) { + valueSet.compose.include.push(composeElement); + } } } else { this.addConceptComposeElement(composeElement, valueSet.compose.exclude); diff --git a/test/export/ValueSetExporter.test.ts b/test/export/ValueSetExporter.test.ts index b6f9a35a4..78baa7b51 100644 --- a/test/export/ValueSetExporter.test.ts +++ b/test/export/ValueSetExporter.test.ts @@ -473,6 +473,57 @@ describe('ValueSetExporter', () => { }); }); + it('should remove and log error when exporting a value set that includes a component from a self referencing value set', () => { + const valueSet = new FshValueSet('DinnerVS'); + valueSet.id = 'dinner-vs'; + const component = new ValueSetConceptComponentRule(true); + component.from = { + system: 'http://food.org/food1', + valueSets: [ + 'http://food.org/food/ValueSet/hot-food', + 'http://food.org/food/ValueSet/cold-food', + 'DinnerVS', + 'http://hl7.org/fhir/us/minimal/ValueSet/dinner-vs', + 'dinner-vs' + ] + }; + const component2 = new ValueSetConceptComponentRule(true); + component2.from = { + system: 'http://food.org/food2', + valueSets: ['DinnerVS', 'http://hl7.org/fhir/us/minimal/ValueSet/dinner-vs', 'dinner-vs'] + }; + valueSet.rules.push(component); + valueSet.rules.push(component2); + doc.valueSets.set(valueSet.name, valueSet); + const exported = exporter.export().valueSets; + expect(exported.length).toBe(1); + expect(exported[0]).toEqual({ + resourceType: 'ValueSet', + id: 'dinner-vs', + name: 'DinnerVS', + url: 'http://hl7.org/fhir/us/minimal/ValueSet/dinner-vs', + status: 'draft', + compose: { + include: [ + { + system: 'http://food.org/food1', + valueSet: [ + 'http://food.org/food/ValueSet/hot-food', + 'http://food.org/food/ValueSet/cold-food' + ] + }, + { + system: 'http://food.org/food2' + } + ] + } + }); + expect(loggerSpy.getAllMessages('error')).toHaveLength(6); + expect(loggerSpy.getLastMessage('error')).toBe( + 'Value set with id dinner-vs has component rule with self-referencing value set (by id, value set name, or url). Removing self-reference.' + ); + }); + it('should export a value set that includes a concept component with at least one concept', () => { const valueSet = new FshValueSet('DinnerVS'); const component = new ValueSetConceptComponentRule(true);