diff --git a/apps/smart-forms-app/package.json b/apps/smart-forms-app/package.json index 175366cd1..2b3c13f0b 100644 --- a/apps/smart-forms-app/package.json +++ b/apps/smart-forms-app/package.json @@ -26,8 +26,8 @@ "homepage": "https://github.com/aehrc/smart-forms#readme", "dependencies": { "@aehrc/sdc-assemble": "^1.2.0", - "@aehrc/sdc-populate": "^2.2.0", - "@aehrc/smart-forms-renderer": "^0.35.0", + "@aehrc/sdc-populate": "^2.2.1", + "@aehrc/smart-forms-renderer": "^0.35.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/material-icons": "^5.0.16", diff --git a/package-lock.json b/package-lock.json index a200edbce..47dd1872c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,8 +50,8 @@ "license": "Apache-2.0", "dependencies": { "@aehrc/sdc-assemble": "^1.2.0", - "@aehrc/sdc-populate": "^2.2.0", - "@aehrc/smart-forms-renderer": "^0.35.0", + "@aehrc/sdc-populate": "^2.2.1", + "@aehrc/smart-forms-renderer": "^0.35.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/material-icons": "^5.0.16", @@ -44043,7 +44043,7 @@ }, "packages/sdc-populate": { "name": "@aehrc/sdc-populate", - "version": "2.2.0", + "version": "2.2.1", "license": "Apache-2.0", "dependencies": { "dayjs": "^1.11.10", @@ -44072,10 +44072,10 @@ }, "packages/smart-forms-renderer": { "name": "@aehrc/smart-forms-renderer", - "version": "0.35.0", + "version": "0.35.1", "license": "Apache-2.0", "dependencies": { - "@aehrc/sdc-populate": "^2.2.0", + "@aehrc/sdc-populate": "^2.2.1", "@iconify/react": "^4.1.1", "dayjs": "^1.11.10", "deep-diff": "^1.0.2", diff --git a/packages/sdc-populate/package.json b/packages/sdc-populate/package.json index 0575ce50c..956944999 100644 --- a/packages/sdc-populate/package.json +++ b/packages/sdc-populate/package.json @@ -1,6 +1,6 @@ { "name": "@aehrc/sdc-populate", - "version": "2.2.0", + "version": "2.2.1", "description": "Performs the $populate operation from the HL7 FHIR SDC (Structured Data Capture) specification: http://hl7.org/fhir/uv/sdc", "main": "lib/index.js", "scripts": { diff --git a/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/interfaces/expressions.interface.ts b/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/interfaces/expressions.interface.ts index 30673b359..0d43d5490 100644 --- a/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/interfaces/expressions.interface.ts +++ b/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/interfaces/expressions.interface.ts @@ -36,7 +36,7 @@ export interface PopulationExpressions { } export interface ValueSetPromise { - promise: Promise; + promise: Promise; valueSet?: ValueSet; } diff --git a/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/createFhirPathContext.ts b/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/createFhirPathContext.ts index 3ef2b21d6..480b1e439 100644 --- a/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/createFhirPathContext.ts +++ b/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/createFhirPathContext.ts @@ -89,10 +89,19 @@ async function populateReferenceContextsIntoContextMap( try { // Resolve promises for referenceContextsTuple const promises: Promise[] = referenceContextTuples.map(([, promise]) => promise); - const responses = await Promise.all(promises); - const resources: (FhirResource | null)[] = responses.map((response) => - responseDataIsFhirResource(response?.data) ? (response.data as FhirResource) : null - ); + const settledPromises = await Promise.allSettled(promises); + const resources: (FhirResource | null)[] = settledPromises.map((settledPromise) => { + if (settledPromise.status === 'rejected') { + return null; + } + + const response = settledPromise.value; + if (responseDataIsFhirResource(response?.data)) { + return response.data as FhirResource; + } + + return null; + }); // Update referenceContextTuples with resolved resources referenceContextTuples = referenceContextTuples.map((tuple, i) => { @@ -179,10 +188,19 @@ async function populateBatchContextsIntoContextMap( // Resolve promises for batchContextEntryTuples const promises: Promise[] = batchContextEntryTuples.map(([, promise]) => promise); - const responses = await Promise.all(promises); - const resources: (FhirResource | null)[] = responses.map((response) => - responseDataIsFhirResource(response?.data) ? (response.data as FhirResource) : null - ); + const settledPromises = await Promise.allSettled(promises); + const resources: (FhirResource | null)[] = settledPromises.map((settledPromise) => { + if (settledPromise.status === 'rejected') { + return null; + } + + const response = settledPromise.value; + if (responseDataIsFhirResource(response?.data)) { + return response.data as FhirResource; + } + + return null; + }); // Add resources to batch bundle for (let i = 0; i < resources.length; i++) { diff --git a/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/processValueSets.ts b/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/processValueSets.ts index 25680bdf4..c68e1eaf0 100644 --- a/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/processValueSets.ts +++ b/packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/processValueSets.ts @@ -32,19 +32,38 @@ export async function resolveValueSetPromises( const valueSetPromiseKeys = Object.keys(valueSetPromises); const valueSetPromiseValues = Object.values(valueSetPromises); const promises = valueSetPromiseValues.map((valueSetPromise) => valueSetPromise.promise); - const valueSets = await Promise.all(promises); + const settledPromises = await Promise.allSettled(promises); + + for (const [i, settledPromise] of settledPromises.entries()) { + if (settledPromise.status === 'rejected') { + continue; + } - for (const [i, valueSet] of valueSets.entries()) { const key = valueSetPromiseKeys[i]; const valueSetPromise = valueSetPromiseValues[i]; if (key && valueSetPromise) { - valueSetPromise.valueSet = valueSet; + // Promises can be generated by a fhirClient (internally) or axios (via callback function), so we need to handle both scenarios + const response = settledPromise.value; + // Get valueSet from response (fhirClient scenario) + if (responseIsValueSet(response)) { + valueSetPromise.valueSet = response; + } + + // Fallback to get valueSet from response.data (axios scenario) + if (!valueSetPromise.valueSet && response.data && responseIsValueSet(response.data)) { + valueSetPromise.valueSet = response.data; + } + newValueSetPromises[key] = valueSetPromise; } } return newValueSetPromises; } +function responseIsValueSet(response: any): response is ValueSet { + return response && response.resourceType === 'ValueSet'; +} + /** * Read a questionnaire response item recursively and retrieve valueSet answers if present * diff --git a/packages/smart-forms-renderer/package.json b/packages/smart-forms-renderer/package.json index 13b995d89..600a4a744 100644 --- a/packages/smart-forms-renderer/package.json +++ b/packages/smart-forms-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@aehrc/smart-forms-renderer", - "version": "0.35.0", + "version": "0.35.1", "description": "FHIR Structured Data Captured (SDC) rendering engine for Smart Forms", "main": "lib/index.js", "scripts": { @@ -27,7 +27,7 @@ }, "homepage": "https://github.com/aehrc/smart-forms#readme", "dependencies": { - "@aehrc/sdc-populate": "^2.2.0", + "@aehrc/sdc-populate": "^2.2.1", "@iconify/react": "^4.1.1", "dayjs": "^1.11.10", "deep-diff": "^1.0.2", diff --git a/packages/smart-forms-renderer/src/utils/questionnaireStoreUtils/addDisplayToCodings.ts b/packages/smart-forms-renderer/src/utils/questionnaireStoreUtils/addDisplayToCodings.ts index 1dec71c6c..aabfb7bec 100644 --- a/packages/smart-forms-renderer/src/utils/questionnaireStoreUtils/addDisplayToCodings.ts +++ b/packages/smart-forms-renderer/src/utils/questionnaireStoreUtils/addDisplayToCodings.ts @@ -101,9 +101,14 @@ export async function resolveLookupPromises( const lookupPromiseValues = Object.values(codeSystemLookupPromises); const promises = lookupPromiseValues.map((lookupPromise) => lookupPromise.promise); - const lookupResults = await Promise.all(promises); + const settledPromises = await Promise.allSettled(promises); - for (const [i, lookupResult] of lookupResults.entries()) { + for (const [i, settledPromise] of settledPromises.entries()) { + if (settledPromise.status === 'rejected') { + continue; + } + + const lookupResult = settledPromise.value; if (!lookupResponseIsValid(lookupResult)) { continue; }