Repository: FHIR/sushi (ts)

Issue:
Declaring Parent of Extension causes conflict in SD
In FSH, you can declare that an extension has a parent:
```
Extension: MyChildExtension
Parent: MyParentExtension
```
This will set the child extension's `baseDefinition` to the parent's canonical URL (indicating that it constrains the parent). It also fixes the child extensions `Extension.url` to the canonical URL of the child extension.  This is a problem because the parent has already fixed `Extension.url` to the parent's canonical URL -- so technically the child cannot fix it to a different value. This means that the child extension definition generated by SUSHI is actually invalid.

There are two possible approaches to fixing this:
1. Use the parent extension as the basis of the child extension, but set `baseDefinition` to the FHIR core `Extension`. This is essentially like cloning the parent and then modifying the clone for the child. This means that there is no _formal_ relationship between the parent and child, and all of the parents constraints need to be repeated in the child. The parent and child are _technically_ two completely different extensions, so the child _cannot_ be used in place of the parent (since it has a different fixed URL).
2. Formally set the child's `baseDefinition` to the parent's canonical URL and _do not_ fix `Extension.url` to the child extension's canonical URL. This is essentially the same as using `Profile:` and `Parent:` instead of `Extension:` and `Parent:`. In this case, the child is truly a constraint of the parent and uses the same fixed URL as the parent; and as such, can be used in place of the parent wherever the parent is expected.

The [FSH specification for Extension](https://hl7.org/fhir/uv/shorthand/reference.html#defining-extensions) is not explicit about which approach is correct, but I think the wording implies a constraint relationship, which would suggest that option 2 above is correct. The only downside of implementing 2 above is that while 2 already has an easy workaround (use `Profile:`), option 1 does not have any workaround -- so there would no longer be an easy way to accomplish option 1 if an author wanted that behavior.

Pull request:
Keep parent's fixedUri on Extension.url
Fixes #1178 and completes task [CIMPL-1042](https://standardhealthrecord.atlassian.net/browse/CIMPL-1042).

When an Extension's parent is another Extension (as opposed to the base Extension type), the Extension.url element will have a fixedUri set on the parent. To be valid FHIR, the child Extension should keep that same fixedUri.

Patch:
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index d832edb..99986fa 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,1 +1,1 @@
-*   @jafeltra @cmoesel @julianxcarter @mint-thompson @guhanthuran
\ No newline at end of file
+*   @jafeltra @cmoesel @mint-thompson
\ No newline at end of file
diff --git a/src/export/StructureDefinitionExporter.ts b/src/export/StructureDefinitionExporter.ts
index 58e905d..dad2487 100644
--- a/src/export/StructureDefinitionExporter.ts
+++ b/src/export/StructureDefinitionExporter.ts
@@ -362,9 +362,11 @@ export class StructureDefinitionExporter implements Fishable {
       // Keep context, assuming context is still valid for child extensions.
       // Keep contextInvariant, assuming context is still valid for child extensions.
 
-      // Automatically set url.fixedUri on Extensions
+      // Automatically set url.fixedUri on Extensions unless it is already set
       const url = structDef.findElement('Extension.url');
-      url.fixedUri = structDef.url;
+      if (url.fixedUri == null) {
+        url.fixedUri = structDef.url;
+      }
       if (structDef.context == null) {
         // Set context to everything by default, but users can override w/ rules, e.g.
         // ^context[0].type = #element


Test patch:
diff --git a/test/export/StructureDefinition.ExtensionExporter.test.ts b/test/export/StructureDefinition.ExtensionExporter.test.ts
index 6277b38..d65d4a0 100644
--- a/test/export/StructureDefinition.ExtensionExporter.test.ts
+++ b/test/export/StructureDefinition.ExtensionExporter.test.ts
@@ -87,7 +87,10 @@ describe('ExtensionExporter', () => {
     expect(exported.length).toBe(2);
     expect(exported[0].name).toBe('Foo');
     expect(exported[1].name).toBe('Bar');
-    expect(exported[1].baseDefinition === exported[0].url);
+    expect(exported[1].baseDefinition).toBe(exported[0].url);
+    const parentFixedUri = exported[0].findElement('Extension.url').fixedUri;
+    const childFixedUri = exported[1].findElement('Extension.url').fixedUri;
+    expect(parentFixedUri).toBe(childFixedUri);
   });
 
   it('should export extensions with the same FSHy parents', () => {
diff --git a/test/export/StructureDefinitionExporter.test.ts b/test/export/StructureDefinitionExporter.test.ts
index dc83fdc..3243b8f 100644
--- a/test/export/StructureDefinitionExporter.test.ts
+++ b/test/export/StructureDefinitionExporter.test.ts
@@ -1160,8 +1160,9 @@ describe('StructureDefinitionExporter R4', () => {
       expect(exported.derivation).toBe('constraint'); // always constraint
 
       // Check that Extension.url is correctly assigned
+      // Since the parent fixed this value, it should be the same as the parent
       expect(exported.elements.find(e => e.id === 'Extension.url').fixedUri).toBe(
-        'http://hl7.org/fhir/us/minimal/StructureDefinition/Foo'
+        'http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName'
       );
     });