Repository: FHIR/sushi
Pull request #534 (2020-07-15 15:07:20)

Allow profiles/extensions to inherit *some* extensions
Instead of removing ALL extensions from the parent SD, allow some to be inherited.  This is implemented by keeping track of extensions NOT to inherit.
See: https://jira.hl7.org/browse/FHIR-27535

cmoesel: Oh, yeah, sorry.  Here it is: https://chat.fhir.org/#narrow/stream/179239-tooling/topic/Forge.20added.20extension.20explicit-type-name/near/203906359

Patch:
diff --git a/src/export/StructureDefinitionExporter.ts b/src/export/StructureDefinitionExporter.ts
index dacd5c8..683e07b 100644
--- a/src/export/StructureDefinitionExporter.ts
+++ b/src/export/StructureDefinitionExporter.ts
@@ -34,6 +34,16 @@ import {
 } from '../fhirtypes/common';
 import { Package } from './Package';
 
+// Extensions that should not be inherited by derived profiles
+// See: https://jira.hl7.org/browse/FHIR-27535
+const UNINHERITED_EXTENSIONS = [
+  'http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status',
+  'http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version',
+  'http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name',
+  'http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm',
+  'http://hl7.org/fhir/StructureDefinition/structuredefinition-wg'
+];
+
 /**
  * The StructureDefinitionExporter is the class for exporting Profiles and Extensions.
  * The operations and structure of both exporters are very similar, so they currently share an exporter.
@@ -67,8 +77,18 @@ export class StructureDefinitionExporter implements Fishable {
     delete structDef.language;
     delete structDef.text;
     delete structDef.contained;
-    delete structDef.extension; // see https://github.com/FHIR/sushi/issues/116
-    delete structDef.modifierExtension;
+    structDef.extension = structDef.extension?.filter(e => !UNINHERITED_EXTENSIONS.includes(e.url));
+    if (!structDef.extension?.length) {
+      // for consistency, delete rather than leaving null-valued
+      delete structDef.extension;
+    }
+    structDef.modifierExtension = structDef.modifierExtension?.filter(
+      e => !UNINHERITED_EXTENSIONS.includes(e.url)
+    );
+    if (!structDef.modifierExtension?.length) {
+      // for consistency, delete rather than leaving null-valued
+      delete structDef.modifierExtension;
+    }
     structDef.url = `${this.tank.config.canonical}/StructureDefinition/${structDef.id}`;
     delete structDef.identifier;
     structDef.version = this.tank.config.version; // can be overridden using a rule


Test patch:
diff --git a/test/export/StructureDefinitionExporter.test.ts b/test/export/StructureDefinitionExporter.test.ts
index e5869d2..85ad4c3 100644
--- a/test/export/StructureDefinitionExporter.test.ts
+++ b/test/export/StructureDefinitionExporter.test.ts
@@ -88,7 +88,29 @@ describe('StructureDefinitionExporter', () => {
     expect(exported.language).toBeUndefined();
     expect(exported.text).toBeUndefined();
     expect(exported.contained).toBeUndefined(); // inherited from Observation
-    expect(exported.extension).toBeUndefined();
+    // NOTE: The following extensions are stripped out as uninherited extensions:
+    // {
+    //   url: "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status",
+    //   valueCode: "normative"
+    // },
+    // {
+    //   url: "http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version",
+    //   valueCode: "4.0.0"
+    // },
+    // { url: 'http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm', valueInteger: 5 },
+    // { url: 'http://hl7.org/fhir/StructureDefinition/structuredefinition-wg', valueCode: 'oo' }
+    //
+    // BUT the following two extensions should remain:
+    expect(exported.extension).toEqual([
+      {
+        url: 'http://hl7.org/fhir/StructureDefinition/structuredefinition-category',
+        valueString: 'Clinical.Diagnostics'
+      },
+      {
+        url: 'http://hl7.org/fhir/StructureDefinition/structuredefinition-security-category',
+        valueCode: 'patient'
+      }
+    ]);
     expect(exported.modifierExtension).toBeUndefined();
     expect(exported.url).toBe('http://hl7.org/fhir/us/minimal/StructureDefinition/Foo'); // constructed from canonical and id
     expect(exported.identifier).toBeUndefined();
@@ -257,7 +279,7 @@ describe('StructureDefinitionExporter', () => {
     expect(exported.language).toBeUndefined();
     expect(exported.text).toBeUndefined();
     expect(exported.contained).toBeUndefined(); // inherited from patient-mothersMaidenName
-    expect(exported.extension).toBeUndefined();
+    expect(exported.extension).toBeUndefined(); // uninherited extensions are filtered out
     expect(exported.modifierExtension).toBeUndefined();
     expect(exported.url).toBe('http://hl7.org/fhir/us/minimal/StructureDefinition/Foo'); // constructed from canonical and id
     expect(exported.identifier).toBeUndefined();
@@ -2338,7 +2360,9 @@ describe('StructureDefinitionExporter', () => {
 
     exporter.exportStructDef(profile);
     const sd = pkg.profiles[0];
-    const extensionElement = sd.extension[0];
+    const extensionElement = sd.extension.find(
+      e => e.url === 'http://hl7.org/fhir/us/minimal/StructureDefinition/SpecialExtension'
+    );
     expect(extensionElement).toBeDefined();
     expect(extensionElement).toEqual({
       url: 'http://hl7.org/fhir/us/minimal/StructureDefinition/SpecialExtension',