Repository: FHIR/sushi (ts)

Issue:
Extension use in arrays: Cannot convert undefined or null to object
This works fine:
```
* name[0].given[0] = "John"
* name[0].given[1] = "Bob"
* name[0].given[0].extension[nameQualifier].valueCode = #CL 
```
This gives "SUSHI encountered the following unexpected error: Cannot convert undefined or null to object":
```
* name[0].given[0] = "John"
* name[0].given[1] = "Bob"
* name[0].given[1].extension[nameQualifier].valueCode = #CL
```
and this is the extension:
```
Profile: SEBaseHumanName
Parent: http://hl7.org/fhir/StructureDefinition/HumanName
Description: "This profile provides the use of Swedish middlename as extensions and is used in the base Patient profile"
* insert SEStandardRuleSet
* extension contains SEBaseMiddleNameExtension named middleName 0..1
* extension contains SEBaseOwnFamilyExtension named ownFamily 0..1
* given.extension contains http://hl7.org/fhir/StructureDefinition/iso21090-EN-qualifier named nameQualifier 0..1
* given.extension[nameQualifier].valueCode from SEBaseNamePartQualifierVS (required)
```
Any thoughts? If the extension is used on both given:s it also works but that is not what is intended.


Pull request:
Fix array extensions
Fixes #733 (problem w/ extensions on non-zero index of primitive arrays).

To reproduce, isolate commit b65e302 and run the test.  It will fail.  Then run the test after applying 95b531e.  It will pass.

OR test manually w/ the following FSH:
```
Profile: MyPatient
Parent: Patient
* address.line.extension contains
    http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-additionalLocator named locator 0..1

 Instance: SomePatient
 InstanceOf: MyPatient
 * address.line[1].extension[locator].valueString = "3rd Floor"
```

Patch:
diff --git a/src/fhirtypes/InstanceDefinition.ts b/src/fhirtypes/InstanceDefinition.ts
index 360389a..e55e6c8 100644
--- a/src/fhirtypes/InstanceDefinition.ts
+++ b/src/fhirtypes/InstanceDefinition.ts
@@ -1,4 +1,4 @@
-import { difference, remove, pull, cloneDeep } from 'lodash';
+import { difference, remove, pull, cloneDeep, isObjectLike } from 'lodash';
 import { Meta } from './specialTypes';
 import { HasId } from './common';
 import { applyMixins } from '../utils';
@@ -53,17 +53,20 @@ export class InstanceDefinition {
  * Otherwise, use all properties from the input.
  *
  * @param input - the value to clone
- * @param keys - optionally, the properties of the value to include in the clone
+ * @param keys - optionally, the properties of the value to include in the clone, defaults to input keys if not specified
  * @returns {any} - a clone of the input, with reordered properties
  */
-function orderedCloneDeep(input: any, keys: string[] = Object.keys(input)): any {
+function orderedCloneDeep(input: any, keys?: string[]): any {
   // non-objects should be cloned normally
   // arrays should get a recursive call on their elements, but don't need reordering
-  if (typeof input !== 'object') {
+  if (!isObjectLike(input)) {
     return cloneDeep(input);
   } else if (Array.isArray(input)) {
     return input.map(element => orderedCloneDeep(element));
   } else {
+    if (keys == null) {
+      keys = Object.keys(input);
+    }
     const underscoreKeys = remove(keys, key => key.startsWith('_'));
     const orderedKeys: string[] = [];
     const result: any = {};


Test patch:
diff --git a/test/export/InstanceExporter.test.ts b/test/export/InstanceExporter.test.ts
index d3ea3c2..11dafb8 100644
--- a/test/export/InstanceExporter.test.ts
+++ b/test/export/InstanceExporter.test.ts
@@ -1998,6 +1998,51 @@ describe('InstanceExporter', () => {
       });
     });
 
+    it('should handle extensions on non-zero element of primitive arrays', () => {
+      // * address.line.extension contains
+      //     http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-additionalLocator named locator 0..1
+      const containsRule = new ContainsRule('address.line.extension');
+      containsRule.items.push({
+        name: 'locator',
+        type: 'http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-additionalLocator'
+      });
+      const extensionCard = new CardRule('address.line.extension[locator]');
+      extensionCard.min = 0;
+      extensionCard.max = '1';
+      patient.rules.push(containsRule, extensionCard);
+
+      // * address.line[1].extension[locator] = "3rd floor"
+      const loc = new AssignmentRule('address.line[1].extension[locator].valueString');
+      loc.value = '3rd Floor';
+      patientInstance.rules.push(loc);
+
+      // In-memory result should be correct
+      const result = exportInstance(patientInstance);
+      expect(result.address).toHaveLength(1);
+      expect(result.address[0].line).toBeUndefined();
+      expect(result.address[0]._line).toHaveLength(2);
+      expect(result.address[0]._line[0]).toBeNull();
+      expect(result.address[0]._line[1].extension).toEqual([
+        {
+          url: 'http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-additionalLocator',
+          valueString: '3rd Floor'
+        }
+      ]);
+
+      // JSON representation should be correct
+      const json = result.toJSON();
+      expect(json.address).toHaveLength(1);
+      expect(json.address[0].line).toBeUndefined();
+      expect(json.address[0]._line).toHaveLength(2);
+      expect(json.address[0]._line[0]).toBeNull();
+      expect(json.address[0]._line[1].extension).toEqual([
+        {
+          url: 'http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-additionalLocator',
+          valueString: '3rd Floor'
+        }
+      ]);
+    });
+
     describe('#Inline Instances', () => {
       beforeEach(() => {
         const inlineInstance = new Instance('MyInlinePatient');
diff --git a/test/testhelpers/testdefs/package/StructureDefinition-iso21090-ADXP-additionalLocator.json b/test/testhelpers/testdefs/package/StructureDefinition-iso21090-ADXP-additionalLocator.json
new file mode 100644
index 0000000..8c7fcf6
--- /dev/null
+++ b/test/testhelpers/testdefs/package/StructureDefinition-iso21090-ADXP-additionalLocator.json
@@ -0,0 +1,1 @@
+{"resourceType":"StructureDefinition","id":"iso21090-ADXP-additionalLocator","extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg","valueCode":"fhir"},{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm","valueInteger":1}],"url":"http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-additionalLocator","version":"4.0.1","name":"ADXP-additionalLocator","status":"draft","date":"2012-06-24","publisher":"Health Level Seven International (Modeling and Methodology)","contact":[{"telecom":[{"system":"url","value":"http://www.hl7.org/Special/committees/mnm"}]}],"description":"This can be a unit designator, such as apartment number, suite number, or floor. There may be several unit designators in an address (e.g., \"3rd floor, Appt. 342\"). This can also be a designator pointing away from the location, rather than specifying a smaller location within some larger one (e.g., Dutch \"t.o.\" means \"opposite to\" for house boats located across the street facing houses).","fhirVersion":"4.0.1","mapping":[{"identity":"rim","uri":"http://hl7.org/v3","name":"RIM Mapping"}],"kind":"complex-type","abstract":false,"context":[{"type":"element","expression":"Address.line"}],"type":"Extension","baseDefinition":"http://hl7.org/fhir/StructureDefinition/Extension","derivation":"constraint","snapshot":{"element":[{"id":"Extension","path":"Extension","short":"additionalLocator","definition":"This can be a unit designator, such as apartment number, suite number, or floor. There may be several unit designators in an address (e.g., \"3rd floor, Appt. 342\"). This can also be a designator pointing away from the location, rather than specifying a smaller location within some larger one (e.g., Dutch \"t.o.\" means \"opposite to\" for house boats located across the street facing houses).","min":0,"max":"*","base":{"path":"Extension","min":0,"max":"*"},"condition":["ele-1"],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"},{"key":"ext-1","severity":"error","human":"Must have either extensions or value[x], not both","expression":"extension.exists() != value.exists()","xpath":"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])","source":"http://hl7.org/fhir/StructureDefinition/Extension"}],"isModifier":false,"mapping":[{"identity":"rim","map":"ADXP[partType=ADL]"}]},{"id":"Extension.id","path":"Extension.id","representation":["xmlAttr"],"short":"Unique id for inter-element referencing","definition":"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.","min":0,"max":"1","base":{"path":"Element.id","min":0,"max":"1"},"type":[{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type","valueUrl":"string"}],"code":"http://hl7.org/fhirpath/System.String"}],"isModifier":false,"isSummary":false,"mapping":[{"identity":"rim","map":"n/a"}]},{"id":"Extension.extension","path":"Extension.extension","slicing":{"discriminator":[{"type":"value","path":"url"}],"description":"Extensions are always sliced by (at least) url","rules":"open"},"short":"Extension","definition":"An Extension","min":0,"max":"0","base":{"path":"Element.extension","min":0,"max":"*"},"type":[{"code":"Extension"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"},{"key":"ext-1","severity":"error","human":"Must have either extensions or value[x], not both","expression":"extension.exists() != value.exists()","xpath":"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \"value\")])","source":"http://hl7.org/fhir/StructureDefinition/Extension"}],"isModifier":false,"isSummary":false},{"id":"Extension.url","path":"Extension.url","representation":["xmlAttr"],"short":"identifies the meaning of the extension","definition":"Source of the definition for the extension code - a logical name or a URL.","comment":"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.","min":1,"max":"1","base":{"path":"Extension.url","min":1,"max":"1"},"type":[{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type","valueUrl":"uri"}],"code":"http://hl7.org/fhirpath/System.String"}],"fixedUri":"http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-additionalLocator","isModifier":false,"isSummary":false,"mapping":[{"identity":"rim","map":"N/A"}]},{"id":"Extension.value[x]","path":"Extension.value[x]","short":"Value of extension","definition":"Value of extension - must be one of a constrained set of the data types (see [Extensibility](http://hl7.org/fhir/extensibility.html) for a list).","min":1,"max":"1","base":{"path":"Extension.value[x]","min":0,"max":"1"},"type":[{"code":"string"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"isSummary":false,"mapping":[{"identity":"rim","map":"N/A"}]}]},"differential":{"element":[{"id":"Extension","path":"Extension","short":"additionalLocator","definition":"This can be a unit designator, such as apartment number, suite number, or floor. There may be several unit designators in an address (e.g., \"3rd floor, Appt. 342\"). This can also be a designator pointing away from the location, rather than specifying a smaller location within some larger one (e.g., Dutch \"t.o.\" means \"opposite to\" for house boats located across the street facing houses).","min":0,"max":"*","mapping":[{"identity":"rim","map":"ADXP[partType=ADL]"}]},{"id":"Extension.extension","path":"Extension.extension","max":"0"},{"id":"Extension.url","path":"Extension.url","fixedUri":"http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-additionalLocator"},{"id":"Extension.value[x]","path":"Extension.value[x]","min":1,"type":[{"code":"string"}]}]}}
\ No newline at end of file