From 0b76561af27e442241dd6706b75011a4b17d13dc Mon Sep 17 00:00:00 2001 From: Dylan Date: Fri, 24 May 2019 12:15:38 -0400 Subject: [PATCH 1/6] initial impl of adding fhirType to fromFHIR signature --- lib/generateClass.js | 22 +++++++++++----------- lib/generateFactory.js | 8 ++++---- lib/generateNamespaceFactory.js | 10 +++++----- lib/includes/json-helper.js | 9 +++++---- test/es6FromFHIRJSONTest.js | 6 +++--- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/lib/generateClass.js b/lib/generateClass.js index db1edb3..0d9e504 100644 --- a/lib/generateClass.js +++ b/lib/generateClass.js @@ -159,7 +159,7 @@ function generateClassBody(def, specs, fhir, fhirProfile, fhirExtension, cw) { .ln(`@param {Array} referencesOut - list of all SHR ref() targets that were instantiated during this function call`) .ln(`@param {boolean} asExtension - Whether the provided instance is an extension`) .ln(`@returns {${clazzName}} An instance of ${clazzName} populated with the FHIR data`); - }).bl('static fromFHIR(fhir, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => writeFromFhir(def, specs, fhir, fhirProfile, fhirExtension, cw)) + }).bl('static fromFHIR(fhir, fhirType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => writeFromFhir(def, specs, fhir, fhirProfile, fhirExtension, cw)) .ln(); } @@ -474,13 +474,13 @@ function writeFromFhir(def, specs, fhir, fhirProfile, fhirExtension, cw) { cw.ln(`const inst = new ${className(def.identifier.name)}();`); if (def.isEntry) { - cw.ln(`inst.entryInfo = FHIRHelper.createInstanceFromFHIR('shr.base.Entry', {});`); // do it this way so we don't have to import Entry - cw.ln(`inst.entryInfo.shrId = FHIRHelper.createInstanceFromFHIR('shr.base.ShrId', shrId);`); - cw.ln(`inst.entryInfo.entryId = FHIRHelper.createInstanceFromFHIR('shr.base.EntryId', fhir['id'] || uuid());`); // re-use the FHIR id if it exists, otherwise generate a new uuid + cw.ln(`inst.entryInfo = FHIRHelper.createInstanceFromFHIR('shr.base.Entry', {}, null);`); // do it this way so we don't have to import Entry + cw.ln(`inst.entryInfo.shrId = FHIRHelper.createInstanceFromFHIR('shr.base.ShrId', shrId, 'string');`); + cw.ln(`inst.entryInfo.entryId = FHIRHelper.createInstanceFromFHIR('shr.base.EntryId', fhir['id'] || uuid(), 'string');`); // re-use the FHIR id if it exists, otherwise generate a new uuid // copied from writeToJson above --- should this URL be configurable? const url = `http://standardhealthrecord.org/spec/${def.identifier.namespace.replace('.', '/')}/${className(def.identifier.name)}`; - cw.ln(`inst.entryInfo.entryType = FHIRHelper.createInstanceFromFHIR('shr.base.EntryType', '${url}');`); + cw.ln(`inst.entryInfo.entryType = FHIRHelper.createInstanceFromFHIR('shr.base.EntryType', '${url}', 'uri');`); } if(fhirProfile){ @@ -703,7 +703,7 @@ function writeFromFhirProfile(def, specs, fhir, fhirProfile, cw) { const dec = field.card.isList ? 'const ' : ''; // if in a list, we need to declare the new variable, so do `const x = new()` const nullCheck = field.card.isList ? '' : `${shrElementPath} || `; // if not in a list, consider that the field was already init'ed, so do `x = x || new()` - let rhs = `FHIRHelper.createInstanceFromFHIR('${field.effectiveIdentifier.fqn}', {}, shrId)`; + let rhs = `FHIRHelper.createInstanceFromFHIR('${field.effectiveIdentifier.fqn}', {}, null, shrId)`; if (field instanceof RefValue) { rhs = `FHIRHelper.createReference( ${rhs}, referencesOut)`; } @@ -805,7 +805,7 @@ function writeFromFhirExtension(def, specs, fhir, fhirExtension, cw) { const varName = `match_${i}`; // ensure a unique variable name here cw.ln(`const ${varName} = fhir['extension'].find(e => e.url == '${profileUrl}');`); cw.bl(`if (${varName} != null)`, () => { - cw.ln(`inst.${methodName} = FHIRHelper.createInstanceFromFHIR('${instance}', ${varName}, shrId, allEntries, mappedResources, referencesOut, true);`); // asExtension = true here, false(default value) everywhere else + cw.ln(`inst.${methodName} = FHIRHelper.createInstanceFromFHIR('${instance}', ${varName}, 'Extension', shrId, allEntries, mappedResources, referencesOut, true);`); // asExtension = true here, false(default value) everywhere else }); } }); @@ -827,12 +827,12 @@ function writeFromFhirValue(def, specs, cw) { cw.ln(`inst.value = fhir;`); } else { const shrType = def.value.effectiveIdentifier.fqn; - cw.ln(`inst.value = FHIRHelper.createInstanceFromFHIR('${shrType}', fhir, shrId, allEntries, mappedResources, referencesOut);`); + cw.ln(`inst.value = FHIRHelper.createInstanceFromFHIR('${shrType}', fhir, fhirType, shrId, allEntries, mappedResources, referencesOut);`); } } else { // it could be any of the options, and we can't necessarily tell which one here // so just call createInstance to leverage the logic that looks up profiles - cw.ln(`inst.value = FHIRHelper.createInstanceFromFHIR(null, fhir, shrId, allEntries, mappedResources, referencesOut);`); + cw.ln(`inst.value = FHIRHelper.createInstanceFromFHIR(null, fhir, fhirType, shrId, allEntries, mappedResources, referencesOut);`); } }); } @@ -1704,7 +1704,7 @@ function generateFromFHIRAssignment(field, fhirElement, fhirElementPath, shrElem const parts = profileUrl.split('/'); shrType = parts[parts.length - 1].replace(/-/g, '.'); - cw.ln(`mappedResources[entryId] = FHIRHelper.createInstanceFromFHIR('${shrType}', referencedEntry['resource'], shrId, allEntries, mappedResources, referencesOut);`); + cw.ln(`mappedResources[entryId] = FHIRHelper.createInstanceFromFHIR('${shrType}', referencedEntry['resource'], /* TODO: */ null, shrId, allEntries, mappedResources, referencesOut);`); }); }); @@ -1740,7 +1740,7 @@ function generateFromFHIRAssignment(field, fhirElement, fhirElementPath, shrElem if (shrType.isPrimitive && !isExtension) { rhs = fhirPathString; } else { - rhs = `FHIRHelper.createInstanceFromFHIR('${shrType.fqn}', ${fhirPathString}, shrId, allEntries, mappedResources, referencesOut, ${isExtension})`; + rhs = `FHIRHelper.createInstanceFromFHIR('${shrType.fqn}', ${fhirPathString}, 'Extension', shrId, allEntries, mappedResources, referencesOut, ${isExtension})`; } if (isRef) { rhs = `FHIRHelper.createReference( ${rhs}, referencesOut)`; diff --git a/lib/generateFactory.js b/lib/generateFactory.js index 49f45be..944cd86 100644 --- a/lib/generateFactory.js +++ b/lib/generateFactory.js @@ -41,15 +41,15 @@ function generateFactory(namespaces) { cw.blComment(() => { cw.ln('Create an instance of a class from its FHIR representation.') .ln('@param {Object} fhir - The element data in FHIR format (use `{}` and provide `type` for a blank instance)') - .ln(`@param {string} [type] - The (optional) type of the element (e.g., 'http://standardhealthrecord.org/spec/shr/demographics/PersonOfRecord'). This is only used if the type cannot be extracted from the JSON.`) + .ln(`@param {string} shrType - The (optional) type of the element (e.g., 'http://standardhealthrecord.org/spec/shr/demographics/PersonOfRecord'). This is only used if the type cannot be extracted from the JSON.`) .ln('@returns {Object} An instance of the requested class populated with the provided data'); }) - .bl('static createInstanceFromFHIR(fhir, type, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => { - cw.ln('const { namespace } = getNamespaceAndNameFromFHIR(fhir, type);') + .bl('static createInstanceFromFHIR(fhir, fhirType, shrType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => { + cw.ln('const { namespace } = getNamespaceAndNameFromFHIR(fhir, shrType);') .ln('switch (namespace) {'); for (const ns of namespaces) { const factory = factoryName(ns.namespace); - cw.ln(`case '${ns.namespace}': return ${factory}.createInstanceFromFHIR(fhir, type, shrId, allEntries, mappedResources, referencesOut, asExtension);`); + cw.ln(`case '${ns.namespace}': return ${factory}.createInstanceFromFHIR(fhir, fhirType, shrType, shrId, allEntries, mappedResources, referencesOut, asExtension);`); } cw.ln(`case 'primitive': return fhir;`); cw.ln(`default: throw new Error(\`Unsupported namespace: \${namespace}\`);`) diff --git a/lib/generateNamespaceFactory.js b/lib/generateNamespaceFactory.js index 7a0d184..57887f1 100644 --- a/lib/generateNamespaceFactory.js +++ b/lib/generateNamespaceFactory.js @@ -46,17 +46,17 @@ function generateNamespaceFactory(ns, defs) { .ln(`@param {string} [type] - The (optional) type of the element (e.g., 'http://standardhealthrecord.org/spec/shr/demographics/PersonOfRecord'). This is only used if the type cannot be extracted from the JSON.`) .ln('@returns {Object} An instance of the requested class populated with the provided data'); }) - .bl('static createInstanceFromFHIR(fhir, type, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => { - cw.ln('const { namespace, elementName } = getNamespaceAndNameFromFHIR(fhir, type);') + .bl('static createInstanceFromFHIR(fhir, fhirType, shrType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => { + cw.ln('const { namespace, elementName } = getNamespaceAndNameFromFHIR(fhir, shrType);') .bl(`if (namespace !== '${ns.namespace}')`, () => { - cw.ln(`throw new Error(\`Unsupported type in ${factory}: \${type}\`);`); + cw.ln(`throw new Error(\`Unsupported type in ${factory}: \${shrType}\`);`); }) .ln('switch (elementName) {'); for (const def of defs) { const elName = className(def.identifier.name); - cw.ln(`case '${elName}': return ${elName}.fromFHIR(fhir, shrId, allEntries, mappedResources, referencesOut, asExtension);`); + cw.ln(`case '${elName}': return ${elName}.fromFHIR(fhir, fhirType, shrId, allEntries, mappedResources, referencesOut, asExtension);`); } - cw.ln(`default: throw new Error(\`Unsupported type in ${factory}: \${type}\`);`) + cw.ln(`default: throw new Error(\`Unsupported type in ${factory}: \${shrType}\`);`) .ln('}'); }); }); diff --git a/lib/includes/json-helper.js b/lib/includes/json-helper.js index e1ef3fd..8119ea0 100644 --- a/lib/includes/json-helper.js +++ b/lib/includes/json-helper.js @@ -67,6 +67,7 @@ export function getNamespaceAndNameFromFHIR(fhir, type) { } // Ensure we have a type before proceeding if (!type) { + debugger; throw new Error(`Couldn't find type for FHIR: ${JSON.stringify(fhir)}`); } @@ -200,14 +201,14 @@ export const FHIRHelper = { * @returns {object} an instance of an ES6 class representing the data * @private */ - createInstanceFromFHIR: function(key, value, shrId, allEntries=[], mappedResources={}, referencesOut=[], asExtension=false) { - if (Array.isArray(value)) { - return value.map(v => FHIRHelper.createInstanceFromFHIR(key, v, shrId, allEntries, mappedResources, referencesOut, asExtension)); + createInstanceFromFHIR: function(shrType, fhir, fhirType, shrId, allEntries=[], mappedResources={}, referencesOut=[], asExtension=false) { + if (Array.isArray(fhir)) { + return fhir.map(v => FHIRHelper.createInstanceFromFHIR(shrType, v, fhirType, shrId, allEntries, mappedResources, referencesOut, asExtension)); } if (OBJECT_FACTORY == null) { throw new Error(`SHR ES6 module is not initialized. Import 'init' before using the ES6 factories and classes`); } - return OBJECT_FACTORY.createInstanceFromFHIR(value, key, shrId, allEntries, mappedResources, referencesOut, asExtension); + return OBJECT_FACTORY.createInstanceFromFHIR(fhir, fhirType, shrType, shrId, allEntries, mappedResources, referencesOut, asExtension); }, /** diff --git a/test/es6FromFHIRJSONTest.js b/test/es6FromFHIRJSONTest.js index daa3fbe..02295cd 100644 --- a/test/es6FromFHIRJSONTest.js +++ b/test/es6FromFHIRJSONTest.js @@ -370,7 +370,7 @@ describe('#FromFHIR_STU3', () => { const allEntries = [json, memberA, memberB].map(j => { return { fullUrl: `http://example.org/fhir/Observation/${j.id}`, resource: j }; }); - const entry = PanelSliceByProfile.fromFHIR(json, '12345', allEntries); + const entry = PanelSliceByProfile.fromFHIR(json, 'Observation', '12345', allEntries); expect(entry).instanceOf(PanelSliceByProfile); const expected = new PanelSliceByProfile() @@ -420,7 +420,7 @@ describe('#FromFHIR_STU3', () => { it('should deserialize a FHIR JSON instance', () => { const json = context.getFHIR('Observation'); - const entry = Observation.fromFHIR(json, '1-1'); + const entry = Observation.fromFHIR(json, 'Observation', '1-1'); expect(entry).instanceOf(Observation); const expected = new Observation() @@ -462,7 +462,7 @@ describe('#FromFHIR_DSTU2', () => { it('should deserialize a FHIR JSON instance', () => { const json = context.getFHIR('Observation'); - const entry = Observation.fromFHIR(json, '1-1'); + const entry = Observation.fromFHIR(json, 'Observation', '1-1'); expect(entry).instanceOf(Observation); const expected = new Observation() From bcd3d87c90bee741eea3f3edd05bed0967f66cef Mon Sep 17 00:00:00 2001 From: Dylan Date: Fri, 24 May 2019 14:04:05 -0400 Subject: [PATCH 2/6] little more robust --- lib/generateClass.js | 19 +++++++++++++------ test/fixtures/spec/shr_slicing.txt | 4 ++++ test/fixtures/spec/shr_slicing_map_dstu2.txt | 1 + test/fixtures/spec/shr_slicing_map_stu3.txt | 1 + 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/generateClass.js b/lib/generateClass.js index 0d9e504..421c513 100644 --- a/lib/generateClass.js +++ b/lib/generateClass.js @@ -655,7 +655,7 @@ function writeFromFhirProfile(def, specs, fhir, fhirProfile, cw) { if(mapping.map === ''){ // Mapping to the value of this es6 instance if (def.value instanceof IdentifiableValue && def.value.identifier.isPrimitive) { - generateFromFHIRAssignment(def.value, element, fhirElementPath, [], 'value', fhirProfile, null, cw); + generateFromFHIRAssignment(def.value, element, fhirElementPath, [], 'value', fhirProfile, null, cw, fhir); } else { logger.error('Value referenced in mapping but none exist on this element.'); } @@ -697,7 +697,7 @@ function writeFromFhirProfile(def, specs, fhir, fhirProfile, cw) { } if (i == mapping.fieldChain.length - 1) { // if it's the last field in the chain - generateFromFHIRAssignment(field, element, fhirElementPath, mapping.fieldMapPath, shrElementPath, fhirProfile, slicing, cw); + generateFromFHIRAssignment(field, element, fhirElementPath, mapping.fieldMapPath, shrElementPath, fhirProfile, slicing, cw, fhir); } else { // if it's not the last element in the field chain, it's an intermediate one so we just want to initialize the value so it's not null const dec = field.card.isList ? 'const ' : ''; // if in a list, we need to declare the new variable, so do `const x = new()` @@ -1683,8 +1683,9 @@ function generateToFHIRAssignment(cardIsList, baseCardIsList, constraintsLength, * @param {StructureDefinition} fhirProfile - the FHIR profile the element comes from * @param {Object} slicing - information on this element related to slicing, if any * @param {CodeWriter} cw - the CodeWriter that is writing the file for this element + * @param {object} fhir - All exported FHIR profiles and extensions. */ -function generateFromFHIRAssignment(field, fhirElement, fhirElementPath, shrElementMapping, shrElementPath, fhirProfile, slicing, cw) { +function generateFromFHIRAssignment(field, fhirElement, fhirElementPath, shrElementMapping, shrElementPath, fhirProfile, slicing, cw, fhir) { const cardIsList = field.card.isList; const isRef = field instanceof RefValue; const fhirPathString = bracketNotation(fhirElementPath); @@ -1702,9 +1703,14 @@ function generateFromFHIRAssignment(field, fhirElement, fhirElementPath, shrElem cw.bl('if (referencedEntry)', () => { const profileUrl = getTargetProfile(fhirElement, fhirProfile.fhirVersion); const parts = profileUrl.split('/'); - shrType = parts[parts.length - 1].replace(/-/g, '.'); + shrType = parts[parts.length - 1]; + + const allFHIRProfiles = [...fhir.profiles, ...fhir._noDiffProfiles]; + const matchingProfile = allFHIRProfiles.find(p => p.id === shrType); + const fhirType = matchingProfile ? `'${matchingProfile.type}'` : null; - cw.ln(`mappedResources[entryId] = FHIRHelper.createInstanceFromFHIR('${shrType}', referencedEntry['resource'], /* TODO: */ null, shrId, allEntries, mappedResources, referencesOut);`); + shrType = shrType.replace(/-/g, '.'); + cw.ln(`mappedResources[entryId] = FHIRHelper.createInstanceFromFHIR('${shrType}', referencedEntry['resource'], ${fhirType}, shrId, allEntries, mappedResources, referencesOut);`); }); }); @@ -1740,7 +1746,8 @@ function generateFromFHIRAssignment(field, fhirElement, fhirElementPath, shrElem if (shrType.isPrimitive && !isExtension) { rhs = fhirPathString; } else { - rhs = `FHIRHelper.createInstanceFromFHIR('${shrType.fqn}', ${fhirPathString}, 'Extension', shrId, allEntries, mappedResources, referencesOut, ${isExtension})`; + const fhirType = fhirElement.type[0].code; + rhs = `FHIRHelper.createInstanceFromFHIR('${shrType.fqn}', ${fhirPathString}, '${fhirType}', shrId, allEntries, mappedResources, referencesOut, ${isExtension})`; } if (isRef) { rhs = `FHIRHelper.createReference( ${rhs}, referencesOut)`; diff --git a/test/fixtures/spec/shr_slicing.txt b/test/fixtures/spec/shr_slicing.txt index f7bbc0b..f071bf3 100644 --- a/test/fixtures/spec/shr_slicing.txt +++ b/test/fixtures/spec/shr_slicing.txt @@ -36,6 +36,10 @@ Value: CodeableConcept EntryElement: Observation 0..1 ref(PatientEntry) +0..1 DataValue + +Element: DataValue +Value: CodeableConcept or Quantity or string or time or dateTime EntryElement: MemberA Based on: Observation diff --git a/test/fixtures/spec/shr_slicing_map_dstu2.txt b/test/fixtures/spec/shr_slicing_map_dstu2.txt index 2979323..c7b2979 100644 --- a/test/fixtures/spec/shr_slicing_map_dstu2.txt +++ b/test/fixtures/spec/shr_slicing_map_dstu2.txt @@ -4,6 +4,7 @@ Target: FHIR_DSTU_2 Observation maps to Observation: PatientEntry maps to subject + DataValue maps to value[x] PanelSliceByProfile: PanelMembers.Observation maps to related.target (slice at = related; slice on = target.reference.resolve(); slice strategy = includes; slice on type = profile) diff --git a/test/fixtures/spec/shr_slicing_map_stu3.txt b/test/fixtures/spec/shr_slicing_map_stu3.txt index 4aa3c22..35c25b1 100644 --- a/test/fixtures/spec/shr_slicing_map_stu3.txt +++ b/test/fixtures/spec/shr_slicing_map_stu3.txt @@ -4,6 +4,7 @@ Target: FHIR_STU_3 Observation maps to Observation: PatientEntry maps to subject + DataValue maps to value[x] PanelSliceByProfile: PanelMembers.Observation maps to related.target (slice at = related; slice on = target.reference.resolve(); slice strategy = includes; slice on type = profile) From cb179e764b6c8c98043814422a9bf522d5180c75 Mon Sep 17 00:00:00 2001 From: Dylan Date: Fri, 24 May 2019 14:11:41 -0400 Subject: [PATCH 3/6] cleanup and doco --- lib/generateClass.js | 1 + lib/generateFactory.js | 1 + lib/generateNamespaceFactory.js | 5 +++-- lib/includes/json-helper.js | 7 +++---- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/generateClass.js b/lib/generateClass.js index 421c513..0046c22 100644 --- a/lib/generateClass.js +++ b/lib/generateClass.js @@ -153,6 +153,7 @@ function generateClassBody(def, specs, fhir, fhirProfile, fhirExtension, cw) { cw.ln(`Deserializes FHIR JSON data to an instance of the ${clazzName} class.`) .ln(`The FHIR must be valid against the ${clazzName} FHIR profile, although this is not validated by the function.`) .ln(`@param {object} fhir - the FHIR JSON data to deserialize`) + .ln(`@param {string} fhirType - the type of the FHIR object that was passed in, in case not otherwise identifiable from the object itself`) .ln(`@param {string} shrId - a unique, persistent, permanent identifier for the overall health record belonging to the Patient; will be auto-generated if not provided`) .ln(`@param {Array} allEntries - the list of all entries that references in 'fhir' refer to`) .ln(`@param {object} mappedResources - any resources that have already been mapped to SHR objects. Format is { fhir_key: {shr_obj} }`) diff --git a/lib/generateFactory.js b/lib/generateFactory.js index 944cd86..c4b5a06 100644 --- a/lib/generateFactory.js +++ b/lib/generateFactory.js @@ -41,6 +41,7 @@ function generateFactory(namespaces) { cw.blComment(() => { cw.ln('Create an instance of a class from its FHIR representation.') .ln('@param {Object} fhir - The element data in FHIR format (use `{}` and provide `type` for a blank instance)') + .ln(`@param {string} fhirType - the type of the FHIR object that was passed in, in case not otherwise identifiable from the object itself`) .ln(`@param {string} shrType - The (optional) type of the element (e.g., 'http://standardhealthrecord.org/spec/shr/demographics/PersonOfRecord'). This is only used if the type cannot be extracted from the JSON.`) .ln('@returns {Object} An instance of the requested class populated with the provided data'); }) diff --git a/lib/generateNamespaceFactory.js b/lib/generateNamespaceFactory.js index 57887f1..8a6d38d 100644 --- a/lib/generateNamespaceFactory.js +++ b/lib/generateNamespaceFactory.js @@ -42,8 +42,9 @@ function generateNamespaceFactory(ns, defs) { cw.blComment(() => { cw.ln('Convert an instance of a class from its FHIR representation.') - .ln('@param {Object} fhir - The element data in JSON format (use `{}` and provide `type` for a blank instance)') - .ln(`@param {string} [type] - The (optional) type of the element (e.g., 'http://standardhealthrecord.org/spec/shr/demographics/PersonOfRecord'). This is only used if the type cannot be extracted from the JSON.`) + .ln('@param {Object} fhir - The element data in JSON format (use `{}` and provide `shrType` for a blank instance)') + .ln(`@param {string} [fhirType] - the type of the FHIR object that was passed in, in case not otherwise identifiable from the object itself`) + .ln(`@param {string} [shrType] - The (optional) type of the element (e.g., 'http://standardhealthrecord.org/spec/shr/demographics/PersonOfRecord'). This is only used if the type cannot be extracted from the JSON.`) .ln('@returns {Object} An instance of the requested class populated with the provided data'); }) .bl('static createInstanceFromFHIR(fhir, fhirType, shrType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => { diff --git a/lib/includes/json-helper.js b/lib/includes/json-helper.js index 8119ea0..2168b29 100644 --- a/lib/includes/json-helper.js +++ b/lib/includes/json-helper.js @@ -67,7 +67,6 @@ export function getNamespaceAndNameFromFHIR(fhir, type) { } // Ensure we have a type before proceeding if (!type) { - debugger; throw new Error(`Couldn't find type for FHIR: ${JSON.stringify(fhir)}`); } @@ -195,9 +194,9 @@ export const FHIRHelper = { /** * Creates an ES6 class instance based on a value extracted from the JSON. - * @param {string} key - the original key under which the value was stored. This is used as a backup in case the value - * does not declare its type. - * @param {object} value - the FHIR data to create an ES6 class instance for + * @param {string} shrType - the fqn of the class to be instantiated + * @param {object} fhir - the FHIR data to create an ES6 class instance for + * @param {string} fhirType - the type of the FHIR object passed in, just in case it's not otherwise available by inspecting the object * @returns {object} an instance of an ES6 class representing the data * @private */ From 3b528c61f336f2873d0883eeac054d474dee8f0a Mon Sep 17 00:00:00 2001 From: Dylan Date: Tue, 28 May 2019 09:10:51 -0400 Subject: [PATCH 4/6] update comment --- lib/generateNamespaceFactory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generateNamespaceFactory.js b/lib/generateNamespaceFactory.js index 8a6d38d..a259fde 100644 --- a/lib/generateNamespaceFactory.js +++ b/lib/generateNamespaceFactory.js @@ -43,7 +43,7 @@ function generateNamespaceFactory(ns, defs) { cw.blComment(() => { cw.ln('Convert an instance of a class from its FHIR representation.') .ln('@param {Object} fhir - The element data in JSON format (use `{}` and provide `shrType` for a blank instance)') - .ln(`@param {string} [fhirType] - the type of the FHIR object that was passed in, in case not otherwise identifiable from the object itself`) + .ln(`@param {string} fhirType - the type of the FHIR object that was passed in, in case not otherwise identifiable from the object itself`) .ln(`@param {string} [shrType] - The (optional) type of the element (e.g., 'http://standardhealthrecord.org/spec/shr/demographics/PersonOfRecord'). This is only used if the type cannot be extracted from the JSON.`) .ln('@returns {Object} An instance of the requested class populated with the provided data'); }) From 71b92baa4b89814f94b016b1746d8557d0b17f72 Mon Sep 17 00:00:00 2001 From: Dylan Date: Tue, 28 May 2019 09:11:43 -0400 Subject: [PATCH 5/6] add test case --- package.json | 1 + test/es6FromFHIRJSONTest.js | 32 +++++++++++++++++-- .../Observation_valueCodeableConcept.json | 12 +++++++ .../fhir/Observation_valueString.json | 6 ++++ yarn.lock | 5 +++ 5 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/fhir/Observation_valueCodeableConcept.json create mode 100644 test/fixtures/fhir/Observation_valueString.json diff --git a/package.json b/package.json index 09fef75..71679f4 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "lint:fix": "./node_modules/.bin/eslint . --fix" }, "dependencies": { + "chai-spies": "^1.0.0", "reserved-words": "^0.1.2" }, "devDependencies": { diff --git a/test/es6FromFHIRJSONTest.js b/test/es6FromFHIRJSONTest.js index 02295cd..69490ca 100644 --- a/test/es6FromFHIRJSONTest.js +++ b/test/es6FromFHIRJSONTest.js @@ -1,4 +1,7 @@ -const { expect } = require('chai'); +const chai = require('chai'); +const expect = chai.expect; +const spies = require('chai-spies'); +chai.use(spies); const setup = require('./setup'); require('babel-register')({ presets: [ 'es2015' ] @@ -409,13 +412,14 @@ describe('#FromFHIR_STU3', () => { describe('#Observation()', () => { - let Observation, Reference, ShrId, EntryId, EntryType; + let Observation, Reference, ShrId, EntryId, EntryType, DataValue; before(() => { Observation = context.importResult('shr/slicing/Observation'); Reference = context.importResult('Reference'); ShrId = context.importResult('shr/base/ShrId'); EntryId = context.importResult('shr/base/EntryId'); EntryType = context.importResult('shr/base/EntryType'); + DataValue = context.importResult('shr/slicing/DataValue'); }); it('should deserialize a FHIR JSON instance', () => { @@ -435,6 +439,30 @@ describe('#FromFHIR_STU3', () => { expect(entry).to.eql(expected); }); + + it('should correctly pass the FHIR type to nested object fromFHIR', () => { + + const spy = chai.spy.on(DataValue, 'fromFHIR'); + const json1 = context.getFHIR('Observation_valueString'); + try { + // TODO: fix the bug that causes this to throw + // this will error out because DataValue.fromFHIR doesn't know what to do with the given fhir (yet) + // but at this point all we care about is that it's passed the right parameter + Observation.fromFHIR(json1, 'Observation', '1-1'); + } catch (e) {} + + expect(spy).to.have.been.called.with('string'); + + const json2 = context.getFHIR('Observation_valueCodeableConcept'); + try { + // TODO: fix the bug that causes this to throw + // this will error out because DataValue.fromFHIR doesn't know what to do with the given fhir (yet) + // but at this point all we care about is that it's passed the right parameter + Observation.fromFHIR(json2, 'Observation', '1-1'); + } catch (e) {} + + expect(spy).to.have.been.called.with('CodeableConcept'); + }); }); }); diff --git a/test/fixtures/fhir/Observation_valueCodeableConcept.json b/test/fixtures/fhir/Observation_valueCodeableConcept.json new file mode 100644 index 0000000..5c3846a --- /dev/null +++ b/test/fixtures/fhir/Observation_valueCodeableConcept.json @@ -0,0 +1,12 @@ +{ + "resourceType": "Observation", + "id": "0000-0000", + "subject": { "reference": "abcd-1234" }, + "valueCodeableConcept": { + "coding": [{ + "system": "SNOMED-CT", + "code": "1234567890", + "display": "Dummy SNOMED Code" + }] + } +} \ No newline at end of file diff --git a/test/fixtures/fhir/Observation_valueString.json b/test/fixtures/fhir/Observation_valueString.json new file mode 100644 index 0000000..6f858e4 --- /dev/null +++ b/test/fixtures/fhir/Observation_valueString.json @@ -0,0 +1,6 @@ +{ + "resourceType": "Observation", + "id": "0000-0000", + "subject": { "reference": "abcd-1234" }, + "valueString": "something or other" +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index bf95d14..000c85e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -511,6 +511,11 @@ callsites@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" +chai-spies@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chai-spies/-/chai-spies-1.0.0.tgz#d16b39336fb316d03abf8c375feb23c0c8bb163d" + integrity sha512-elF2ZUczBsFoP07qCfMO/zeggs8pqCf3fZGyK5+2X4AndS8jycZYID91ztD9oQ7d/0tnS963dPkd0frQEThDsg== + chai@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" From 574c7fc044795073ec330c33d90aa89ceb24bb42 Mon Sep 17 00:00:00 2001 From: Dylan Date: Tue, 28 May 2019 10:43:30 -0400 Subject: [PATCH 6/6] rearrange parameters --- lib/generateFactory.js | 6 +++--- lib/generateNamespaceFactory.js | 4 ++-- lib/includes/json-helper.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/generateFactory.js b/lib/generateFactory.js index c4b5a06..4f202f2 100644 --- a/lib/generateFactory.js +++ b/lib/generateFactory.js @@ -40,17 +40,17 @@ function generateFactory(namespaces) { cw.blComment(() => { cw.ln('Create an instance of a class from its FHIR representation.') + .ln(`@param {string} shrType - The type of the element (e.g., 'shr.core.CodeableConcept'). This is only used if the type cannot be extracted from the JSON.`) .ln('@param {Object} fhir - The element data in FHIR format (use `{}` and provide `type` for a blank instance)') .ln(`@param {string} fhirType - the type of the FHIR object that was passed in, in case not otherwise identifiable from the object itself`) - .ln(`@param {string} shrType - The (optional) type of the element (e.g., 'http://standardhealthrecord.org/spec/shr/demographics/PersonOfRecord'). This is only used if the type cannot be extracted from the JSON.`) .ln('@returns {Object} An instance of the requested class populated with the provided data'); }) - .bl('static createInstanceFromFHIR(fhir, fhirType, shrType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => { + .bl('static createInstanceFromFHIR(shrType, fhir, fhirType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => { cw.ln('const { namespace } = getNamespaceAndNameFromFHIR(fhir, shrType);') .ln('switch (namespace) {'); for (const ns of namespaces) { const factory = factoryName(ns.namespace); - cw.ln(`case '${ns.namespace}': return ${factory}.createInstanceFromFHIR(fhir, fhirType, shrType, shrId, allEntries, mappedResources, referencesOut, asExtension);`); + cw.ln(`case '${ns.namespace}': return ${factory}.createInstanceFromFHIR(shrType, fhir, fhirType, shrId, allEntries, mappedResources, referencesOut, asExtension);`); } cw.ln(`case 'primitive': return fhir;`); cw.ln(`default: throw new Error(\`Unsupported namespace: \${namespace}\`);`) diff --git a/lib/generateNamespaceFactory.js b/lib/generateNamespaceFactory.js index a259fde..c67470a 100644 --- a/lib/generateNamespaceFactory.js +++ b/lib/generateNamespaceFactory.js @@ -42,12 +42,12 @@ function generateNamespaceFactory(ns, defs) { cw.blComment(() => { cw.ln('Convert an instance of a class from its FHIR representation.') + .ln(`@param {string} shrType - The type of the element (e.g., 'shr.core.CodeableConcept'). This is only used if the type cannot be extracted from the JSON.`) .ln('@param {Object} fhir - The element data in JSON format (use `{}` and provide `shrType` for a blank instance)') .ln(`@param {string} fhirType - the type of the FHIR object that was passed in, in case not otherwise identifiable from the object itself`) - .ln(`@param {string} [shrType] - The (optional) type of the element (e.g., 'http://standardhealthrecord.org/spec/shr/demographics/PersonOfRecord'). This is only used if the type cannot be extracted from the JSON.`) .ln('@returns {Object} An instance of the requested class populated with the provided data'); }) - .bl('static createInstanceFromFHIR(fhir, fhirType, shrType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => { + .bl('static createInstanceFromFHIR(shrType, fhir, fhirType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false)', () => { cw.ln('const { namespace, elementName } = getNamespaceAndNameFromFHIR(fhir, shrType);') .bl(`if (namespace !== '${ns.namespace}')`, () => { cw.ln(`throw new Error(\`Unsupported type in ${factory}: \${shrType}\`);`); diff --git a/lib/includes/json-helper.js b/lib/includes/json-helper.js index 2168b29..eee29d1 100644 --- a/lib/includes/json-helper.js +++ b/lib/includes/json-helper.js @@ -207,7 +207,7 @@ export const FHIRHelper = { if (OBJECT_FACTORY == null) { throw new Error(`SHR ES6 module is not initialized. Import 'init' before using the ES6 factories and classes`); } - return OBJECT_FACTORY.createInstanceFromFHIR(fhir, fhirType, shrType, shrId, allEntries, mappedResources, referencesOut, asExtension); + return OBJECT_FACTORY.createInstanceFromFHIR(shrType, fhir, fhirType, shrId, allEntries, mappedResources, referencesOut, asExtension); }, /**