Repository: FHIR/sushi
Pull request #220 (2020-02-14 18:27:35)

No snapshot
This PR stops snapshots from being generated by default. To generate snapshots, the user can use the `-s` or `--snapshot` flag.

My method for testing that this doesn't cause issues in the IG publisher was to run the IG publisher using output both with and without snapshots. I then compared the generated JSON output that the IG publisher created, and looked for any major divergence that wasn't just something like a changed timestamp. This was manual and not super efficient, so if any reviewers have a better way let me know what worked.

cmoesel: What FSH did you run this test on?  Random sample FSH, fsh-mcode, or something else?
ngfreiter: fsh-mcode for me.
cmoesel: I was hoping you would say that!
jafeltra: I did notice one additional thing after I had approved that I just wanted to double check on. When comparing the generated files `StructureDefinition-mcode-cancer-disease-status.json` in `output`, I noticed there were a couple differences between what was generated on master and what was generated on this branch. They don't seem too harmful, but I'm not entirely sure where/how they were changed, so I wanted to ask. The only difference in the corresponding SD in `input` is the missing `snapshot` on this branch.

I can also attach the two files if that's easiest for anyone.
ngfreiter: At least some of these changes do seem to be positive changes. For example, the link http://hl7.org/fhir/us/mcode/extensibility.html is now http://hl7.org/fhir/R4/extensibility.html, changing it from a broken link into one that works.

Patch:
diff --git a/README.md b/README.md
index 4e090f5..913bf51 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ Usage: sushi <path-to-fsh-defs> [options]
 Options:
   -o, --out <out>  the path to the output folder (default: "build")
   -d, --debug      output extra debugging information
+  -s, --snapshot   generate snapshot in Structure Definition output (default: false)
   -v, --version    print SUSHI version
   -h, --help       output usage information
 ```
diff --git a/src/app.ts b/src/app.ts
index a3d79ff..b0bdd18 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -20,6 +20,7 @@ async function app() {
     .usage('<path-to-fsh-defs> [options]')
     .option('-o, --out <out>', 'the path to the output folder', path.join('.', 'build'))
     .option('-d, --debug', 'output extra debugging information')
+    .option('-s, --snapshot', 'generate snapshot in Structure Definition output', false)
     .version(getVersion(), '-v, --version', 'print SUSHI version')
     .arguments('<path-to-fsh-defs>')
     .action(function(pathToFshDefs) {
@@ -104,13 +105,17 @@ async function app() {
   let count = 0;
   const writeResources = (
     folder: string,
-    resources: { getFileName: () => string; toJSON: () => any }[]
+    resources: { getFileName: () => string; toJSON: (snapshot: boolean) => any }[]
   ) => {
     const exportDir = path.join(program.out, 'input', folder);
     resources.forEach(resource => {
-      fs.outputJSONSync(path.join(exportDir, resource.getFileName()), resource.toJSON(), {
-        spaces: 2
-      });
+      fs.outputJSONSync(
+        path.join(exportDir, resource.getFileName()),
+        resource.toJSON(program.snapshot),
+        {
+          spaces: 2
+        }
+      );
       count++;
     });
   };
diff --git a/src/fhirtypes/StructureDefinition.ts b/src/fhirtypes/StructureDefinition.ts
index d805d2e..7badc9e 100644
--- a/src/fhirtypes/StructureDefinition.ts
+++ b/src/fhirtypes/StructureDefinition.ts
@@ -303,7 +303,7 @@ export class StructureDefinition {
    * Exports the StructureDefinition to a properly formatted FHIR JSON representation.
    * @returns {any} the FHIR JSON representation of the StructureDefinition
    */
-  toJSON(): any {
+  toJSON(snapshot = true): any {
     const j: LooseStructDefJSON = { resourceType: this.resourceType };
     // First handle properties that are just straight translations to JSON
     for (const prop of PROPS) {
@@ -315,14 +315,14 @@ export class StructureDefinition {
     }
 
     // Now handle snapshot and differential
-    j.snapshot = { element: this.elements.map(e => e.toJSON()) };
+    if (snapshot) {
+      j.snapshot = { element: this.elements.map(e => e.toJSON()) };
+    }
 
     const differentialElements = this.elements
       .filter(e => e.hasDiff())
       .map(e => e.calculateDiff().toJSON());
-    const defaultDifferentialElements = [
-      { id: j.snapshot.element[0].id, path: j.snapshot.element[0].path }
-    ];
+    const defaultDifferentialElements = [{ id: this.elements[0].id, path: this.elements[0].path }];
 
     j.differential = {
       element: differentialElements.length > 0 ? differentialElements : defaultDifferentialElements


Test patch:
diff --git a/test/fhirtypes/StructureDefinition.test.ts b/test/fhirtypes/StructureDefinition.test.ts
index 3ef143d..82d74b2 100644
--- a/test/fhirtypes/StructureDefinition.test.ts
+++ b/test/fhirtypes/StructureDefinition.test.ts
@@ -122,11 +122,38 @@ describe('StructureDefinition', () => {
       });
     });
 
+    it('should reflect differentials for elements that changed after capturing originals without generating snapshot', () => {
+      const code = observation.elements.find(e => e.id === 'Observation.code');
+      code.short = 'Special observation code';
+      const valueX = observation.elements.find(e => e.id === 'Observation.value[x]');
+      valueX.min = 1;
+
+      const json = observation.toJSON(false);
+      expect(json.differential.element).toHaveLength(2);
+      expect(json.differential.element[0]).toEqual({
+        id: 'Observation.code',
+        path: 'Observation.code',
+        short: 'Special observation code'
+      });
+      expect(json.differential.element[1]).toEqual({
+        id: 'Observation.value[x]',
+        path: 'Observation.value[x]',
+        min: 1
+      });
+      expect(json.snapshot).toBeUndefined();
+    });
+
     it('should reflect basic differential for structure definitions with no changes', () => {
       const json = observation.toJSON();
       expect(json.differential).toEqual({ element: [{ id: 'Observation', path: 'Observation' }] });
     });
 
+    it('should reflect basic differential for structure definitions with no changes without generating snapshot', () => {
+      const json = observation.toJSON(false);
+      expect(json.differential).toEqual({ element: [{ id: 'Observation', path: 'Observation' }] });
+      expect(json.snapshot).toBeUndefined();
+    });
+
     it('should properly serialize snapshot and differential for constrained choice type with constraints on specific choices', () => {
       // constrain value[x] to only a Quantity or string and give each its own short
       const valueX = observation.elements.find(e => e.id === 'Observation.value[x]');