Repository: FHIR/sushi (ts)

Issue:
SUSHI should support extensions on dependencies in sushi-config.yaml
SUSHI should allow `extension` property on the entries in `dependencies` in `sushi-config.yaml`. These extensions should be carried through and added in the ImplementationGuide resource.

Note: this came up as a first step to supporting package scopes.

Pull request:
Allow extensions on configured dependencies
Fixes #1414

This PR allows `extension` on the configured `dependencies` list in `sushi-config.yaml`. Extensions are then passed on to the IG JSON. No additional manipulation or validation is done.

There are some examples in tests, but if you want to try this in a project, add the following (or something similar) to your `sushi-config.yaml`:

```yaml
dependencies:
  hl7.fhir.us.core:
    id: us core
    version: 6.1.0
    extension:
      - url: http://hl7.org/fhir/tools/StructureDefinition/package-scope
        valueString: myscope
```

Patch:
diff --git a/src/ig/IGExporter.ts b/src/ig/IGExporter.ts
index 40c49fd..e879258 100644
--- a/src/ig/IGExporter.ts
+++ b/src/ig/IGExporter.ts
@@ -300,6 +300,8 @@ export class IGExporter {
         : 'id_' + dependsOn.packageId.replace(/\.|-/g, '_');
     }
 
+    // Keep dependsOn.extension as configured, so no special handling is needed
+
     return dependsOn;
   }
 
diff --git a/src/import/YAMLConfiguration.ts b/src/import/YAMLConfiguration.ts
index ab14dc1..73c3e5b 100644
--- a/src/import/YAMLConfiguration.ts
+++ b/src/import/YAMLConfiguration.ts
@@ -322,6 +322,7 @@ export type YAMLConfigurationDependencyDetails = {
     | ImplementationGuideDependsOn['version'] // string
     | number; // YAML will parse some versions as numbers (e.g., 1.2)
   reason?: ImplementationGuideDependsOn['reason']; // string; only supported in R5
+  extension?: Extension[];
 };
 
 export type YAMLConfigurationGlobalMap = {
diff --git a/src/import/YAMLschema.json b/src/import/YAMLschema.json
index cb5a552..882545a 100644
--- a/src/import/YAMLschema.json
+++ b/src/import/YAMLschema.json
@@ -664,6 +664,12 @@
         },
         "reason": {
           "type": "string"
+        },
+        "extension": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/extension"
+          }
         }
       },
       "additionalProperties": false
diff --git a/src/import/importConfiguration.ts b/src/import/importConfiguration.ts
index 7710b8a..d7a0843 100644
--- a/src/import/importConfiguration.ts
+++ b/src/import/importConfiguration.ts
@@ -590,7 +590,8 @@ function parseDependencies(
         typeof versionOrDetails.version === 'string' || typeof versionOrDetails.version === 'number'
           ? `${versionOrDetails.version}`
           : undefined,
-      reason: versionOrDetails.reason
+      reason: versionOrDetails.reason,
+      extension: versionOrDetails.extension
     });
   });
 }


Test patch:
diff --git a/test/ig/IGExporter.IG.test.ts b/test/ig/IGExporter.IG.test.ts
index 10be801..915195a 100644
--- a/test/ig/IGExporter.IG.test.ts
+++ b/test/ig/IGExporter.IG.test.ts
@@ -115,6 +115,17 @@ describe('IGExporter', () => {
             uri: 'http://hl7.org/fhir/us/mcode/ImplementationGuide/hl7.fhir.us.mcode',
             id: 'mcode',
             version: '1.0.0'
+          },
+          {
+            packageId: 'hl7.package.with.extension',
+            version: '1.0.0',
+            id: 'package.with.extension',
+            extension: [
+              {
+                url: 'http://example.org/fake/StructureDefinition/bar',
+                valueString: 'bar'
+              }
+            ]
           }
         ],
         resources: [
@@ -235,6 +246,19 @@ describe('IGExporter', () => {
             uri: 'http://hl7.org/fhir/us/mcode/ImplementationGuide/hl7.fhir.us.mcode',
             packageId: 'hl7.fhir.us.mcode',
             version: '1.0.0'
+          },
+          // package.with.extension tests that extensions are passed from config to ig resource
+          {
+            id: 'package.with.extension',
+            uri: 'http://fhir.org/packages/hl7.package.with.extension/ImplementationGuide/hl7.package.with.extension',
+            packageId: 'hl7.package.with.extension',
+            version: '1.0.0',
+            extension: [
+              {
+                url: 'http://example.org/fake/StructureDefinition/bar',
+                valueString: 'bar'
+              }
+            ]
           }
         ],
         definition: {
diff --git a/test/import/YAMLConfiguration.test.ts b/test/import/YAMLConfiguration.test.ts
index fcee8dc..d438655 100644
--- a/test/import/YAMLConfiguration.test.ts
+++ b/test/import/YAMLConfiguration.test.ts
@@ -54,6 +54,13 @@ describe('YAMLConfiguration', () => {
           id: 'mcode',
           uri: 'http://hl7.org/fhir/us/mcode/ImplementationGuide/hl7.fhir.us.mcode',
           version: '1.0.0'
+        },
+        'hl7.fhir.us.foo': {
+          id: 'foo',
+          version: '1.0.0',
+          extension: [
+            { url: 'http://example.org/fake/StructureDefinition/bar', valueString: 'bar' }
+          ]
         }
       });
       expect(config.global).toEqual({
diff --git a/test/import/fixtures/example-config.yaml b/test/import/fixtures/example-config.yaml
index 6023fb4..1b64126 100644
--- a/test/import/fixtures/example-config.yaml
+++ b/test/import/fixtures/example-config.yaml
@@ -69,6 +69,12 @@ dependencies:
     id: mcode
     uri: http://hl7.org/fhir/us/mcode/ImplementationGuide/hl7.fhir.us.mcode
     version: 1.0.0
+  hl7.fhir.us.foo:
+    id: foo
+    version: 1.0.0
+    extension:
+      - url: http://example.org/fake/StructureDefinition/bar
+        valueString: bar
 
 # The global property corresponds to the IG.global property, but it
 # uses the type as the YAML key and the profile as its value. Since
diff --git a/test/import/importConfiguration.test.ts b/test/import/importConfiguration.test.ts
index fcd020d..68b3a73 100644
--- a/test/import/importConfiguration.test.ts
+++ b/test/import/importConfiguration.test.ts
@@ -95,6 +95,14 @@ describe('importConfiguration', () => {
           packageId: 'hl7.fhir.us.mcode',
           uri: 'http://hl7.org/fhir/us/mcode/ImplementationGuide/hl7.fhir.us.mcode',
           version: '1.0.0'
+        },
+        {
+          id: 'foo',
+          packageId: 'hl7.fhir.us.foo',
+          version: '1.0.0',
+          extension: [
+            { url: 'http://example.org/fake/StructureDefinition/bar', valueString: 'bar' }
+          ]
         }
       ],
       global: [
@@ -1762,13 +1770,14 @@ describe('importConfiguration', () => {
       ).toBeTruthy();
     });
 
-    it('should convert dependencies to a list with reason and uri when provided', () => {
+    it('should convert dependencies to a list with reason and uri and extension when provided', () => {
       minYAML.dependencies = {
         foo: {
           id: 'foo',
           uri: 'http://example.org',
           version: '1.0.0',
-          reason: 'Foo is always necessary'
+          reason: 'Foo is always necessary',
+          extension: [{ url: 'http://example.org/fake/extension', valueString: 'foo' }]
         }
       };
       const config = importConfiguration(minYAML, 'test-config.yaml');
@@ -1778,9 +1787,11 @@ describe('importConfiguration', () => {
           id: 'foo',
           version: '1.0.0',
           uri: 'http://example.org',
-          reason: 'Foo is always necessary'
+          reason: 'Foo is always necessary',
+          extension: [{ url: 'http://example.org/fake/extension', valueString: 'foo' }]
         }
       ]);
+      expect(loggerSpy.getAllMessages('warn')).toHaveLength(0);
     });
   });