Repository: FHIR/sushi (ts)

Issue:
References to codes defined in locally-defined code system cause IG Pub error
I tried to define a local code system:
````
CodeSystem:  AJCCFairUse
Title: "AJCC Fair Use"
Description: "A small local subset of AJCC staging codes for example purposes."
* #3C
* #cM0
* #cT3
* #cN3
* #pM0
* #pT3
* #pN3
```
and then referenced it in an instance like this:

`* valueCodeableConcept = AJCCFairUse#3C "IIIC"`

That didn't work because the IG Pub complained that you must use a URL for a code system, not a local name. 

So I tried this:

`Alias:   FairUse = http://hl7.org/fhir/us/mcode/CodeSystem/AJCCFairUse`

and then tried to reference it in an instance like this:

`* valueCodeableConcept = FairUse#3C "IIIC"`

but that didn't work either. The IG Pub complained "Unknown Code System 'http://hl7.org/fhir/us/mcode/CodeSystem/AJCCFairUse'", and "URL value 'http://hl7.org/fhir/us/mcode/CodeSystem/AJCCFairUse' does not resolve"

So I'm wondering how one an define a code system in FSH and use codes from that system in the same FSH Tank.

Sorry I can't provide a more complete example because I pulled back my change.

Pull request:
Replace references to local code system with url
Fixes #149 by way of task https://standardhealthrecord.atlassian.net/browse/CIMPL-247.

When an Instance or Structure Definition refers to a code system by name, and that code system is defined locally, replace the code system's name with the code system's url.

I didn't manage to exactly replicate every error mentioned in #149, but this pull request addresses a main portion of it. There may be further work required relating to references to locally defined code systems.

Patch:
diff --git a/src/fhirtypes/common.ts b/src/fhirtypes/common.ts
index 7951e5b..4a415a3 100644
--- a/src/fhirtypes/common.ts
+++ b/src/fhirtypes/common.ts
@@ -1,6 +1,6 @@
 import { StructureDefinition, PathPart, ElementDefinition, InstanceDefinition } from '.';
 import { FixedValueRule } from '../fshtypes/rules';
-import { FshReference, Instance, SourceInfo } from '../fshtypes';
+import { FshReference, Instance, SourceInfo, FshCode } from '../fshtypes';
 import { FSHTank } from '../import';
 import { Type, Fishable } from '../utils/Fishable';
 import cloneDeep = require('lodash/cloneDeep');
@@ -123,9 +123,10 @@ export function getArrayIndex(pathPart: PathPart): number {
 }
 
 /**
- * Replaces references to instances by the correct path to that instance
+ * Replaces references to instances by the correct path to that instance.
+ * Replaces references to local code systems by the url for that code system.
  * @param {FixedValueRule} rule - The rule to replace references on
- * @param {FSHTank} tank - The tank holding the instances
+ * @param {FSHTank} tank - The tank holding the instances and code systems
  * @param {Fishable} fisher - A fishable implementation for finding definitions and metadata
  * @returns {FixedValueRule} a clone of the rule if replacing is done, otherwise the original rule
  */
@@ -153,6 +154,14 @@ export function replaceReferences(
       const fv = clone.fixedValue as FshReference;
       fv.reference = `${instanceMeta.sdType}/${id}`;
     }
+  } else if (rule.fixedValue instanceof FshCode) {
+    const codeSystem = tank.fish(rule.fixedValue.system, Type.CodeSystem);
+    const codeSystemMeta = fisher.fishForMetadata(codeSystem?.name, Type.CodeSystem);
+    if (codeSystem && codeSystemMeta) {
+      clone = cloneDeep(rule);
+      const fv = clone.fixedValue as FshCode;
+      fv.system = codeSystemMeta.url;
+    }
   }
   return clone ?? rule;
 }


Test patch:
diff --git a/test/export/InstanceExporter.test.ts b/test/export/InstanceExporter.test.ts
index 480bf73..87cced9 100644
--- a/test/export/InstanceExporter.test.ts
+++ b/test/export/InstanceExporter.test.ts
@@ -1,7 +1,14 @@
 import { InstanceExporter, Package, StructureDefinitionExporter } from '../../src/export';
 import { FSHTank, FSHDocument } from '../../src/import';
 import { FHIRDefinitions, loadFromPath } from '../../src/fhirdefs';
-import { Instance, Profile, FshCode, FshReference, Extension } from '../../src/fshtypes';
+import {
+  Instance,
+  Profile,
+  FshCode,
+  FshReference,
+  Extension,
+  FshCodeSystem
+} from '../../src/fshtypes';
 import { FixedValueRule, ContainsRule, CardRule, OnlyRule } from '../../src/fshtypes/rules';
 import { loggerSpy, TestFisher } from '../testhelpers';
 import { InstanceDefinition } from '../../src/fhirtypes';
@@ -558,6 +565,45 @@ describe('InstanceExporter', () => {
       });
     });
 
+    // Fixing codes from local systems
+    it('should fix a code to a top level element while replacing the local code system name with its url', () => {
+      const brightInstance = new Instance('BrightObservation');
+      brightInstance.instanceOf = 'Observation';
+      const fixedCodeRule = new FixedValueRule('code');
+      fixedCodeRule.fixedValue = new FshCode('bright', 'Visible');
+      brightInstance.rules.push(fixedCodeRule);
+      doc.instances.set(brightInstance.name, brightInstance);
+
+      const visibleSystem = new FshCodeSystem('Visible');
+      doc.codeSystems.set(visibleSystem.name, visibleSystem);
+      const exported = exportInstance(brightInstance);
+      expect(exported.code.coding).toEqual([
+        {
+          code: 'bright',
+          system: 'http://example.com/CodeSystem/Visible'
+        }
+      ]);
+    });
+
+    it('should fix a code to a nested element while replacing the local code system name with its url', () => {
+      const brightInstance = new Instance('BrightObservation');
+      brightInstance.instanceOf = 'Observation';
+      const fixedCodeRule = new FixedValueRule('component[0].code');
+      fixedCodeRule.fixedValue = new FshCode('bright', 'Visible');
+      brightInstance.rules.push(fixedCodeRule);
+      doc.instances.set(brightInstance.name, brightInstance);
+
+      const visibleSystem = new FshCodeSystem('Visible');
+      doc.codeSystems.set(visibleSystem.name, visibleSystem);
+      const exported = exportInstance(brightInstance);
+      expect(exported.component[0].code.coding).toEqual([
+        {
+          code: 'bright',
+          system: 'http://example.com/CodeSystem/Visible'
+        }
+      ]);
+    });
+
     // Sliced elements
     it('should fix a single sliced element to a value', () => {
       const fixedValRule = new FixedValueRule('extension[level].valueCoding.system');
diff --git a/test/export/StructureDefinitionExporter.test.ts b/test/export/StructureDefinitionExporter.test.ts
index 70f5099..9ab312b 100644
--- a/test/export/StructureDefinitionExporter.test.ts
+++ b/test/export/StructureDefinitionExporter.test.ts
@@ -7,7 +7,8 @@ import {
   FshCode,
   FshReference,
   Instance,
-  FshValueSet
+  FshValueSet,
+  FshCodeSystem
 } from '../../src/fshtypes';
 import {
   CardRule,
@@ -1068,6 +1069,27 @@ describe('StructureDefinitionExporter', () => {
     });
   });
 
+  it('should apply a Code FixedValueRule and replace the local code system name with its url', () => {
+    const profile = new Profile('LightObservation');
+    profile.parent = 'Observation';
+    const rule = new FixedValueRule('valueCodeableConcept');
+    rule.fixedValue = new FshCode('bright', 'Visible');
+    profile.rules.push(rule);
+
+    const visibleSystem = new FshCodeSystem('Visible');
+    doc.codeSystems.set(visibleSystem.name, visibleSystem);
+
+    exporter.exportStructDef(profile);
+    const sd = pkg.profiles[0];
+    const fixedElement = sd.findElement('Observation.value[x]:valueCodeableConcept');
+    expect(fixedElement.patternCodeableConcept.coding).toEqual([
+      {
+        code: 'bright',
+        system: 'http://example.com/CodeSystem/Visible'
+      }
+    ]);
+  });
+
   it('should not apply an incorrect FixedValueRule', () => {
     const profile = new Profile('Foo');
     profile.parent = 'Observation';