Repository: FHIR/sushi
Pull request #899 (2021-08-04 18:40:59)

Update SUSHI --init to avoid publisher errors
Fixes https://github.com/FHIR/sushi/issues/884.

`sushi --init` was causing errors and warnings in the IG Publisher. These turned out to be related to:
* Not having any examples of the `MyPatient` profile
* Not having any description on the `MyPatient` profile
* Not having a `publisher` set

So to fix this, this PR updates the `patient.fsh` file to have an example and a description. It also updates the default `sushi-config.yaml` file to contain default values for `publisher.name` and `publisher.url`, and updates the `init()` function so that the user can specify custom values for these fields.

# Testing
To test this, try doing `sushi --init` on `master`, and build with the publisher with `_genonce`. The QA page, `output/qa.html`, will have several errors and warnings. Then do the same on this branch, and the QA page should look clean, like this: 
![image](https://user-images.githubusercontent.com/52747882/128236405-acf9360b-e5db-4774-93e8-f2ff35201d56.png)

The QA page should definitely be clean if you use default values, and should likely be clean if you use custom values, but note that certain custom values could lead to warnings. But either way make sure to test both approaches. Ensure that the publisher value you use is inserted in the footer of pages as shown here:
![image](https://user-images.githubusercontent.com/52747882/128236556-25923480-29c8-4e52-a7de-b17579f979ba.png)



Patch:
diff --git a/src/utils/Processing.ts b/src/utils/Processing.ts
index cecd5ca..8a49f53 100644
--- a/src/utils/Processing.ts
+++ b/src/utils/Processing.ts
@@ -3,7 +3,7 @@ import path from 'path';
 import fs from 'fs-extra';
 import readlineSync from 'readline-sync';
 import YAML from 'yaml';
-import { isPlainObject, padEnd, sortBy } from 'lodash';
+import { isPlainObject, padEnd, sortBy, upperFirst } from 'lodash';
 import { EOL } from 'os';
 import { logger } from './FSHLogger';
 import { loadDependency, loadSupplementalFHIRPackage, FHIRDefinitions } from '../fhirdefs';
@@ -432,12 +432,23 @@ export async function init(): Promise<void> {
   // Accept user input for certain fields
   ['name', 'id', 'canonical', 'status', 'version'].forEach(field => {
     const userValue = readlineSync.question(
-      `${field.charAt(0).toUpperCase() + field.slice(1)} (Default: ${configDoc.get(field)}): `
+      `${upperFirst(field)} (Default: ${configDoc.get(field)}): `
     );
     if (userValue) {
       configDoc.set(field, userValue);
     }
   });
+
+  // And for nested publisher fields
+  ['name', 'url'].forEach(field => {
+    const userValue = readlineSync.question(
+      `Publisher ${upperFirst(field)} (Default: ${configDoc.get('publisher').get(field)}): `
+    );
+    if (userValue) {
+      configDoc.get('publisher').set(field, userValue);
+    }
+  });
+
   // Ensure copyrightYear is accurate
   configDoc.set('copyrightYear', `${new Date().getFullYear()}+`);
   const projectName = configDoc.get('name');
diff --git a/src/utils/init-project/patient.fsh b/src/utils/init-project/patient.fsh
index e2ac4eb..5ba8d7a 100644
--- a/src/utils/init-project/patient.fsh
+++ b/src/utils/init-project/patient.fsh
@@ -3,4 +3,12 @@
 // SUSHI will look for definitions in any file using the .fsh ending.
 Profile: MyPatient
 Parent: Patient
-* name 1..* MS
\ No newline at end of file
+Description: "An example profile of the Patient resource."
+* name 1..* MS
+
+Instance: PatientExample
+InstanceOf: MyPatient
+Description: "An example of a patient with a license to krill."
+* name
+  * given[0] = "James"
+  * family = "Pond"
\ No newline at end of file
diff --git a/src/utils/init-project/sushi-config.yaml b/src/utils/init-project/sushi-config.yaml
index 4ce8303..05c4259 100644
--- a/src/utils/init-project/sushi-config.yaml
+++ b/src/utils/init-project/sushi-config.yaml
@@ -10,6 +10,9 @@ version: 0.1.0
 fhirVersion: 4.0.1
 copyrightYear: 2020+
 releaseLabel: ci-build
+publisher:
+  name: Example Publisher
+  url: http://example.org/example-publisher
 
 # ╭────────────────────────────────────────────menu.xml────────────────────────────────────────────╮
 # │  To use a provided input/includes/menu.xml file, delete the "menu" property below.             │


Test patch:
diff --git a/test/utils/Processing.test.ts b/test/utils/Processing.test.ts
index a20c1df..be279d5 100644
--- a/test/utils/Processing.test.ts
+++ b/test/utils/Processing.test.ts
@@ -1042,7 +1042,9 @@ describe('Processing', () => {
         ['Id (Default: fhir.example): '],
         ['Canonical (Default: http://example.org): '],
         ['Status (Default: draft): '],
-        ['Version (Default: 0.1.0): ']
+        ['Version (Default: 0.1.0): '],
+        ['Publisher Name (Default: Example Publisher): '],
+        ['Publisher Url (Default: http://example.org/example-publisher): ']
       ]);
       expect(yesNoSpy.mock.calls).toHaveLength(1);
       expect(yesNoSpy.mock.calls[0][0]).toMatch(/Initialize SUSHI project in .*ExampleIG/);
@@ -1100,6 +1102,10 @@ describe('Processing', () => {
           return 'active';
         } else if (question.startsWith('Version')) {
           return '2.0.0';
+        } else if (question.startsWith('Publisher Name')) {
+          return 'SUSHI Chefs';
+        } else if (question.startsWith('Publisher Url')) {
+          return 'http://custom-publisher.org';
         }
       });
       await init();
@@ -1108,7 +1114,9 @@ describe('Processing', () => {
         ['Id (Default: fhir.example): '],
         ['Canonical (Default: http://example.org): '],
         ['Status (Default: draft): '],
-        ['Version (Default: 0.1.0): ']
+        ['Version (Default: 0.1.0): '],
+        ['Publisher Name (Default: Example Publisher): '],
+        ['Publisher Url (Default: http://example.org/example-publisher): ']
       ]);
       expect(yesNoSpy.mock.calls).toHaveLength(1);
       expect(yesNoSpy.mock.calls[0][0]).toMatch(/Initialize SUSHI project in .*MyNonDefaultName/);
@@ -1164,7 +1172,9 @@ describe('Processing', () => {
         ['Id (Default: fhir.example): '],
         ['Canonical (Default: http://example.org): '],
         ['Status (Default: draft): '],
-        ['Version (Default: 0.1.0): ']
+        ['Version (Default: 0.1.0): '],
+        ['Publisher Name (Default: Example Publisher): '],
+        ['Publisher Url (Default: http://example.org/example-publisher): ']
       ]);
       expect(yesNoSpy.mock.calls).toHaveLength(1);
       expect(yesNoSpy.mock.calls[0][0]).toMatch(/Initialize SUSHI project in .*ExampleIG/);
diff --git a/test/utils/fixtures/init-config/default-config.yaml b/test/utils/fixtures/init-config/default-config.yaml
index b0da426..5ee8e2c 100644
--- a/test/utils/fixtures/init-config/default-config.yaml
+++ b/test/utils/fixtures/init-config/default-config.yaml
@@ -10,6 +10,9 @@ version: 0.1.0
 fhirVersion: 4.0.1
 copyrightYear: 2021+
 releaseLabel: ci-build
+publisher:
+  name: Example Publisher
+  url: http://example.org/example-publisher
 
 # ╭────────────────────────────────────────────menu.xml────────────────────────────────────────────╮
 # │  To use a provided input/includes/menu.xml file, delete the "menu" property below.             │
diff --git a/test/utils/fixtures/init-config/user-input-config.yaml b/test/utils/fixtures/init-config/user-input-config.yaml
index 2dd7c1f..758d668 100644
--- a/test/utils/fixtures/init-config/user-input-config.yaml
+++ b/test/utils/fixtures/init-config/user-input-config.yaml
@@ -10,6 +10,9 @@ version: 2.0.0
 fhirVersion: 4.0.1
 copyrightYear: 2021+
 releaseLabel: ci-build
+publisher:
+  name: SUSHI Chefs
+  url: http://custom-publisher.org
 
 # ╭────────────────────────────────────────────menu.xml────────────────────────────────────────────╮
 # │  To use a provided input/includes/menu.xml file, delete the "menu" property below.             │