Repository: FHIR/sushi (ts)

Issue:
SUSHI doesn't resolve code systems by name when processing codes
In US Core, there is a code system with the `name` [USCoreTagsCodeSystem](http://www.hl7.org/fhir/us/core/CodeSystem-us-core-tags.html) which contains the code `sdoh`.

As such, you should be able to use that name in a FSH code, like `USCoreTagsCodeSystem#sdoh`.  When you do, however, SUSHI complains:
> Resolved value "USCoreTagsCodeSystem" is not a valid URI.

First reported on Zulip [here](https://chat.fhir.org/#narrow/stream/215610-shorthand/topic/Aliases.2C.20terminology.2C.20and.20packages/near/310083886).

Simple FSH Online example [here](https://fshschool.org/FSHOnline/#/share/3tqBFHX).

Pull request description:
Find CodeSystems from packages by id, name, and URL
Fixes #1172 

This PR supports using CodeSystem `name` or `id`, as well as `url`, when assigning codes from CodeSystems that are either in the FHIR version or a dependency.

The tests are added in the first commit and they are fixed with the second commit. Using `name` or `id` worked when used with CodeSystems in on ValueSetComponentRules, so I took a similar approach as the one we used [there](https://github.com/FHIR/sushi/blob/master/src/export/ValueSetExporter.ts#L58-L62). Basically, fishing for metadata will correctly find the CodeSystem whether it is in the `fisher.tank` or `fisher.fhir` (which includes any FHIR resources and any dependency resources). If we find a CodeSystem, we want to use the `url` in the `system` on the rule.


Patch:
diff --git a/src/export/Package.ts b/src/export/Package.ts
index b7557d2..5ba8bca 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 81c67da..97a2a9a 100644
--- a/src/fhirtypes/common.ts
+++ b/src/fhirtypes/common.ts
@@ -443,17 +443,15 @@ export function replaceReferences<T extends AssignmentRule | CaretValueRule>(
     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;


Test patch:
diff --git a/test/export/InstanceExporter.test.ts b/test/export/InstanceExporter.test.ts
index bc9a53d..e9023da 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 c378734..dc83fdc 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';