diff --git a/.github/workflows/build_test_lint.yml b/.github/workflows/build_test_lint.yml
index ac6840ea..953b4685 100644
--- a/.github/workflows/build_test_lint.yml
+++ b/.github/workflows/build_test_lint.yml
@@ -79,3 +79,34 @@ jobs:
- name: Check formatting
run: npm run check-formatting
+
+ deploy-storybook:
+ name: Deploy Storybook to S3
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pages: write
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js 18.x
+ uses: actions/setup-node@v4
+ with:
+ node-version: 18
+ cache: npm
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ role-to-assume: arn:aws:iam::209248795938:role/SmartFormsReactAppDeployment
+ aws-region: ap-southeast-2
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build application
+ run: npm run build-storybook -w packages/smart-forms-renderer
+
+ - name: Upload static Storybook site to S3
+ run: aws s3 sync packages/smart-forms-renderer/storybook-static s3://smart-forms-storybook/storybook
diff --git a/.github/workflows/deploy_app.yml b/.github/workflows/deploy_app.yml
index 3a1600fc..e48f9093 100644
--- a/.github/workflows/deploy_app.yml
+++ b/.github/workflows/deploy_app.yml
@@ -4,13 +4,15 @@ on:
push:
branches: ['main']
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
jobs:
build:
name: Deploy Smart Forms app to S3
runs-on: ubuntu-latest
- permissions:
- id-token: write
- contents: read
steps:
- uses: actions/checkout@v4
diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml
index dafee6e6..96f0c6d3 100644
--- a/.github/workflows/deploy_docs.yml
+++ b/.github/workflows/deploy_docs.yml
@@ -34,31 +34,4 @@ jobs:
run: npm run build -w documentation
- name: Upload static Docusaurus site to S3
- run: aws s3 sync documentation/build s3://smart-forms-docs/docs
-
- deploy-storybook:
- name: Deploy Storybook to S3
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Use Node.js 18.x
- uses: actions/setup-node@v4
- with:
- node-version: 18
- cache: npm
-
- - name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@v4
- with:
- role-to-assume: arn:aws:iam::209248795938:role/SmartFormsReactAppDeployment
- aws-region: ap-southeast-2
-
- - name: Install dependencies
- run: npm ci
-
- - name: Build application
- run: npm run build-storybook -w packages/smart-forms-renderer
-
- - name: Upload static Storybook site to S3
- run: aws s3 sync packages/smart-forms-renderer/storybook-static s3://smart-forms-storybook/storybook
+ run: aws s3 sync documentation/build s3://smart-forms-docs/docs --cache-control no-cache
diff --git a/README.md b/README.md
index d37e6328..e876677e 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ Powered by SMART on FHIR and Structured Data Capture, Smart Forms allow you to e
+Update 11/07/2024: The documentation link isn't working. It is currently being fixed.
---
Smart Forms is a Typescript-based [React](https://reactjs.org/) forms web application currently ongoing development by [CSIRO's Australian e-Health Research Centre](https://aehrc.csiro.au/) as part of the Primary Care Data Quality project funded by the Australian Government Department of Health.
diff --git a/deployment/cloudfront/SmartFormsRedirectToCorrectRoute.js b/deployment/cloudfront/SmartFormsRedirectToCorrectRoute.js
index c6c5cdb7..93db4651 100644
--- a/deployment/cloudfront/SmartFormsRedirectToCorrectRoute.js
+++ b/deployment/cloudfront/SmartFormsRedirectToCorrectRoute.js
@@ -82,21 +82,28 @@ function handler(event) {
}
- // Handle Docz routes
- if (uri.includes('/docz')) {
- // Reroute to smartforms.csiro.au/docz/index.html
- if (uri === '/docz/') {
+ // Handle Docs routes
+ if (uri.includes('/docs')) {
+ // Reroute to smartforms.csiro.au/docs/index.html
+ if (uri === '/docs/') {
request.uri += 'index.html';
return request;
}
- if (uri === '/docz') {
+ if (uri === '/docs') {
request.uri = '/redirect.html';
return request;
}
if (!uri.includes('.')) {
- request.uri += '/index.html';
+ // For https://smartforms.csiro.au/docs/sdc/population/ cases
+ if (uri.endsWith('/')) {
+ request.uri += 'index.html';
+ }
+ // For https://smartforms.csiro.au/docs/sdc/population cases
+ else {
+ request.uri += '/index.html';
+ }
return request;
}
diff --git a/deployment/ehr-proxy/ehr-proxy-app/lib/ehr-proxy-app-stack.ts b/deployment/ehr-proxy/ehr-proxy-app/lib/ehr-proxy-app-stack.ts
index 655d1fa3..f6d6553b 100644
--- a/deployment/ehr-proxy/ehr-proxy-app/lib/ehr-proxy-app-stack.ts
+++ b/deployment/ehr-proxy/ehr-proxy-app/lib/ehr-proxy-app-stack.ts
@@ -73,7 +73,12 @@ export class EhrProxyAppStack extends cdk.Stack {
listener.addAction('EhrProxyExtractAction', {
action: ListenerAction.forward([extractTargetGroup]),
priority: 1,
- conditions: [ListenerCondition.pathPatterns(['/fhir/QuestionnaireResponse/$extract'])]
+ conditions: [
+ ListenerCondition.pathPatterns([
+ '/fhir/QuestionnaireResponse/$extract',
+ '/fhir/StructureMap/$convert'
+ ])
+ ]
});
// Create a target for the transform service
diff --git a/documentation/docs/index.md b/documentation/docs/index.md
index 9dd5222b..b05c1197 100644
--- a/documentation/docs/index.md
+++ b/documentation/docs/index.md
@@ -14,6 +14,7 @@ This documentation is intended to provide a guide on how to use Smart Forms. It
- [Components](/docs/components): A showcase of supported Questionnaire form components.
- [SDC](/docs/sdc): A section around the conformance and usage of functionalities defined in the SDC specification.
- [Developer Usage](/docs/dev): A guide on how to use the form renderer in your own application.
+- [FHIR Operations](/docs/operations): A guide on using the $populate, $assemble and $extract operations.
### Referenced FHIR Specifications
diff --git a/documentation/docs/operations/assemble.mdx b/documentation/docs/operations/assemble.mdx
new file mode 100644
index 00000000..b6129b29
--- /dev/null
+++ b/documentation/docs/operations/assemble.mdx
@@ -0,0 +1,33 @@
+---
+sidebar_position: 3
+---
+
+# $assemble
+
+## Useful links
+
+**Deployed service: https://smartforms.csiro.au/api/fhir/Questionnaire/$assemble**
+
+FHIR Operation definition: http://hl7.org/fhir/uv/sdc/OperationDefinition/Questionnaire-assemble
+
+Github: https://github.com/aehrc/smart-forms/tree/main/services/assemble-express
+
+Dockerhub: https://hub.docker.com/r/aehrc/smart-forms-assemble
+
+## Usage
+
+A Questionnaire resource can be assembled using a **POST** request to a URL such as:
+
+```http request
+https://smartforms.csiro.au/api/fhir/Questionnaire/$assemble (type-level)
+```
+
+#### Parameters
+
+| Name | Cardinality | Type | Documentation |
+| ------------- | ----------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
+| questionnaire | 1..1 | Questionnaire | The [Modular Questionnaire](https://hl7.org/fhir/uv/sdc/StructureDefinition-sdc-questionnaire-modular.html) to assemble the content of. |
+
+#### Try it out
+
+[](https://elements.getpostman.com/redirect?entityId=22885901-2af2cfbb-3a0a-49c6-8404-105ef0751415&entityType=collection)
diff --git a/documentation/docs/operations/extract.mdx b/documentation/docs/operations/extract.mdx
new file mode 100644
index 00000000..4727bb69
--- /dev/null
+++ b/documentation/docs/operations/extract.mdx
@@ -0,0 +1,196 @@
+---
+sidebar_position: 4
+---
+
+# StructureMap $extract
+
+This $extract **proof-of-concept** reference implementation is an abstraction on top of an existing StructureMap $transform operation.
+We leveraged Brian Postlethwaite's [.NET FHIR Mapping Language engine](https://github.com/brianpos/fhir-net-mappinglanguage/tree/main/demo-map-server) to expose a StructureMap $transform operation on https://proxy.smartforms.io/fhir/StructureMap/$transform.
+
+:::note
+
+This reference implementation is a proof-of-concept. It is highly likely that the underlying implementation will change in the future.
+
+:::
+
+## Useful links
+
+#### Services links
+
+Deployed service: https://proxy.smartforms.io/fhir/QuestionnaireResponse/$extract
+
+Underlying $transform service: https://proxy.smartforms.io/fhir/StructureMap/$transform
+
+FHIR Mapping Language to StructureMap $convert service: https://proxy.smartforms.io/fhir/StructureMap/$convert
+
+#### Specification links
+
+FHIR $extract operation definition: http://hl7.org/fhir/uv/sdc/OperationDefinition/QuestionnaireResponse-extract
+
+FHIR $transform operation definition: https://hl7.org/fhir/r4/structuremap-operation-transform.html
+
+FHIR Mapping Language $convert workflow: https://confluence.hl7.org/pages/viewpage.action?pageId=76158820#UsingtheFHIRMappingLanguage-WebServices
+
+FHIR StructureMap-based extraction: https://hl7.org/fhir/uv/sdc/extraction.html#structuremap-based-extraction
+
+#### Source code links
+
+Github: https://github.com/aehrc/smart-forms/tree/main/services/extract-express
+
+Dockerhub: https://hub.docker.com/r/aehrc/smart-forms-extract
+
+## Usage
+
+Resource(s) can be extracted from a QuestionnaireResponse using a **POST** request to a URL such as:
+
+```http request
+https://proxy.smartforms.io/fhir/QuestionnaireResponse/$extract (type-level)
+```
+
+#### Parameters
+
+| Name | Cardinality | Type | Documentation |
+| ---------------------- | ----------- | -------- | ------------------------------------------------------------------------------------------------------- |
+| questionnaire-response | 1..1 | Resource | The QuestionnaireResponse to extract data from. Used when the operation is invoked at the 'type' level. |
+
+#### Try it out
+
+[](https://elements.getpostman.com/redirect?entityId=22885901-2af2cfbb-3a0a-49c6-8404-105ef0751415&entityType=collection)
+
+## How it works
+
+The $extract operation is an abstraction on top of an existing StructureMap $transform operation.
+
+#### The underlying $transform
+
+A [$transform](https://hl7.org/fhir/r4/structuremap-operation-transform.html) operation requires two input parameters:
+
+1. `source` - Contains the structure map defining the mapping rules
+2. `content` - Contains the data to be transformed (in terms of SDC, this is the QuestionnaireResponse)
+
+The output of $transform is the transformed data, a FHIR resource.
+
+Taking this logic, we can use a StructureMap `$transform` operation to perform the extraction of resources from a QuestionnaireResponse. Let's say we want to extract a bundle containing an Observation resource from a QuestionnaireResponse.
+We need a StructureMap that maps the data from the QuestionnaireResponse to the Observation resource. Normally we would write this mapping in [FHIR Mapping Language](https://www.hl7.org/fhir/mapping-language.html) and convert it to a StructureMap.
+
+For more information on using the FHIR Mapping Language, refer to https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Mapping+Language. Brian has a really awesome tool that can help you write and test your mappings at https://fhirpath-lab.com/FhirMapper2.
+
+Once we have both the `source` StructureMap and the `content` QuestionnaireResponse, our $transform request body should look roughly like this:
+
+```json
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "source",
+ "resource": // -
+ }
+ },
+ {
+ "name": "content",
+ "resource": // - a filled QR from
+ }
+ ]
+}
+```
+
+Relevant resources:
+
+`source`- https://smartforms.csiro.au/api/fhir/StructureMap/extract-bmi
+
+`content`- http://smartforms.csiro.au/fhir/Questionnaire/CalculatedExpressionBMICalculatorPrepop
+
+[//]: # 'turn it into a microservice'
+
+Running the $transform operation will return the transformed data, which in this case is a Bundle resource containing the Observation resource:
+
+```json
+{
+ "resourceType": "Bundle",
+ "id": "",
+ "type": "transaction",
+ "entry": [
+ {
+ "request": {
+ "method": "POST",
+ "url": "Observation"
+ },
+ "resource": {
+ "resourceType": "Observation",
+ "status": "final",
+ "code": {
+ "coding": [
+ {
+ "system": "http://snomed.info/sct",
+ "code": "60621009",
+ "display": "Body mass index"
+ }
+ ]
+ },
+ "subject": {
+ "reference": "Patient/pat-sf"
+ },
+ "valueQuantity": {
+ "value": 29.55,
+ "unit": "kg/m2",
+ "system": "http://unitsofmeasure.org",
+ "code": "kg/m2"
+ }
+ }
+ }
+ ]
+}
+```
+
+#### The $extract operation
+
+Using the logic above, we can abstract the $transform operation into a $extract operation. The $extract operation requires only one input parameter (or you can just provide the QuestionnaireResponse in the request body):
+
+`questionnaire-response` - Contains the QuestionnaireResponse to extract data from
+
+The provided QuestionnaireResponse should fulfill two criteria:
+
+1. It needs to contain a canonical reference in its `questionnaire` property.
+
+```json
+{
+ ...
+ "questionnaire": "https://smartforms.csiro.au/docs/sdc/population/calculated-expression-1|0.1.0",
+ ...
+}
+```
+
+2. The referenced Questionnaire should have a [questionnaire-targetStructureMap](http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-targetStructureMap) extension. This extension needs to contain a canonical reference to a StructureMap that maps the data from the QuestionnaireResponse to the desired resource(s).
+
+```json
+{
+ ...
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-targetStructureMap",
+ "valueCanonical": "https://smartforms.csiro.au/docs/StructureMap/extract-bmi"
+ }
+ ],
+ ...
+}
+```
+
+The $extract POC implementation defines a definitional repository to resolve Questionnaires and StructureMaps - https://smartforms.csiro.au/api/fhir. See https://hub.docker.com/r/aehrc/smart-forms-extract for more information.
+
+The $extract operation will resolve the referenced Questionnaire + StructureMap, and set the StructureMap as the `source` in the $transform operation. The `content` will be the provided QuestionnaireResponse.
+
+The underlying $transform operation will be executed, and the transformed data will be returned as the output of the $extract operation.
+
+## The $convert operation
+
+Brian's [.NET FHIR Mapping Language engine](https://github.com/brianpos/fhir-net-mappinglanguage/tree/main/demo-map-server) has a handy debug mode that can be activated by adding `debug=true` to the $transform query parameters.
+The debug payload contains useful details such as warnings/errors, trace output, the converted StructureMap, and the output resource.
+
+Conveniently, it also accepts both a FHIR Mapping Language map and a StructureMap resource as the `source` input.
+This means we can use it as a $convert operation to convert a FHIR Mapping Language map to a StructureMap resource.
+
+```
+https://proxy.smartforms.io/fhir/StructureMap/$convert
+```
+
+It expects the request headers for the `Content-Type` to be `text/plain`. The request body should contain the FHIR Mapping Language map, and the response will contain the converted StructureMap resource.
diff --git a/documentation/docs/operations/index.mdx b/documentation/docs/operations/index.mdx
index 7c36fa14..a0a6dfd1 100644
--- a/documentation/docs/operations/index.mdx
+++ b/documentation/docs/operations/index.mdx
@@ -8,18 +8,19 @@ sidebar_label: Introduction
Smart Forms provides reference implementations for `$populate` and `$assemble` operations as [ExpressJS](https://expressjs.com/) services to complement the FHIR Questionnaire renderer.
These operations are available as Docker images and can be deployed as microservices.
-[//]: # '1. **`$populate`**'
-[//]: # ' Operation definition: [SDCPopulateQuestionnaire](http://hl7.org/fhir/uv/sdc/OperationDefinition/Questionnaire-populate)'
-[//]: # ' Github repository:'
-[//]: #
-[//]: # ' A React-based library that contains the rendering engine. It acts as a reference implementation for the [SDC Form Filler](https://hl7.org/fhir/uv/sdc/CapabilityStatement-sdc-form-filler.html).'
-[//]: #
-[//]: # '2. **`$assemble`** ([@aehrc/sdc-populate](https://www.npmjs.com/package/@aehrc/sdc-populate))'
-[//]: #
-[//]: # ' A reference implementation of the [SDC Populate Questionnaire](https://hl7.org/fhir/uv/sdc/OperationDefinition-Questionnaire-populate.html) operation, also known as $populate.'
-[//]: # ' Currently, there are no written documentation available for this library. Please refer to the [API](/docs/api/sdc-populate) for more information.'
-[//]: #
-[//]: # '3. **SDC Assemble** ([@aehrc/sdc-assemble](https://www.npmjs.com/package/@aehrc/sdc-assemble))'
-[//]: #
-[//]: # ' A reference implementation of the [SDC Assemble Questionnaire](https://hl7.org/fhir/uv/sdc/OperationDefinition-Questionnaire-assemble.html) operation, also known as $assemble.'
-[//]: # ' Currently, there are no written documentation available for this library. Please refer to the [API](/docs/api/sdc-assemble) for more information.'
+1. **$populate** (https://smartforms.csiro.au/api/fhir/Questionnaire/$populate)
+
+ A reference implementation of the [SDC Populate Questionnaire](https://hl7.org/fhir/uv/sdc/OperationDefinition-Questionnaire-populate.html) operation, also known as $populate.
+ It builds on the [@aehrc/sdc-populate](https://www.npmjs.com/package/@aehrc/sdc-populate) library.
+
+2. **$assemble** (https://smartforms.csiro.au/api/fhir/Questionnaire/$assemble)
+
+ A reference implementation of the [SDC Assemble](https://hl7.org/fhir/uv/sdc/OperationDefinition-Questionnaire-assemble.html) operation, also known as $assemble.
+ It builds on the [@aehrc/sdc-assemble](https://www.npmjs.com/package/@aehrc/sdc-assemble) library.
+
+3. **$extract** (https://proxy.smartforms.io/fhir/QuestionnaireResponse/$extract)
+
+ A **proof-of-concept** reference implementation of the [SDC QuestionnaireResponse Extract](https://hl7.org/fhir/uv/sdc/OperationDefinition-QuestionnaireResponse-extract.html) operation, also known as $extract.
+
+ This $extract POC reference implementation is an abstraction on top of an existing StructureMap $transform operation.
+ We leveraged Brian Postlethwaite's [.NET FHIR Mapping Language engine](https://github.com/brianpos/fhir-net-mappinglanguage/tree/main/demo-map-server) to expose a StructureMap $transform operation on https://proxy.smartforms.io/fhir/StructureMap/$transform.
diff --git a/documentation/docs/operations/populate.mdx b/documentation/docs/operations/populate.mdx
new file mode 100644
index 00000000..7f4b9fca
--- /dev/null
+++ b/documentation/docs/operations/populate.mdx
@@ -0,0 +1,47 @@
+---
+sidebar_position: 2
+---
+
+# $populate
+
+## Useful links
+
+**Deployed service: https://smartforms.csiro.au/api/fhir/Questionnaire/$populate**
+
+FHIR Operation definition: http://hl7.org/fhir/uv/sdc/OperationDefinition/Questionnaire-populate
+
+Github: https://github.com/aehrc/smart-forms/tree/main/services/populate-express
+
+Dockerhub: https://hub.docker.com/r/aehrc/smart-forms-populate
+
+## Usage
+
+A Questionnaire resource can be populated using a **POST** request to a URL such as:
+
+```http request
+https://smartforms.csiro.au/api/fhir/Questionnaire/$populate (type-level)
+```
+
+#### Parameters
+
+| Name | Cardinality | Type | Documentation |
+| --------------- | ----------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| questionnaire | 1..1 | Questionnaire | The Questionnaire is provided directly as part of the request. |
+| subject | 1..1 | Reference | The resource that is to be the QuestionnaireResponse.subject. The QuestionnaireResponse instance will reference the provided subject. |
+| context | 0..\* | | Resources containing information to be used to help populate the QuestionnaireResponse. These will typically be FHIR resources. |
+| context.name | 0..\* | string | The name of the launchContext or root Questionnaire variable the passed content should be used as for population purposes. The name SHALL correspond to a launchContext or variable declared at the root of the Questionnaire. |
+| context.content | 0..\* | Resource | The actual resource (or resources) to use as the value of the launchContext or variable. |
+
+https://smartforms.csiro.au/api/fhir only stores Questionnaire definitions and does not contain any clinical data. Therefore when using this sample implementation, contextual information for pre-population should be provided as actual FHIR resources, not references.
+
+#### Debugging
+
+You can add the `debug=true` query parameter to return an additional `contextResult-custom` output parameter in the response.
+
+```http request
+https://smartforms.csiro.au/api/fhir/Questionnaire/$populate?debug=true
+```
+
+#### Try it out
+
+[](https://elements.getpostman.com/redirect?entityId=22885901-2af2cfbb-3a0a-49c6-8404-105ef0751415&entityType=collection)
diff --git a/documentation/docusaurus.config.ts b/documentation/docusaurus.config.ts
index a7f1b34f..bc66a853 100644
--- a/documentation/docusaurus.config.ts
+++ b/documentation/docusaurus.config.ts
@@ -12,7 +12,7 @@ const config: Config = {
url: 'https://smartforms.csiro.au',
// Set the // pathname under which your site is served
// For GitHub pages deployment, it is often '//'
- baseUrl: '/docs',
+ baseUrl: '/docs/',
// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
@@ -20,7 +20,9 @@ const config: Config = {
projectName: '', // Usually your repo name.\
onBrokenLinks: 'warn',
- onBrokenMarkdownLinks: 'warn',
+ onBrokenMarkdownLinks: 'throw',
+
+ trailingSlash: false,
// Even if you don't use internationalization, you can use this field to set
// useful metadata like html lang. For example, if your site is Chinese, you
@@ -35,11 +37,12 @@ const config: Config = {
'classic',
{
docs: {
+ showLastUpdateTime: true,
routeBasePath: '/',
- sidebarPath: './sidebars.ts',
+ sidebarPath: './sidebars.ts'
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
- editUrl: 'https://github.com/aehrc/smart-forms/'
+ // editUrl: 'https://github.com/aehrc/smart-forms/'
},
theme: {
customCss: './src/css/custom.css'
@@ -53,7 +56,6 @@ const config: Config = {
// Replace with your project's social card
image: 'img/logo-sf.svg',
navbar: {
- title: 'Smart Forms',
logo: {
alt: 'Smart Forms',
src: 'img/logo-sf.svg',
@@ -153,6 +155,28 @@ const config: Config = {
copyright: `Copyright © ${new Date().getFullYear()} Commonwealth Scientific and Industrial Research
- Organisation (CSIRO).`
},
+ // Refer to https://docusaurus.io/docs/search#connecting-algolia
+ algolia: {
+ // The application ID provided by Algolia
+ appId: 'SL7YXI16RH',
+
+ // Public API key: it is safe to commit it
+ apiKey: 'a4c401a7bac65bc81b7dd7efe958b951',
+
+ indexName: 'smartforms-csiro',
+
+ // Optional: see doc section below
+ contextualSearch: true,
+
+ // Optional: Algolia search parameters
+ searchParameters: {},
+
+ // Optional: path for search page that enabled by default (`false` to disable it)
+ searchPagePath: 'search',
+
+ // Optional: whether the insights feature is enabled or not on Docsearch (`false` by default)
+ insights: false
+ },
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula
diff --git a/package-lock.json b/package-lock.json
index 8a21e486..bd9c8c7f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42626,7 +42626,7 @@
}
},
"services/extract-express": {
- "version": "0.3.0",
+ "version": "0.4.0",
"license": "Apache-2.0",
"dependencies": {
"cors": "^2.8.5",
@@ -42642,7 +42642,7 @@
}
},
"services/populate-express": {
- "version": "2.2.6",
+ "version": "2.2.7",
"license": "Apache-2.0",
"dependencies": {
"@aehrc/sdc-populate": "^2.2.7",
diff --git a/packages/smart-forms-renderer/src/utils/calculatedExpression.ts b/packages/smart-forms-renderer/src/utils/calculatedExpression.ts
index bc2a5ceb..43a5aaf5 100644
--- a/packages/smart-forms-renderer/src/utils/calculatedExpression.ts
+++ b/packages/smart-forms-renderer/src/utils/calculatedExpression.ts
@@ -34,7 +34,7 @@ import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
import { updateQrItemsInGroup } from './qrItem';
import cloneDeep from 'lodash.clonedeep';
import dayjs from 'dayjs';
-import { qrItemHasItemsOrAnswer } from './manageForm';
+import { updateQuestionnaireResponse } from './updateQr';
interface EvaluateInitialCalculatedExpressionsParams {
initialResponse: QuestionnaireResponse;
@@ -165,15 +165,6 @@ export function initialiseCalculatedExpressionValues(
populatedResponse: QuestionnaireResponse,
calculatedExpressions: Record
): QuestionnaireResponse {
- if (
- !questionnaire.item ||
- questionnaire.item.length === 0 ||
- !populatedResponse.item ||
- populatedResponse.item.length === 0
- ) {
- return populatedResponse;
- }
-
// Filter calculated expressions, only preserve key-value pairs with values
const calculatedExpressionsWithValues: Record = {};
for (const linkId in calculatedExpressions) {
@@ -186,41 +177,12 @@ export function initialiseCalculatedExpressionValues(
}
}
- // Populate calculated expression values into QR
- const qItemsIndexMap = mapQItemsIndex(questionnaire);
- const topLevelQRItemsByIndex = getQrItemsIndex(
- questionnaire.item,
- populatedResponse.item,
- qItemsIndexMap
+ return updateQuestionnaireResponse(
+ questionnaire,
+ populatedResponse,
+ initialiseItemCalculatedExpressionValueRecursive,
+ calculatedExpressionsWithValues
);
-
- const topLevelQrItems: QuestionnaireResponseItem[] = [];
- for (const [index, topLevelQItem] of questionnaire.item.entries()) {
- const topLevelQRItemOrItems = topLevelQRItemsByIndex[index] ?? {
- linkId: topLevelQItem.linkId,
- text: topLevelQItem.text,
- item: []
- };
-
- const updatedTopLevelQRItem = initialiseItemCalculatedExpressionValueRecursive(
- topLevelQItem,
- topLevelQRItemOrItems,
- calculatedExpressionsWithValues
- );
-
- if (Array.isArray(updatedTopLevelQRItem)) {
- if (updatedTopLevelQRItem.length > 0) {
- topLevelQrItems.push(...updatedTopLevelQRItem);
- }
- continue;
- }
-
- if (updatedTopLevelQRItem && qrItemHasItemsOrAnswer(updatedTopLevelQRItem)) {
- topLevelQrItems.push(updatedTopLevelQRItem);
- }
- }
-
- return { ...populatedResponse, item: topLevelQrItems };
}
function initialiseItemCalculatedExpressionValueRecursive(
diff --git a/packages/smart-forms-renderer/src/utils/repopulateIntoResponse.ts b/packages/smart-forms-renderer/src/utils/repopulateIntoResponse.ts
index 936d7105..57210c38 100644
--- a/packages/smart-forms-renderer/src/utils/repopulateIntoResponse.ts
+++ b/packages/smart-forms-renderer/src/utils/repopulateIntoResponse.ts
@@ -1,14 +1,9 @@
-import type {
- Questionnaire,
- QuestionnaireItem,
- QuestionnaireResponse,
- QuestionnaireResponseItem
-} from 'fhir/r4';
+import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
import type { ItemToRepopulate } from './repopulateItems';
import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
import { isSpecificItemControl } from './itemControl';
import { questionnaireResponseStore, questionnaireStore } from '../stores';
-import { qrItemHasItemsOrAnswer } from './manageForm';
+import { updateQuestionnaireResponse } from './updateQr';
/**
* Re-populate checked items in the re-population dialog into the current QuestionnaireResponse
@@ -19,63 +14,14 @@ export function repopulateResponse(checkedItemsToRepopulate: Record
-): QuestionnaireResponse {
- if (
- !questionnaire.item ||
- questionnaire.item.length === 0 ||
- !updatableResponse.item ||
- updatableResponse.item.length === 0
- ) {
- return updatableResponse;
- }
-
- const qItemsIndexMap = mapQItemsIndex(questionnaire);
- const topLevelQRItemsByIndex = getQrItemsIndex(
- questionnaire.item,
- updatableResponse.item,
- qItemsIndexMap
- );
-
- const topLevelQrItems: QuestionnaireResponseItem[] = [];
- for (const [index, topLevelQItem] of questionnaire.item.entries()) {
- const topLevelQRItemOrItems = topLevelQRItemsByIndex[index] ?? {
- linkId: topLevelQItem.linkId,
- text: topLevelQItem.text,
- item: []
- };
-
- const updatedTopLevelQRItem = repopulateItemRecursive(
- topLevelQItem,
- topLevelQRItemOrItems,
- checkedItemsToRepopulate
- );
-
- if (Array.isArray(updatedTopLevelQRItem)) {
- if (updatedTopLevelQRItem.length > 0) {
- topLevelQrItems.push(...updatedTopLevelQRItem);
- }
- continue;
- }
-
- if (updatedTopLevelQRItem && qrItemHasItemsOrAnswer(updatedTopLevelQRItem)) {
- topLevelQrItems.push(updatedTopLevelQRItem);
- }
- }
-
- return { ...updatableResponse, item: topLevelQrItems };
-}
-
function repopulateItemRecursive(
qItem: QuestionnaireItem,
qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null,
diff --git a/packages/smart-forms-renderer/src/utils/updateQr.ts b/packages/smart-forms-renderer/src/utils/updateQr.ts
new file mode 100644
index 00000000..1c3b0c39
--- /dev/null
+++ b/packages/smart-forms-renderer/src/utils/updateQr.ts
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2024 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type {
+ Questionnaire,
+ QuestionnaireItem,
+ QuestionnaireResponse,
+ QuestionnaireResponseItem
+} from 'fhir/r4';
+import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
+import { qrItemHasItemsOrAnswer } from './manageForm';
+
+export type RepopulateFunction = (
+ qItem: QuestionnaireItem,
+ qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null,
+ extraData: T
+) => QuestionnaireResponseItem | QuestionnaireResponseItem[] | null;
+
+/**
+ * A generic (and safe) way to update a QuestionnaireResponse given a recursive function and a set of data i.e. Record, Record
+ * This function relies heavily on mapQItemsIndex() and getQrItemsIndex() to accurately pinpoint the locations of QR items based on their positions in the Q, taking into account repeating group answers, non-filled questions, etc
+ *
+ * @author Sean Fong
+ */
+export function updateQuestionnaireResponse(
+ questionnaire: Questionnaire,
+ questionnaireResponse: QuestionnaireResponse,
+ recursiveUpdateFunction: RepopulateFunction,
+ extraData: T
+) {
+ if (
+ !questionnaire.item ||
+ questionnaire.item.length === 0 ||
+ !questionnaireResponse.item ||
+ questionnaireResponse.item.length === 0
+ ) {
+ return questionnaireResponse;
+ }
+
+ const qItemsIndexMap = mapQItemsIndex(questionnaire);
+ const topLevelQRItemsByIndex = getQrItemsIndex(
+ questionnaire.item,
+ questionnaireResponse.item,
+ qItemsIndexMap
+ );
+
+ const topLevelQrItems = [];
+ for (const [index, topLevelQItem] of questionnaire.item.entries()) {
+ const topLevelQRItemOrItems = topLevelQRItemsByIndex[index] ?? {
+ linkId: topLevelQItem.linkId,
+ text: topLevelQItem.text,
+ item: []
+ };
+
+ const updatedTopLevelQRItem = recursiveUpdateFunction(
+ topLevelQItem,
+ topLevelQRItemOrItems,
+ extraData
+ );
+
+ if (Array.isArray(updatedTopLevelQRItem)) {
+ if (updatedTopLevelQRItem.length > 0) {
+ topLevelQrItems.push(...updatedTopLevelQRItem);
+ }
+ continue;
+ }
+
+ if (updatedTopLevelQRItem && qrItemHasItemsOrAnswer(updatedTopLevelQRItem)) {
+ topLevelQrItems.push(updatedTopLevelQRItem);
+ }
+ }
+
+ return { ...questionnaireResponse, item: topLevelQrItems };
+}
diff --git a/push-extract-image.sh b/push-extract-image.sh
index 7a0687de..2468377a 100644
--- a/push-extract-image.sh
+++ b/push-extract-image.sh
@@ -24,5 +24,5 @@ cd services/extract-express && npm run compile && cd -
# Build the Docker image for multiple architectures, then push to Docker Hub.
docker buildx build --file ./services/extract-express/Dockerfile \
--tag aehrc/smart-forms-extract:latest \
- --tag aehrc/smart-forms-extract:v0.3.1 \
+ --tag aehrc/smart-forms-extract:v0.4.0 \
--platform linux/amd64,linux/arm64/v8 --push --no-cache .
diff --git a/services/extract-express/README.md b/services/extract-express/README.md
index 45452def..3c7ea5e0 100644
--- a/services/extract-express/README.md
+++ b/services/extract-express/README.md
@@ -5,6 +5,8 @@ It is an abstraction on top of an existing StructureMap [$transform](https://hl7
A proof-of-concept StructureMap $transform is defined on https://proxy.smartforms.io/fhir/StructureMap/$transform, leveraging Brian's .NET mapping engine from https://github.com/brianpos/fhir-net-mappinglanguage/tree/main/demo-map-server.
+A `StructureMap/$convert` operation is also defined in the POC implementation to convert a FHIR Mapping Language map to a StructureMap resource, using the same .NET mapping engine.
+
## Configuration
Create a .env file (or copy from example.env) in the root of the project with the following:
```env
@@ -31,9 +33,10 @@ You can use `docker run -p 3003:3003 -e EHR_SERVER_URL=https://proxy.smartforms.
Docker image: https://hub.docker.com/r/aehrc/smart-forms-extract
-**By default, ```FORMS_SERVER_URL``` is set to https://smartforms.csiro.au/api/fhir in the Docker image.**
+**By default, ```FORMS_SERVER_URL``` is set to https://smartforms.csiro.au/api/fhir in the Docker image.** This endpoint is used to resolve referenced FHIR Questionnaires and StructureMaps.
## Sample implementation
-A sample implementation of this service is available at https://proxy.smartforms.io/fhir/QuestionnaireResponse/$extract.
+A sample implementation of the `$extract` service is available at https://proxy.smartforms.io/fhir/QuestionnaireResponse/$extract.
+`StructureMap/$convert` is available at https://proxy.smartforms.io/fhir/StructureMap/$convert.
-Note: The $extract service on https://smartforms.csiro.au/api/fhir only performs processing - it does not persist any data.
+Note: The $extract and $convert service on https://proxy.smartforms.io/fhir only performs processing - it does not persist any data.
diff --git a/services/extract-express/package.json b/services/extract-express/package.json
index 17a61ca5..d99674b9 100644
--- a/services/extract-express/package.json
+++ b/services/extract-express/package.json
@@ -1,11 +1,11 @@
{
"name": "extract-express",
- "version": "0.3.1",
+ "version": "0.4.0",
"description": "",
"main": "lib/index.js",
"scripts": {
"compile": "tsc",
- "start": "node lib/index.js",
+ "start": "node --inspect lib/index.js",
"start:watch": "node --inspect --watch lib/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
diff --git a/services/extract-express/src/debug.ts b/services/extract-express/src/debug.ts
new file mode 100644
index 00000000..cdc7979d
--- /dev/null
+++ b/services/extract-express/src/debug.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2024 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { OperationOutcome, Parameters, ParametersParameter, StructureMap } from 'fhir/r4b';
+
+export function responseIsParametersResource(parameters: any): parameters is DebugOutputParameters {
+ return (
+ parameters &&
+ parameters.resourceType === 'Parameters' &&
+ !!parameters.parameter &&
+ parameters.parameter.length > 0 &&
+ parameters.parameter[2].name === 'parameters' &&
+ parameters.parameter[2].part.length > 0 &&
+ !!parameters.parameter[2].part[1] &&
+ parameters.parameter[2].part[1].name === 'source' &&
+ !!parameters.parameter[2].part[1].resource &&
+ parameters.parameter[2].part[1].resource.resourceType === 'StructureMap'
+ );
+}
+
+export function getStructureMapFromDebugOutputParameters(
+ outputParameters: any
+): StructureMap | null {
+ if (!responseIsParametersResource(outputParameters)) {
+ return null;
+ }
+
+ return outputParameters.parameter[2].part[1].resource;
+}
+
+export interface DebugOutputParameters extends Parameters {
+ parameter: [OutcomeParameter, ResultParameter, DebugParametersParameter, TraceParameter];
+}
+
+interface OutcomeParameter extends ParametersParameter {
+ name: 'outcome';
+ resource: OperationOutcome;
+}
+
+interface ResultParameter extends ParametersParameter {
+ name: 'result';
+ valueString: string;
+}
+
+interface DebugParametersParameter extends ParametersParameter {
+ name: 'parameters';
+ part: [
+ {
+ name: 'evaluator';
+ valueString: string;
+ },
+ {
+ name: 'source';
+ resource: StructureMap;
+ }
+ ];
+}
+
+interface TraceParameter extends ParametersParameter {
+ name: 'content';
+ part: any[];
+}
diff --git a/services/extract-express/src/fhirMappingLanguage.ts b/services/extract-express/src/fhirMappingLanguage.ts
new file mode 100644
index 00000000..315fc076
--- /dev/null
+++ b/services/extract-express/src/fhirMappingLanguage.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export function getFhirMappingLanguageMap(body: any): string | null {
+ if (typeof body === 'string' && body.includes('map')) {
+ return body;
+ }
+
+ return null;
+}
diff --git a/services/extract-express/src/index.ts b/services/extract-express/src/index.ts
index d77ff000..6924c5e0 100644
--- a/services/extract-express/src/index.ts
+++ b/services/extract-express/src/index.ts
@@ -19,6 +19,8 @@ import express from 'express';
import cors from 'cors';
import { getQuestionnaireResponse } from './questionnaireResponse';
import {
+ createFailStructureMapConversionOutcome,
+ createInvalidFhirMappingLanguageMap,
createInvalidParametersOutcome,
createInvalidQuestionnaireCanonicalOutcome,
createNoFormsServerUrlSetOutcome,
@@ -29,8 +31,14 @@ import {
} from './operationOutcome';
import { getQuestionnaire } from './questionnaire';
import { getTargetStructureMap, getTargetStructureMapCanonical } from './structureMap';
-import { createTransformInputParameters, invokeTransform } from './transform';
+import {
+ createTransformInputParametersForConvert,
+ createTransformInputParametersForExtract,
+ invokeTransform
+} from './transform';
import dotenv from 'dotenv';
+import { getFhirMappingLanguageMap } from './fhirMappingLanguage';
+import { getStructureMapFromDebugOutputParameters } from './debug';
const app = express();
const port = 3003;
@@ -52,6 +60,7 @@ app.use(
// Allows the app to accept JSON and URL encoded data up to 50MB
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ extended: true }));
+app.use(express.text());
// Allows the app to work behind reverse proxies, forwarding the correct req.protocol to the /StructureMap/$transform call
// Without this, doing a HTTPS $extract call will result in a HTTP $transform call
@@ -136,7 +145,7 @@ app.post('/fhir/QuestionnaireResponse/\\$extract', async (req, res) => {
return;
}
- const transformInputParameters = createTransformInputParameters(
+ const transformInputParameters = createTransformInputParametersForExtract(
targetStructureMap,
questionnaireResponse
);
@@ -165,6 +174,76 @@ app.post('/fhir/QuestionnaireResponse/\\$extract', async (req, res) => {
}
});
+app.get('/fhir/\\$convert', (_, res) => {
+ res.send(
+ 'This service is healthy!\nHowever, this server only supports StructureMap/$convert.\nPerform a POST request to /fhir/StructureMap/$convert to convert a FHIR Mapping Language map to a StructureMap resource.'
+ );
+});
+
+app.get('/fhir/StructureMap/\\$convert', (_, res) => {
+ res.send(
+ 'This service is healthy!\nPerform a POST request to the same path to convert a FHIR Mapping Language map to a StructureMap resource.'
+ );
+});
+
+app.post('/fhir/StructureMap/\\$convert', async (req, res) => {
+ let ehrServerUrl = req.protocol + '://' + req.get('host') + '/fhir';
+ let ehrServerAuthToken: string | null = null;
+
+ // Set EHR server URL and auth token if provided in env variables
+ if (EHR_SERVER_URL) {
+ ehrServerUrl = EHR_SERVER_URL;
+ ehrServerAuthToken = EHR_SERVER_AUTH_TOKEN ?? null;
+ }
+
+ try {
+ // Get FHIR Mapping Language map from the request body
+ const body = req.body;
+ const fhirMappingLanguageMap = getFhirMappingLanguageMap(body);
+
+ if (!fhirMappingLanguageMap) {
+ const outcome = createInvalidFhirMappingLanguageMap();
+ res.status(400).json(outcome);
+ return;
+ }
+
+ const transformInputParameters =
+ createTransformInputParametersForConvert(fhirMappingLanguageMap);
+
+ const outputParameters = await invokeTransform(
+ transformInputParameters,
+ ehrServerUrl,
+ ehrServerAuthToken ?? undefined,
+ true
+ );
+
+ // Get StructureMap resource from the output parameters
+ const structureMap = getStructureMapFromDebugOutputParameters(outputParameters);
+ if (!structureMap) {
+ const outcome = createFailStructureMapConversionOutcome();
+ res.status(400).json(outcome);
+ return;
+ }
+
+ res.json(structureMap);
+ } catch (error) {
+ console.error(error);
+ if (error instanceof Error) {
+ res.status(500).json(createOperationOutcome(error?.message)); // Sending the error message as an OperationOutcome
+ return;
+ }
+
+ // If the error is not an instance of Error, send a generic error message
+ res
+ .status(500)
+ .json(
+ createOperationOutcome(
+ 'Something went wrong here. Please raise a GitHub issue at https://github.com/aehrc/smart-forms/issues/new'
+ )
+ );
+ }
+});
+
app.listen(port, () => {
console.log(`Transform Express app listening on port ${port}`);
});
diff --git a/services/extract-express/src/operationOutcome.ts b/services/extract-express/src/operationOutcome.ts
index 5d130370..e58c78a2 100644
--- a/services/extract-express/src/operationOutcome.ts
+++ b/services/extract-express/src/operationOutcome.ts
@@ -43,6 +43,19 @@ export function createInvalidParametersOutcome(): OperationOutcome {
};
}
+export function createInvalidFhirMappingLanguageMap(): OperationOutcome {
+ return {
+ resourceType: 'OperationOutcome',
+ issue: [
+ {
+ severity: 'error',
+ code: 'invalid',
+ details: { text: 'Input provided is not a valid FHIR Mapping Language map.' }
+ }
+ ]
+ };
+}
+
export function createInvalidQuestionnaireCanonicalOutcome(): OperationOutcome {
return {
resourceType: 'OperationOutcome',
@@ -109,6 +122,21 @@ export function createNoTargetStructureMapFoundOutcome(
};
}
+export function createFailStructureMapConversionOutcome(): OperationOutcome {
+ return {
+ resourceType: 'OperationOutcome',
+ issue: [
+ {
+ severity: 'error',
+ code: 'invalid',
+ details: {
+ text: `Failed to convert the provided FHIR Mapping Language map to a StructureMap.`
+ }
+ }
+ ]
+ };
+}
+
export function createOperationOutcome(errorMessage: string): OperationOutcome {
return {
resourceType: 'OperationOutcome',
diff --git a/services/extract-express/src/transform.ts b/services/extract-express/src/transform.ts
index f9595318..cf6eecf5 100644
--- a/services/extract-express/src/transform.ts
+++ b/services/extract-express/src/transform.ts
@@ -24,7 +24,7 @@ import type {
} from 'fhir/r4b';
import { HEADERS } from './globals';
-export function createTransformInputParameters(
+export function createTransformInputParametersForExtract(
targetStructureMap: StructureMap,
questionnaireResponse: QuestionnaireResponse
): TransformInputParameters {
@@ -43,12 +43,41 @@ export function createTransformInputParameters(
};
}
+export function createTransformInputParametersForConvert(
+ fhirMappingLanguageMap: string
+): TransformInputParameters {
+ return {
+ resourceType: 'Parameters',
+ parameter: [
+ {
+ name: 'source',
+ valueString: fhirMappingLanguageMap
+ },
+ // Hardcoded content to be empty QuestionnaireResponse since it is not used in the conversion
+ {
+ name: 'content',
+ resource: {
+ resourceType: 'QuestionnaireResponse',
+ status: 'in-progress'
+ }
+ }
+ ]
+ };
+}
+
export async function invokeTransform(
transformInputParameters: TransformInputParameters,
ehrServerUrl: string,
- ehrServerAuthToken?: string
+ ehrServerAuthToken?: string,
+ debugMode?: boolean
): Promise {
- const requestUrl = `${ehrServerUrl}/StructureMap/$transform`;
+ let requestUrl = `${ehrServerUrl}/StructureMap/$transform`;
+
+ // Brian's demo-map-server by default return xml when debug=true, explicitly set it to json
+ if (debugMode) {
+ requestUrl += '?debug=true&_format=json';
+ }
+
const headers = ehrServerAuthToken
? { ...HEADERS, Authorization: `Bearer ${ehrServerAuthToken}` }
: HEADERS;
@@ -59,8 +88,11 @@ export async function invokeTransform(
});
if (!response.ok) {
+ const debugModeMessage = debugMode
+ ? '\nNote: The input FML map might not be valid. If it is valid but you are still getting this error, please raise a GitHub issue at https://github.com/aehrc/smart-forms/issues/new'
+ : '';
throw new Error(
- `HTTP error when performing ${ehrServerUrl}/StructureMap/$transform. Status: ${response.status}`
+ `HTTP error when performing ${ehrServerUrl}/StructureMap/$transform. Status: ${response.status}${debugModeMessage}`
);
}
@@ -68,14 +100,21 @@ export async function invokeTransform(
}
export interface TransformInputParameters extends Parameters {
- parameter: [SourceParameter, ContentParameter];
+ parameter:
+ | [SourceResourceParameter, ContentParameter]
+ | [SourceValueStringParameter, ContentParameter];
}
-interface SourceParameter extends ParametersParameter {
+interface SourceResourceParameter extends ParametersParameter {
name: 'source';
resource: StructureMap;
}
+interface SourceValueStringParameter extends ParametersParameter {
+ name: 'source';
+ valueString: string;
+}
+
interface ContentParameter extends ParametersParameter {
name: 'content';
resource: FhirResource;