Repository: FHIR/sushi
Pull request #707 (2020-12-30 16:04:59)

Relax required slice validation
This addresses [CIMPL-618](https://standardhealthrecord.atlassian.net/browse/CIMPL-621), and also https://github.com/FHIR/GoFSH/issues/57 by relaxing the validation we do on required slices. Previously, when SUSHI was validating the cardinality of slices, it would not count things that had a numerical index in the FSH. For example:
```
Instance: ctc-adverse-event-example-1
InstanceOf: CTCAdverseEvent
Usage: #example
* extension[0].url = "http://hl7.org/fhir/us/mcode/StructureDefinition/ctcae-grade"
* extension[0].valueCodeableConcept = $ctcae-grade-code-system#2 "Moderate Adverse Event"
```
the extension at `extension[0]` would not satisfy the cardinality of a `ctae-grade` extension, which was `1..1`. If instead the definition looked like this:
```
Instance: ctc-adverse-event-example-1
InstanceOf: CTCAdverseEvent
Usage: #example
* extension[grade].url = "http://hl7.org/fhir/us/mcode/StructureDefinition/ctcae-grade"
* extension[grade].valueCodeableConcept = $ctcae-grade-code-system#2 "Moderate Adverse Event"
```
SUSHI would be satisfied, since it knows which slice is being defined.

This check was overly strict, but we are likely not interested in the business of deciding which slice is being defined, so this PR takes the simple approach of just not validating cardinality of a slice if that slice _could_ be referred to by a numerically indexed path. So in the example above, `extension[0]` could be referring to any extension, so we don't validate cardinality for any required extensions.


Patch:
diff --git a/src/export/InstanceExporter.ts b/src/export/InstanceExporter.ts
index b3b326b..6618111 100644
--- a/src/export/InstanceExporter.ts
+++ b/src/export/InstanceExporter.ts
@@ -156,6 +156,16 @@ export class InstanceExporter implements Fishable {
       }
       // Recursively validate children of the current element
       if (Array.isArray(instanceChild)) {
+        // If the child is a slice, and there are no array elements with matching slice names
+        // but there are array elements that could match (since they use numerical indices)
+        // we can go no further in validation, since we can't know which slice is represented
+        if (
+          child.sliceName &&
+          !instanceChild.find((arrayEl: any) => arrayEl?._sliceName === child.sliceName) &&
+          instanceChild.find((arrayEl: any) => !arrayEl?._sliceName)
+        ) {
+          return;
+        }
         // Filter so that if the child is a slice, we only count relevant slices
         instanceChild = instanceChild.filter(
           (arrayEl: any) => !child.sliceName || arrayEl?._sliceName === child.sliceName


Test patch:
diff --git a/test/export/InstanceExporter.test.ts b/test/export/InstanceExporter.test.ts
index 9dc5358..aacb030 100644
--- a/test/export/InstanceExporter.test.ts
+++ b/test/export/InstanceExporter.test.ts
@@ -1718,6 +1718,21 @@ describe('InstanceExporter', () => {
       );
     });
 
+    it('should not log an error when a required sliced element could be satisfied by elements without a sliceName', () => {
+      const assignedValueRule = new AssignmentRule('result[0]');
+      assignedValueRule.value = new FshReference('Fsh are friends');
+      lipidInstance.rules.push(assignedValueRule);
+      exportInstance(lipidInstance);
+      const messages = loggerSpy.getAllMessages('error');
+      // No errors relating to specific slices are logged, since result[0] could be referring to any of them
+      expect(messages[messages.length - 2]).toMatch(
+        /DiagnosticReport.status.*File: LipidInstance\.fsh.*Line: 10 - 20/s
+      );
+      expect(messages[messages.length - 1]).toMatch(
+        /DiagnosticReport.result.*File: LipidInstance\.fsh.*Line: 10 - 20/s
+      );
+    });
+
     it('should log an error when a required element inherited from a resource is not present', () => {
       const observationInstance = new Instance('Pow')
         .withFile('ObservationInstance.fsh')