diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
new file mode 100644
index 0000000000..bfaa195ac4
--- /dev/null
+++ b/.github/workflows/benchmark.yml
@@ -0,0 +1,71 @@
+# This workflow runs a set of performance benchmarks that run and upload their results to a S3
+# bucket.
+
+name: Benchmark
+
+on:
+ workflow_dispatch:
+
+env:
+ # The add-exports and add-opens flags are required for Java 17
+ MAVEN_OPTS: --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED
+
+jobs:
+ benchmark:
+ name: Benchmark
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ # This is required so that git-commit-id-plugin can find the latest tag.
+ fetch-depth: 0
+ - name: Set up JDK
+ uses: actions/setup-java@v4
+ with:
+ java-version: 17
+ distribution: "zulu"
+ - name: Cache local Maven repository
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |-
+ ${{ runner.os }}-maven-
+ - name: Cache test data
+ id: cache-test-data
+ uses: actions/cache@v4
+ with:
+ path: fhir-server/src/test/resources/test-data/parquet
+ key: ${{ runner.os }}-test-data-${{ hashFiles('fhir-server/src/test/resources/test-data/fhir/*.ndjson', 'encoders/src/main/**/*.java', 'encoders/src/main/**/*.scala', 'fhir-server/src/main/java/au/csiro/pathling/io/Database.java') }}
+ - name: Get current time
+ id: timestamp
+ run: echo "::set-output name=timestamp::$(date +'%Y%m%d%H%M%S')"
+ - name: Run the verify goal with Maven
+ env:
+ # If there is a cache hit on the test data, we don't need to import it. If it is a cache
+ # miss, we still need to explicitly activate the `importTestData` profile, otherwise
+ # it will be deactivated by `skipTests`.
+ PATHLING_PROFILES: runBenchmark,${{ steps.cache-test-data.outputs.cache-hit && '!importTestData' || 'importTestData' }}
+ run: >-
+ mvn --batch-mode verify
+ -pl fhir-server -am
+ -P${{ env.PATHLING_PROFILES }}
+ -DskipSurefireTests
+ -Dpathling.benchmark.testIterations=100
+ -Dpathling.benchmark.warmupIterations=10
+ timeout-minutes: 720
+ - name: Upload benchmark artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: benchmark-results
+ path: "**/jmh-*.json"
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v1
+ with:
+ role-to-assume: arn:aws:iam::865780493209:role/PathlingBenchmarkUpload
+ aws-region: ap-southeast-2
+ - name: Upload benchmark file to S3
+ run: aws s3 sync fhir-server/target/benchmark s3://pathling-benchmark/${{ github.ref }}/${{ github.run_id }}/${{ steps.timestamp.outputs.timestamp }}/
diff --git a/.github/workflows/python-pre-release.yml b/.github/workflows/python-pre-release.yml
index e570126eed..d371413dd3 100644
--- a/.github/workflows/python-pre-release.yml
+++ b/.github/workflows/python-pre-release.yml
@@ -10,23 +10,27 @@ on:
required: true
default: ".dev0"
+env:
+ # The add-exports and add-opens flags are required for Java 17
+ MAVEN_OPTS: --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED
+
jobs:
deploy-python-pre-release:
name: Python pre-release
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
diff --git a/.github/workflows/release-js-client.yml b/.github/workflows/release-js-client.yml
index 30908d7933..5b00f78156 100644
--- a/.github/workflows/release-js-client.yml
+++ b/.github/workflows/release-js-client.yml
@@ -9,23 +9,27 @@ on:
tags:
- 'pathling-client-v**'
+env:
+ # The add-exports and add-opens flags are required for Java 17
+ MAVEN_OPTS: --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED
+
jobs:
deploy:
name: NPM
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: 'zulu'
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
diff --git a/.github/workflows/release-js-import.yml b/.github/workflows/release-js-import.yml
index 6487bae664..0f8a732838 100644
--- a/.github/workflows/release-js-import.yml
+++ b/.github/workflows/release-js-import.yml
@@ -9,23 +9,27 @@ on:
tags:
- 'pathling-import-v**'
+env:
+ # The add-exports and add-opens flags are required for Java 17
+ MAVEN_OPTS: --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED
+
jobs:
deploy:
name: NPM
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: 'zulu'
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 678fc53010..073ebd9b46 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -13,23 +13,27 @@ on:
tags:
- "v**"
+env:
+ # The add-exports and add-opens flags are required for Java 17
+ MAVEN_OPTS: --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED
+
jobs:
deploy-maven:
name: Maven Central
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -63,17 +67,17 @@ jobs:
needs: deploy-maven
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -96,17 +100,17 @@ jobs:
needs: deploy-python
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml
index 66236570ac..3ab6716a3c 100644
--- a/.github/workflows/site.yml
+++ b/.github/workflows/site.yml
@@ -9,24 +9,28 @@ on:
branches:
- main
+env:
+ # The add-exports and add-opens flags are required for Java 17
+ MAVEN_OPTS: --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED
+
jobs:
deploy:
name: GitHub Pages
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Set up Helm
uses: azure/setup-helm@v3
with:
version: 3.13.2
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 3cc069dc88..f121e959cb 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -19,23 +19,27 @@ on:
branches-ignore:
- gh-pages
+env:
+ # The add-exports and add-opens flags are required for Java 17
+ MAVEN_OPTS: --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED
+
jobs:
encoders:
name: Encoders
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -52,17 +56,17 @@ jobs:
${{ env.PATHLING_OPTS }}
timeout-minutes: 20
- name: Upload encoders test reports
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: encoders-surefire-reports
path: encoders/target/surefire-reports
- name: Upload test coverage report
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: encoders-coverage
path: "**/jacoco.xml"
- name: Upload dump files
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: encoders-dump-files
path: |-
@@ -82,17 +86,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -100,7 +104,7 @@ jobs:
${{ runner.os }}-maven-
- name: Cache test data
id: cache-test-data
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: fhir-server/src/test/resources/test-data/parquet
key: ${{ runner.os }}-test-data-${{ hashFiles('fhir-server/src/test/resources/test-data/fhir/*.ndjson', 'encoders/src/main/**/*.java', 'encoders/src/main/**/*.scala', 'fhir-server/src/main/java/au/csiro/pathling/io/Database.java') }}
@@ -124,22 +128,22 @@ jobs:
${{ env.PATHLING_OPTS }}
timeout-minutes: 30
- name: Upload fhir-server test reports
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-1-surefire-reports
path: fhir-server/target/surefire-reports
- name: Upload test coverage report
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-1-coverage
path: "**/jacoco.xml"
- name: Upload timing log
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-1-timing-log
path: fhir-server/target/timing.log
- name: Upload dump files
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-1-dump-files
path: |-
@@ -159,17 +163,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -177,7 +181,7 @@ jobs:
${{ runner.os }}-maven-
- name: Cache test data
id: cache-test-data
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: fhir-server/src/test/resources/test-data/parquet
key: ${{ runner.os }}-test-data-${{ hashFiles('fhir-server/src/test/resources/test-data/fhir/*.ndjson', 'encoders/src/main/**/*.java', 'encoders/src/main/**/*.scala', 'fhir-server/src/main/java/au/csiro/pathling/io/Database.java') }}
@@ -201,22 +205,22 @@ jobs:
${{ env.PATHLING_OPTS }}
timeout-minutes: 30
- name: Upload fhir-server test reports
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-2-surefire-reports
path: fhir-server/target/surefire-reports
- name: Upload test coverage report
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-2-coverage
path: "**/jacoco.xml"
- name: Upload timing log
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-2-timing-log
path: fhir-server/target/timing.log
- name: Upload dump files
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-2-dump-files
path: |-
@@ -242,17 +246,17 @@ jobs:
role-to-assume: arn:aws:iam::865780493209:role/PathlingBenchmarkUpload
aws-region: ap-southeast-2
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -260,7 +264,7 @@ jobs:
${{ runner.os }}-maven-
- name: Cache test data
id: cache-test-data
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: fhir-server/src/test/resources/test-data/parquet
key: ${{ runner.os }}-test-data-${{ hashFiles('fhir-server/src/test/resources/test-data/fhir/*.ndjson', 'encoders/src/main/**/*.java', 'encoders/src/main/**/*.scala', 'fhir-server/src/main/java/au/csiro/pathling/io/Database.java') }}
@@ -281,37 +285,37 @@ jobs:
${{ env.PATHLING_OPTS }}
timeout-minutes: 30
- name: Upload utilities test reports
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: utilities-surefire-reports
path: utilities/target/surefire-reports
- name: Upload terminology test reports
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: terminology-surefire-reports
path: terminology/target/surefire-reports
- name: Upload FHIRPath test reports
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhirpath-surefire-reports
path: fhirpath/target/surefire-reports
- name: Upload FHIR server test reports
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-3-surefire-reports
path: fhir-server/target/surefire-reports
- name: Upload FHIR server test coverage report
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-3-coverage
path: "**/jacoco.xml"
- name: Upload timing log
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-3-timing-log
path: fhir-server/target/timing.log
- name: Upload dump files
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: fhir-server-3-dump-files
path: |-
@@ -334,17 +338,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -386,7 +390,7 @@ jobs:
image-ref: "aehrc/pathling:${{ github.sha }}"
exit-code: "1"
ignore-unfixed: true
- security-checks: "vuln"
+ scanners: "vuln"
severity: CRITICAL
timeout: 20m
- name: Publish test results
@@ -398,82 +402,25 @@ jobs:
check_name: Docker image test report
fail_on_test_failures: true
- benchmark:
- name: Benchmark
- runs-on: ubuntu-latest
- permissions:
- id-token: write
- steps:
- - name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@v1
- with:
- role-to-assume: arn:aws:iam::865780493209:role/PathlingBenchmarkUpload
- aws-region: ap-southeast-2
- - name: Checkout code
- uses: actions/checkout@v3
- with:
- # This is required so that git-commit-id-plugin can find the latest tag.
- fetch-depth: 0
- - name: Set up JDK
- uses: actions/setup-java@v2
- with:
- java-version: 11
- distribution: "zulu"
- - name: Cache local Maven repository
- uses: actions/cache@v3
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |-
- ${{ runner.os }}-maven-
- - name: Cache test data
- id: cache-test-data
- uses: actions/cache@v3
- with:
- path: fhir-server/src/test/resources/test-data/parquet
- key: ${{ runner.os }}-test-data-${{ hashFiles('fhir-server/src/test/resources/test-data/fhir/*.ndjson', 'encoders/src/main/**/*.java', 'encoders/src/main/**/*.scala', 'fhir-server/src/main/java/au/csiro/pathling/io/Database.java') }}
- - name: Get current time
- id: timestamp
- run: echo "::set-output name=timestamp::$(date +'%Y%m%d%H%M%S')"
- - name: Run the verify goal with Maven
- env:
- # If there is a cache hit on the test data, we don't need to import it. If it is a cache
- # miss, we still need to explicitly activate the `importTestData` profile, otherwise
- # it will be deactivated by `skipTests`.
- PATHLING_PROFILES: runBenchmark,${{ steps.cache-test-data.outputs.cache-hit && '!importTestData' || 'importTestData' }}
- run: >-
- mvn --batch-mode verify
- -pl fhir-server -am
- -P${{ env.PATHLING_PROFILES }}
- -DskipSurefireTests
- timeout-minutes: 20
- - name: Upload benchmark artifact
- uses: actions/upload-artifact@v3
- with:
- name: benchmark-results
- path: "**/jmh-*.json"
- - name: Upload benchmark file to S3
- run: aws s3 sync fhir-server/target/benchmark s3://pathling-benchmark/${{ github.ref }}/${{ github.run_id }}/${{ steps.timestamp.outputs.timestamp }}/
-
js-client:
name: JavaScript client
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- - uses: actions/setup-node@v3
+ - uses: actions/setup-node@v4
with:
node-version: "16"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -490,20 +437,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- - uses: actions/setup-node@v3
+ - uses: actions/setup-node@v4
with:
node-version: "16"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -515,7 +462,7 @@ jobs:
-pl lib/import -am
timeout-minutes: 5
- name: Upload import lambda
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: import-lambda
path: lib/import/target/pathling-import-lambda.zip
@@ -525,17 +472,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -547,7 +494,7 @@ jobs:
with:
python-version: 3.8
- name: Cache Python packages
- uses: actions/cache@v3
+ uses: actions/cache@v4
id: pythoncache
with:
path: /home/runner/.cache/pip
@@ -570,17 +517,17 @@ jobs:
${{ env.PATHLING_OPTS }}
timeout-minutes: 30
- name: Upload library API test reports
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: library-api-surefire-reports
path: library-api/target/surefire-reports
- name: Upload library API test coverage report
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: library-api-coverage
path: "**/jacoco.xml"
- name: Upload Python API test coverage report
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: python-api-coverage
path: lib/python/**/coverage.xml
@@ -590,28 +537,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |-
${{ runner.os }}-maven-
- - name: Get R package installation location
- run: echo "R_PACKAGES=$(Rscript -e 'cat(.libPaths(), sep="\n")' | head -n 1)" >> $GITHUB_ENV
- name: Cache R packages
uses: actions/cache@v2
with:
- path: ${{ env.R_PACKAGES }}
+ path: ${{ runner.temp }}/Library
key: r-packages-${{ runner.os }}-${{ hashFiles('lib/R/DESCRIPTION.src') }}
restore-keys: r-packages-${{ runner.os }}-
- name: Extract Spark version
@@ -619,7 +564,7 @@ jobs:
run: echo "SPARK_VERSION=$(mvn help:evaluate -Dexpression=pathling.Rapi.sparkVersion -q -DforceStdout)" >> $GITHUB_ENV
- name: Extract Hadoop version
working-directory: lib/R
- run: echo "HADOOP_VERSION=$(mvn help:evaluate -Dexpression=pathling.Rapi.hadoopVersion -q -DforceStdout)" >> $GITHUB_ENV
+ run: echo "HADOOP_VERSION=$(mvn help:evaluate -Dexpression=pathling.Rapi.hadoopMajorVersion -q -DforceStdout)" >> $GITHUB_ENV
- name: Cache Spark
id: cache-spark
uses: actions/cache@v2
@@ -652,6 +597,15 @@ jobs:
-pl lib/R -am -Pdocs
${{ env.PATHLING_OPTS }}
timeout-minutes: 60
+ - name: Upload check logs
+ uses: actions/upload-artifact@v2
+ if: always()
+ with:
+ name: r-check-logs
+ path: |
+ lib/R/target/pathling.Rcheck/*.log
+ lib/R/target/pathling.Rcheck/*.out
+ lib/R/target/pathling.Rcheck/*.fail
- name: Upload package as artifact
uses: actions/upload-artifact@v2
with:
@@ -663,35 +617,41 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Set up Helm
uses: azure/setup-helm@v3
with:
version: 3.13.2
- - uses: actions/setup-node@v3
+ - uses: actions/setup-node@v4
with:
node-version: "16"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |-
${{ runner.os }}-maven-
+ - name: Cache R packages
+ uses: actions/cache@v2
+ with:
+ path: ${{ runner.temp }}/Library
+ key: r-packages-${{ runner.os }}-${{ hashFiles('lib/R/DESCRIPTION.src') }}
+ restore-keys: r-packages-${{ runner.os }}-
- name: Extract Spark version
working-directory: lib/R
run: echo "SPARK_VERSION=$(mvn help:evaluate -Dexpression=pathling.Rapi.sparkVersion -q -DforceStdout)" >> $GITHUB_ENV
- name: Extract Hadoop version
working-directory: lib/R
- run: echo "HADOOP_VERSION=$(mvn help:evaluate -Dexpression=pathling.Rapi.hadoopVersion -q -DforceStdout)" >> $GITHUB_ENV
+ run: echo "HADOOP_VERSION=$(mvn help:evaluate -Dexpression=pathling.Rapi.hadoopMajorVersion -q -DforceStdout)" >> $GITHUB_ENV
- name: Cache Spark
id: cache-spark
uses: actions/cache@v2
@@ -724,34 +684,34 @@ jobs:
if: github.actor != 'dependabot[bot]'
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Download encoders coverage report
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: encoders-coverage
path: encoders-coverage
- name: Download library API coverage report
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: library-api-coverage
path: library-api-coverage
- name: Download Python API coverage report
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: python-api-coverage
path: python-api-coverage
- name: Download FHIR server coverage report 1
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: fhir-server-1-coverage
path: fhir-server-1-coverage
- name: Download FHIR server coverage report 2
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: fhir-server-2-coverage
path: fhir-server-2-coverage
- name: Download FHIR server coverage report 3
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: fhir-server-3-coverage
path: fhir-server-3-coverage
@@ -766,17 +726,17 @@ jobs:
if: startsWith(github.ref, 'refs/heads/release/')
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- java-version: 11
+ java-version: 17
distribution: "zulu"
- name: Cache local Maven repository
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9fef2d9a2f..5705afcd55 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -30,7 +30,7 @@ Your branch should be named `issue/[GitHub issue #]`.
You will need the following software to build the solution:
-* Java 11
+* Java 17
* Maven 3+
* Node.js 19.x
* Python 3.7+
@@ -53,8 +53,9 @@ follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
The "public API" of Pathling is defined as:
1. the FHIR API;
-2. the public API of the encoders module;
+2. the public API of the library API;
3. the public API of the Python library;
+3. the public API of the R library;
4. the Parquet schema, and;
5. the configuration schema (see
[Configuration](https://pathling.csiro.au/docs/configuration.html)).
diff --git a/LICENSE b/LICENSE
index 0e0ba8bf6b..6a50f430e4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -183,61 +183,65 @@ agree to comply with the licence terms for these components as part of
accessing the Software. Other third party software may also be identified in
separate files distributed with the Software.
-* (Apache License, Version 2.0) HAPI FHIR - Core Library (ca.uhn.hapi.fhir:hapi-fhir-base:6.10.0 - http://jamesagnew.github.io/hapi-fhir/)
-* (Apache License, Version 2.0) HAPI FHIR - Client Framework (ca.uhn.hapi.fhir:hapi-fhir-client:6.10.0 - https://hapifhir.io/hapi-deployable-pom/hapi-fhir-client)
-* (Apache License, Version 2.0) HAPI FHIR - Server Framework (ca.uhn.hapi.fhir:hapi-fhir-server:6.10.0 - https://hapifhir.io/hapi-deployable-pom/hapi-fhir-server)
-* (Apache License, Version 2.0) HAPI FHIR Structures - FHIR R4 (ca.uhn.hapi.fhir:hapi-fhir-structures-r4:6.10.0 - https://hapifhir.io/hapi-deployable-pom/hapi-fhir-structures-r4)
-* (Eclipse Public License 1.0) (GNU Lesser General Public License) Logback Classic Module (ch.qos.logback:logback-classic:1.2.11 - http://logback.qos.ch/logback-classic)
+* (Apache License, Version 2.0) HAPI FHIR - Core Library (ca.uhn.hapi.fhir:hapi-fhir-base:7.0.2 - http://jamesagnew.github.io/hapi-fhir/)
+* (Apache License, Version 2.0) HAPI FHIR - Client Framework (ca.uhn.hapi.fhir:hapi-fhir-client:7.0.2 - https://hapifhir.io/hapi-deployable-pom/hapi-fhir-client)
+* (Apache License, Version 2.0) HAPI FHIR - Server Framework (ca.uhn.hapi.fhir:hapi-fhir-server:7.0.2 - https://hapifhir.io/hapi-deployable-pom/hapi-fhir-server)
+* (Apache License, Version 2.0) HAPI FHIR Structures - FHIR R4 (ca.uhn.hapi.fhir:hapi-fhir-structures-r4:7.0.2 - https://hapifhir.io/hapi-deployable-pom/hapi-fhir-structures-r4)
+* (Eclipse Public License 1.0) (GNU Lesser General Public License) Logback Classic Module (ch.qos.logback:logback-classic:1.4.14 - http://logback.qos.ch/logback-classic)
* (Apache License, Version 2.0) AWS SDK for Java - Bundle (com.amazonaws:aws-java-sdk-bundle:1.12.603 - https://aws.amazon.com/sdkforjava)
* (MIT License) java jwt (com.auth0:java-jwt:4.4.0 - https://github.com/auth0/java-jwt)
-* (MIT License) jwks-rsa (com.auth0:jwks-rsa:0.22.0 - https://github.com/auth0/jwks-rsa-java)
-* (Apache License, Version 2.0) Jackson-core (com.fasterxml.jackson.core:jackson-core:2.15.3 - https://github.com/FasterXML/jackson-core)
+* (MIT License) jwks-rsa (com.auth0:jwks-rsa:0.22.1 - https://github.com/auth0/jwks-rsa-java)
+* (Apache License, Version 2.0) Jackson-core (com.fasterxml.jackson.core:jackson-core:2.16.1 - https://github.com/FasterXML/jackson-core)
* (Apache License, Version 2.0) Woodstox (com.fasterxml.woodstox:woodstox-core:6.4.0 - https://github.com/FasterXML/woodstox)
* (Apache License, Version 2.0) docker-java (com.github.docker-java:docker-java:3.3.4 - https://github.com/docker-java/docker-java)
* (Apache License, Version 2.0) docker-java-transport-okhttp (com.github.docker-java:docker-java-transport-okhttp:3.3.4 - https://github.com/docker-java/docker-java)
-* (Apache License, Version 2.0) WireMock (com.github.tomakehurst:wiremock-jre8-standalone:2.35.0 - http://wiremock.org)
+* (Apache License, Version 2.0) WireMock (com.github.tomakehurst:wiremock-jre8-standalone:2.35.2 - http://wiremock.org)
* (Apache License, Version 2.0) FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/)
* (Apache License, Version 2.0) Gson (com.google.code.gson:gson:2.10 - https://github.com/google/gson/gson)
* (Apache License, Version 2.0) Guava: Google Core Libraries for Java (com.google.guava:guava:32.1.3-jre - https://github.com/google/guava)
* (Apache License, Version 2.0) Apache Commons BeanUtils (commons-beanutils:commons-beanutils:1.9.4 - https://commons.apache.org/proper/commons-beanutils/)
-* (Apache License, Version 2.0) Apache Commons IO (commons-io:commons-io:2.13.0 - https://commons.apache.org/proper/commons-io/)
+* (Apache License, Version 2.0) Apache Commons IO (commons-io:commons-io:2.16.1 - https://commons.apache.org/proper/commons-io/)
* (Apache License, Version 2.0) Commons Lang (commons-lang:commons-lang:2.6 - http://commons.apache.org/lang/)
-* (Apache License, Version 2.0) delta-core (io.delta:delta-core_2.12:2.4.0 - https://delta.io/)
-* (Apache License, Version 2.0) micrometer-registry-prometheus (io.micrometer:micrometer-registry-prometheus:1.11.3 - https://github.com/micrometer-metrics/micrometer)
+* (Apache License, Version 2.0) delta-spark (io.delta:delta-spark_2.12:3.2.0 - https://delta.io/)
+* (Apache License, Version 2.0) micrometer-registry-prometheus (io.micrometer:micrometer-registry-prometheus:1.13.0 - https://github.com/micrometer-metrics/micrometer)
* (MIT License) Sentry SDK (io.sentry:sentry:6.6.0 - https://github.com/getsentry/sentry-java)
-* (Apache License, Version 2.0) Jakarta Bean Validation API (jakarta.validation:jakarta.validation-api:2.0.2 - https://beanvalidation.org)
-* (Apache License, Version 2.0) Joda-Time (joda-time:joda-time:2.12.5 - https://www.joda.org/joda-time/)
+* (Apache License, Version 2.0) Jakarta Bean Validation API (jakarta.validation:jakarta.validation-api:3.0.2 - https://beanvalidation.org)
+* (CDDL + GPLv2 with classpath exception) Java Servlet API (javax.servlet:javax.servlet-api:4.0.1 - https://javaee.github.io/servlet-spec/)
+* (Apache License, Version 2.0) Joda-Time (joda-time:joda-time:2.12.7 - https://www.joda.org/joda-time/)
* (BSD License) ANTLR 4 Tool (org.antlr:antlr4:4.9.3 - http://www.antlr.org)
-* (Apache License, Version 2.0) Apache Commons Lang (org.apache.commons:commons-lang3:3.12.0 - https://commons.apache.org/proper/commons-lang/)
+* (Apache License, Version 2.0) Apache Commons Lang (org.apache.commons:commons-lang3:3.14.0 - https://commons.apache.org/proper/commons-lang/)
+* (Apache License, Version 2.0) Apache Derby Tools (org.apache.derby:derbytools:10.14.2.0 - http://db.apache.org/derby/)
* (Apache License, Version 2.0) Apache Hadoop Amazon Web Services support (org.apache.hadoop:hadoop-aws:3.3.4 - no url defined)
* (Apache License, Version 2.0) Apache Hadoop Client API (org.apache.hadoop:hadoop-client-api:3.3.4 - no url defined)
-* (Apache License, Version 2.0) Spark Project Catalyst (org.apache.spark:spark-catalyst_2.12:3.4.1 - https://spark.apache.org/)
-* (Apache License, Version 2.0) Spark Project Core (org.apache.spark:spark-core_2.12:3.4.1 - https://spark.apache.org/)
-* (Apache License, Version 2.0) Spark Project Hive (org.apache.spark:spark-hive_2.12:3.4.1 - https://spark.apache.org/)
-* (Apache License, Version 2.0) Spark Project Kubernetes (org.apache.spark:spark-kubernetes_2.12:3.4.1 - https://spark.apache.org/spark-kubernetes_2.12/)
-* (Apache License, Version 2.0) Spark Project SQL (org.apache.spark:spark-sql_2.12:3.4.1 - https://spark.apache.org/)
-* (Apache License, Version 2.0) Spark Project Unsafe (org.apache.spark:spark-unsafe_2.12:3.4.1 - https://spark.apache.org/)
+* (Apache License, Version 2.0) Spark Project Catalyst (org.apache.spark:spark-catalyst_2.12:3.5.0 - https://spark.apache.org/)
+* (Apache License, Version 2.0) Spark Project Core (org.apache.spark:spark-core_2.12:3.5.0 - https://spark.apache.org/)
+* (Apache License, Version 2.0) Spark Project Hive (org.apache.spark:spark-hive_2.12:3.5.0 - https://spark.apache.org/)
+* (Apache License, Version 2.0) Spark Project Kubernetes (org.apache.spark:spark-kubernetes_2.12:3.5.0 - https://spark.apache.org/spark-kubernetes_2.12/)
+* (Apache License, Version 2.0) Spark Project SQL (org.apache.spark:spark-sql_2.12:3.5.0 - https://spark.apache.org/)
+* (Apache License, Version 2.0) Spark Project Unsafe (org.apache.spark:spark-unsafe_2.12:3.5.0 - https://spark.apache.org/)
* (Apache License, Version 2.0) Awaitility (org.awaitility:awaitility:4.2.0 - http://awaitility.org)
-* (Apache License, Version 2.0) Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:6.0.13.Final - http://hibernate.org/validator/hibernate-validator)
-* (Apache License, Version 2.0) Infinispan Commons (org.infinispan:infinispan-commons:14.0.21.Final - http://www.infinispan.org/infinispan-commons-parent/infinispan-commons)
-* (Apache License, Version 2.0) Infinispan Core (org.infinispan:infinispan-core:14.0.21.Final - http://www.infinispan.org/infinispan-core)
-* (Public Domain) JSON in Java (org.json:json:20230618 - https://github.com/douglascrockford/JSON-java)
-* (Eclipse Public License v2.0) JUnit Jupiter API (org.junit.jupiter:junit-jupiter-api:5.9.1 - https://junit.org/junit5/)
-* (Eclipse Public License v2.0) JUnit Jupiter Engine (org.junit.jupiter:junit-jupiter-engine:5.9.1 - https://junit.org/junit5/)
-* (Eclipse Public License v2.0) JUnit Jupiter Params (org.junit.jupiter:junit-jupiter-params:5.9.1 - https://junit.org/junit5/)
-* (MIT License) mockito-core (org.mockito:mockito-core:4.8.1 - https://github.com/mockito/mockito)
-* (GNU General Public License (GPL), version 2, with the Classpath exception) JMH Core (org.openjdk.jmh:jmh-core:1.36 - http://openjdk.java.net/projects/code-tools/jmh/jmh-core/)
-* (GNU General Public License (GPL), version 2, with the Classpath exception) JMH Generators: Annotation Processors (org.openjdk.jmh:jmh-generator-annprocess:1.36 - http://openjdk.java.net/projects/code-tools/jmh/jmh-generator-annprocess/)
-* (MIT License) Project Lombok (org.projectlombok:lombok:1.18.28 - https://projectlombok.org)
+* (Apache License, Version 2.0) Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:8.0.1.Final - http://hibernate.org/validator/hibernate-validator)
+* (Apache License, Version 2.0) Infinispan Commons (org.infinispan:infinispan-commons:15.0.3.Final - http://www.infinispan.org)
+* (Apache License, Version 2.0) Infinispan Component Annotations (org.infinispan:infinispan-component-annotations:15.0.3.Final - http://www.infinispan.org)
+* (Apache License, Version 2.0) Infinispan Core (org.infinispan:infinispan-core:15.0.3.Final - http://www.infinispan.org)
+* (Public Domain) JSON in Java (org.json:json:20240303 - https://github.com/douglascrockford/JSON-java)
+* (Eclipse Public License v2.0) JUnit Jupiter API (org.junit.jupiter:junit-jupiter-api:5.10.1 - https://junit.org/junit5/)
+* (Eclipse Public License v2.0) JUnit Jupiter Engine (org.junit.jupiter:junit-jupiter-engine:5.10.1 - https://junit.org/junit5/)
+* (Eclipse Public License v2.0) JUnit Jupiter Params (org.junit.jupiter:junit-jupiter-params:5.10.1 - https://junit.org/junit5/)
+* (MIT License) mockito-core (org.mockito:mockito-core:5.8.0 - https://github.com/mockito/mockito)
+* (GNU General Public License (GPL), version 2, with the Classpath exception) JMH Core (org.openjdk.jmh:jmh-core:1.37 - http://openjdk.java.net/projects/code-tools/jmh/jmh-core/)
+* (GNU General Public License (GPL), version 2, with the Classpath exception) JMH Generators: Annotation Processors (org.openjdk.jmh:jmh-generator-annprocess:1.37 - http://openjdk.java.net/projects/code-tools/jmh/jmh-generator-annprocess/)
+* (MIT License) Project Lombok (org.projectlombok:lombok:1.18.32 - https://projectlombok.org)
* (Apache License, Version 2.0) Scala Library (org.scala-lang:scala-library:2.12.17 - https://www.scala-lang.org/)
* (Apache License, Version 2.0) JSONassert (org.skyscreamer:jsonassert:1.5.1 - https://github.com/skyscreamer/JSONassert)
-* (MIT License) SLF4J API Module (org.slf4j:slf4j-api:1.7.36 - http://www.slf4j.org)
-* (Apache License, Version 2.0) spring-boot-configuration-processor (org.springframework.boot:spring-boot-configuration-processor:2.7.18 - https://spring.io/projects/spring-boot)
-* (Apache License, Version 2.0) spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:2.7.18 - https://spring.io/projects/spring-boot)
-* (Apache License, Version 2.0) spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:2.7.18 - https://spring.io/projects/spring-boot)
-* (Apache License, Version 2.0) spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:2.7.18 - https://spring.io/projects/spring-boot)
-* (Apache License, Version 2.0) spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:2.7.18 - https://spring.io/projects/spring-boot)
-* (Apache License, Version 2.0) spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:2.7.18 - https://spring.io/projects/spring-boot)
-* (Apache License, Version 2.0) spring-security-oauth2-jose (org.springframework.security:spring-security-oauth2-jose:5.7.10 - https://spring.io/projects/spring-security)
-* (Apache License, Version 2.0) spring-security-oauth2-resource-server (org.springframework.security:spring-security-oauth2-resource-server:5.7.10 - https://spring.io/projects/spring-security)
-* (Apache License, Version 2.0) spring-security-test (org.springframework.security:spring-security-test:5.7.10 - https://spring.io/projects/spring-security)
+* (MIT License) SLF4J API Module (org.slf4j:slf4j-api:2.0.9 - http://www.slf4j.org)
+* (Apache License, Version 2.0) spring-boot-configuration-processor (org.springframework.boot:spring-boot-configuration-processor:3.2.0 - https://spring.io/projects/spring-boot)
+* (Apache License, Version 2.0) spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:3.2.0 - https://spring.io/projects/spring-boot)
+* (Apache License, Version 2.0) spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:3.2.0 - https://spring.io/projects/spring-boot)
+* (Apache License, Version 2.0) spring-boot-starter-jetty (org.springframework.boot:spring-boot-starter-jetty:3.2.0 - https://spring.io/projects/spring-boot)
+* (Apache License, Version 2.0) spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:3.2.0 - https://spring.io/projects/spring-boot)
+* (Apache License, Version 2.0) spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:3.2.0 - https://spring.io/projects/spring-boot)
+* (Apache License, Version 2.0) spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:3.2.0 - https://spring.io/projects/spring-boot)
+* (Apache License, Version 2.0) spring-security-oauth2-jose (org.springframework.security:spring-security-oauth2-jose:6.2.0 - https://spring.io/projects/spring-security)
+* (Apache License, Version 2.0) spring-security-oauth2-resource-server (org.springframework.security:spring-security-oauth2-resource-server:6.2.0 - https://spring.io/projects/spring-security)
+* (Apache License, Version 2.0) spring-security-test (org.springframework.security:spring-security-test:6.2.0 - https://spring.io/projects/spring-security)
diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md
new file mode 100644
index 0000000000..d95fab3b47
--- /dev/null
+++ b/RELEASE_CHECKLIST.md
@@ -0,0 +1,52 @@
+# Release checklist
+
+- [ ] Create a release branch (`release/[version]`)
+- [ ] Update the version to a SNAPSHOT version for pre-release activities
+- [ ] Update all dependencies
+- [ ] Get all tests and checks passing on CI ("Test" workflow)
+- [ ] Create a Python pre-release ("Python pre-release" workflow, manually
+ triggered)
+- [ ] Test snapshot library API, dev Python library release and R package on
+ target Databricks release
+- [ ] Deploy pre-release Docker image to demo cluster and test
+- [ ] Update the supported Databricks Runtime versions in the
+ documentation (`site/docs/libraries/installation/databricks.md`)
+- [ ] Update licenses (`mvn install -DskipTests -Plicenses`)
+- [ ] Update the version to the release version
+- [ ] Tag the release (`v[version]`)
+- [ ] Merge to main and make sure it is green
+- [ ] Create a GitHub release for the tag, describe new features, bug fixes and
+ breaking changes
+- [ ] Submit the built R package to CRAN manually
+
+## Updating the version
+
+The version number needs to be changed in the following places:
+
+- [ ] All POM files (version of the main POM, references to parent in all child
+ POMs)
+- [ ] `lib/python/Dockerfile`
+- [ ] `docs/libraries/installation/spark.md`
+
+## JavaScript libraries
+
+The JavaScript libraries (`lib/import` and `lib/js`) are versioned independently
+of the rest of the project. To release changes to these libraries, follow these
+steps:
+
+- [ ] Update the version in `lib/import/package.json`
+ and/or `lib/js/package.json` (using `npm version`)
+- [ ] Tag the repository with `pathling-import-v[version]`
+ and/or `pathling-client-v[version]`
+- [ ] Push the tag to GitHub
+
+## Helm chart
+
+The Helm chart is versioned independently of the rest of the project. The Helm
+chart publishes the chart to the web site based upon the metadata in
+the `Chart.yaml` file.
+
+It only currently supports the publishing of the current version of the chart.
+
+No tagging is required to release the chart, it is built and published upon
+every execution of the "Deploy site" workflow.
diff --git a/deployment/helm/pathling/Chart.yaml b/deployment/helm/pathling/Chart.yaml
index 82fc23febf..1bfa669e3a 100644
--- a/deployment/helm/pathling/Chart.yaml
+++ b/deployment/helm/pathling/Chart.yaml
@@ -3,7 +3,7 @@ name: pathling
description: A Helm chart for Pathling Server
icon: https://raw.githubusercontent.com/aehrc/pathling/main/media/logo-icon-colour-detail.svg
type: application
-version: 1.0.0
+version: 1.0.2
maintainers:
- name: John Grimes
email: John.Grimes@csiro.au
diff --git a/deployment/helm/pathling/README.md b/deployment/helm/pathling/README.md
index a66309c857..8a7820f802 100644
--- a/deployment/helm/pathling/README.md
+++ b/deployment/helm/pathling/README.md
@@ -75,7 +75,7 @@ scenario, all processing is performed on a single pod.
```yml
pathling:
- image: aehrc/pathling:6
+ image: aehrc/pathling:7
resources:
requests:
cpu: 2
@@ -113,7 +113,7 @@ pods on demand (at the cost of some latency).
```yml
pathling:
- image: aehrc/pathling:6
+ image: aehrc/pathling:7
resources:
requests:
cpu: 1
@@ -138,7 +138,7 @@ pathling:
logging.level.au.csiro.pathling: debug
spark.master: k8s://https://kubernetes.default.svc
spark.kubernetes.namespace: pathling
- spark.kubernetes.executor.container.image: aehrc/pathling:6
+ spark.kubernetes.executor.container.image: aehrc/pathling:7
spark.kubernetes.executor.volumes.hostPath.warehouse.options.path: /home/user/data/pathling
spark.kubernetes.executor.volumes.hostPath.warehouse.mount.path: /usr/share/warehouse
spark.kubernetes.executor.volumes.hostPath.warehouse.mount.readOnly: false
diff --git a/deployment/helm/pathling/templates/deployment.yaml b/deployment/helm/pathling/templates/deployment.yaml
index 66b0ad15f6..3e0a11fff9 100644
--- a/deployment/helm/pathling/templates/deployment.yaml
+++ b/deployment/helm/pathling/templates/deployment.yaml
@@ -46,7 +46,7 @@ spec:
{{- if gt (add (len .Values.pathling.config) (len .Values.pathling.secretConfig)) 0 }}
env:
- name: JAVA_TOOL_OPTIONS
- value: {{ printf "-Xmx%s %s" .Values.pathling.resources.maxHeapSize .Values.pathling.additionalJavaOptions }}
+ value: {{ printf "-Xmx%s --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED %s" .Values.pathling.resources.maxHeapSize .Values.pathling.additionalJavaOptions }}
- name: spark.driver.host
value: pathling-driver
- name: spark.driver.port
diff --git a/deployment/helm/pathling/templates/service.yaml b/deployment/helm/pathling/templates/service.yaml
index 0f8bf0a94c..d19bc4fdd7 100644
--- a/deployment/helm/pathling/templates/service.yaml
+++ b/deployment/helm/pathling/templates/service.yaml
@@ -3,7 +3,7 @@ apiVersion: v1
metadata:
name: pathling-fhir
spec:
- type: ClusterIP
+ type: NodePort
ports:
- port: 8080
targetPort: 8080
diff --git a/encoders/pom.xml b/encoders/pom.xml
index cb406fb35c..1964678a50 100644
--- a/encoders/pom.xml
+++ b/encoders/pom.xml
@@ -84,6 +84,10 @@
spark-unsafe_${pathling.scalaVersion}
provided
+
+ javax.servlet
+ javax.servlet-api
+
@@ -204,6 +208,11 @@
+
+
+ **/logback.xml
+
+
diff --git a/encoders/src/license/THIRD-PARTY.properties b/encoders/src/license/THIRD-PARTY.properties
index f17c8e7aae..1a72053020 100644
--- a/encoders/src/license/THIRD-PARTY.properties
+++ b/encoders/src/license/THIRD-PARTY.properties
@@ -18,13 +18,16 @@
# - BSD 3 Clause
# - BSD 3-clause
# - BSD-3-Clause
+# - CDDL + GPLv2 with classpath exception
# - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
# - EDL 1.0
# - EPL 2.0
+# - EPL-2.0
# - Eclipse Public License - v 1.0
# - Eclipse Public License v2.0
# - GNU General Public License, version 2
# - GNU Lesser General Public License
+# - GPL-2.0-with-classpath-exception
# - GPL2 w/ CPE
# - Indiana University Extreme! Lab Software License, vesion 1.1.1
# - LGPL 2.1
@@ -50,5 +53,5 @@
# Please fill the missing licenses for dependencies :
#
#
-#Fri Dec 01 16:32:30 AEST 2023
+#Mon May 20 15:42:34 AEST 2024
oro--oro--2.0.8=
diff --git a/encoders/src/main/java/au/csiro/pathling/config/EncodingConfiguration.java b/encoders/src/main/java/au/csiro/pathling/config/EncodingConfiguration.java
index 0dbf89b9fc..e1cf3c2c8c 100644
--- a/encoders/src/main/java/au/csiro/pathling/config/EncodingConfiguration.java
+++ b/encoders/src/main/java/au/csiro/pathling/config/EncodingConfiguration.java
@@ -23,9 +23,9 @@
package au.csiro.pathling.config;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
import java.util.Set;
-import javax.validation.constraints.Min;
-import javax.validation.constraints.NotNull;
import au.csiro.pathling.encoders.FhirEncoders;
import lombok.Builder;
import lombok.Data;
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/Catalyst.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/Catalyst.scala
new file mode 100644
index 0000000000..3afaa88da2
--- /dev/null
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/Catalyst.scala
@@ -0,0 +1,55 @@
+package au.csiro.pathling.encoders
+
+import org.apache.spark.sql.catalyst.expressions.Expression
+import org.apache.spark.sql.catalyst.expressions.objects.StaticInvoke
+import org.apache.spark.sql.types.DataType
+
+
+/**
+ * This class provides a compatibility layer for Spark Catalyst.
+ * It is used to createnew Expressions using constructors compatible with the runtime
+ * version of Spark Catalyst.
+ * This is necessary to address running Pathling in Databricks environments,
+ * which habitually use different (newer) version of Catalyst that one used by
+ * the corresponding Spark public release.
+ */
+object Catalyst {
+
+ private lazy val staticInvokeAdapter: (Class[_], DataType, String, Seq[Expression]) => StaticInvoke = {
+ val constructor = classOf[StaticInvoke].getConstructors.head
+ if (constructor.getParameterCount == 8) {
+ // catalyst 3.5.x (used by Spark 3.5.x)
+ StaticInvoke.apply(_, _, _, _)
+ } else if (constructor.getParameterCount == 9) {
+ // catalyst 4.0.0-preview-rc1 (used by Databricks runtime 14.3 LTS with Spark 3.5.0)
+ (staticObject: Class[_], dataType: DataType, functionName: String, arguments: Seq[Expression]) =>
+ constructor.newInstance(staticObject, dataType, functionName,
+ arguments,
+ Nil,
+ Boolean.box(true),
+ Boolean.box(true),
+ Boolean.box(true),
+ None).asInstanceOf[StaticInvoke]
+ } else {
+ throw new IllegalStateException(
+ "Unsupported version of Spark Catalyst with InvokeStatic constructor: " + constructor)
+ }
+ }
+
+ /**
+ * Creates a new [[StaticInvoke]] expression using a constructor compatible with the runtime version of Spark Catalyst.
+ *
+ * @param staticObject the class object to invoke.
+ * @param dataType the return type of the function.
+ * @param functionName the name of the function to invoke.
+ * @param arguments the arguments to pass to the function.
+ * @return the new [[StaticInvoke]] expression.
+ */
+ def staticInvoke(
+ staticObject: Class[_],
+ dataType: DataType,
+ functionName: String,
+ arguments: Seq[Expression] = Nil): StaticInvoke = {
+ staticInvokeAdapter(staticObject, dataType, functionName, arguments)
+ }
+}
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/EncoderUtils.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/EncoderUtils.scala
index 9074ba3781..95bbde9df4 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/EncoderUtils.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/EncoderUtils.scala
@@ -37,7 +37,7 @@ object EncoderUtils {
}
def arrayExpression(array: Expression): StaticInvoke = {
- StaticInvoke(
+ Catalyst.staticInvoke(
classOf[util.Arrays],
ObjectType(classOf[util.List[_]]),
"asList",
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/Expressions.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/Expressions.scala
index a2862ff3f7..7b243bd56b 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/Expressions.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/Expressions.scala
@@ -112,6 +112,54 @@ case class InstanceOf(value: Expression,
}
+
+/**
+ * Gets value of an element of a HAPI object. This is could be composed from `If` and `Invoke`
+ * expression but having it as a dedicated expression makes the serializer expression more readable
+ * and also avoids the problems with 'If' optimisations.
+ *
+ * @param value the expression with the reference to the HAPI object
+ * @param dataType the data type of the element to get
+ * @param hasMethod the name of method to check if the element is present, e.g. hasNameElement()
+ * @param getMethod the name of method to get the value of the element, e.g. getNameElement()
+ * @return the value of the element or null if the element is not present or the object is null
+ */
+case class GetHapiValue(value: Expression,
+ dataType: DataType,
+ hasMethod: String, getMethod: String)
+ extends Expression with NonSQLExpression {
+
+ override def nullable: Boolean = true
+
+ override def children: Seq[Expression] = value :: Nil
+
+ override def eval(input: InternalRow): Any =
+ throw new UnsupportedOperationException("Only code-generated evaluation is supported.")
+
+ override def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
+
+ val obj = value.genCode(ctx)
+ val javaType = CodeGenerator.javaType(dataType)
+ val code =
+ code"""
+ |// BEGIN: GetHapiValue
+ |${obj.code}
+ |boolean ${ev.isNull} = true;
+ |$javaType ${ev.value} = ${CodeGenerator.defaultValue(dataType)};
+ |if (!${obj.isNull} && ${obj.value}.$hasMethod()) {
+ | ${ev.isNull} = false;
+ | ${ev.value} = ${obj.value}.$getMethod();
+ |}
+ |// END: GetHapiValue
+ """.stripMargin
+ ev.copy(code = code)
+ }
+
+ override protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]): Expression = {
+ GetHapiValue(newChildren.head, dataType, hasMethod, getMethod)
+ }
+}
+
/**
* Casts the result of an expression to another type.
*
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/QuantitySupport.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/QuantitySupport.scala
index ad789d07c2..9efc513f2f 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/QuantitySupport.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/QuantitySupport.scala
@@ -26,11 +26,9 @@ package au.csiro.pathling.encoders
import au.csiro.pathling.encoders.terminology.ucum.Ucum
import au.csiro.pathling.sql.types.FlexiDecimal
import au.csiro.pathling.sql.types.FlexiDecimalSupport.createFlexiDecimalSerializer
-import org.apache.spark.sql.Column
+import org.apache.spark.sql.catalyst.expressions.Expression
import org.apache.spark.sql.catalyst.expressions.objects.{Invoke, StaticInvoke}
-import org.apache.spark.sql.catalyst.expressions.{CreateNamedStruct, Expression, If, IsNull, Literal}
-import org.apache.spark.sql.functions.{lit, struct}
-import org.apache.spark.sql.types.{DataTypes, Decimal, DecimalType, ObjectType, StructField}
+import org.apache.spark.sql.types.{DataTypes, ObjectType, StructField}
import org.apache.spark.unsafe.types.UTF8String
/**
@@ -54,15 +52,15 @@ object QuantitySupport {
val canonicalizedValue =
createFlexiDecimalSerializer(
- StaticInvoke(classOf[Ucum], ObjectType(classOf[java.math.BigDecimal]),
+ Catalyst.staticInvoke(classOf[Ucum], ObjectType(classOf[java.math.BigDecimal]),
"getCanonicalValue", Seq(valueExp, codeExp)))
val canonicalizedCode =
- StaticInvoke(
+ Catalyst.staticInvoke(
classOf[UTF8String],
DataTypes.StringType,
"fromString",
- StaticInvoke(classOf[Ucum], ObjectType(classOf[java.lang.String]), "getCanonicalCode",
+ Catalyst.staticInvoke(classOf[Ucum], ObjectType(classOf[java.lang.String]), "getCanonicalCode",
Seq(valueExp, codeExp)) :: Nil)
Seq(
(VALUE_CANONICALIZED_FIELD_NAME, canonicalizedValue),
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/SchemaConverter.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/SchemaConverter.scala
index 96bfacce85..2ca837c0cc 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/SchemaConverter.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/SchemaConverter.scala
@@ -24,8 +24,7 @@
package au.csiro.pathling.encoders
import au.csiro.pathling.encoders.ExtensionSupport.{EXTENSIONS_FIELD_NAME, FID_FIELD_NAME}
-import au.csiro.pathling.encoders.QuantitySupport.{CODE_CANONICALIZED_FIELD_NAME, VALUE_CANONICALIZED_FIELD_NAME}
-import au.csiro.pathling.encoders.datatypes.{DataTypeMappings, DecimalCustomCoder}
+import au.csiro.pathling.encoders.datatypes.DataTypeMappings
import au.csiro.pathling.schema.SchemaVisitor
import au.csiro.pathling.schema.SchemaVisitor.isCollection
import ca.uhn.fhir.context._
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/SchemaProcessor.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/SchemaProcessor.scala
index 731b05f58e..d5a1b30dc0 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/SchemaProcessor.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/SchemaProcessor.scala
@@ -24,6 +24,7 @@ package au.csiro.pathling.encoders
import au.csiro.pathling.schema._
import ca.uhn.fhir.context._
+import org.hl7.fhir.instance.model.api.IBaseReference
/**
@@ -95,8 +96,20 @@ trait SchemaProcessor[DT, SF] extends SchemaVisitor[DT, SF] with EncoderSettings
}
}
+ private def includeElement(elementDefinition: BaseRuntimeElementDefinition[_]): Boolean = {
+ val nestingLevel = EncodingContext.currentNestingLevel(elementDefinition)
+ if (classOf[IBaseReference].isAssignableFrom(elementDefinition.getImplementingClass)) {
+ // This is a special provision for References which disallows any nesting.
+ // It removes the `assigner` field from the Identifier type instances
+ // nested inside a Reference (in its `identifier` element).
+ nestingLevel <= 0
+ } else {
+ nestingLevel <= maxNestingLevel
+ }
+ }
+
override def visitElement(elementCtx: ElementCtx[DT, SF]): Seq[SF] = {
- if (EncodingContext.currentNestingLevel(elementCtx.elementDefinition) <= maxNestingLevel) {
+ if (includeElement(elementCtx.elementDefinition)) {
buildValue(elementCtx.childDefinition, elementCtx.elementDefinition, elementCtx.elementName)
} else {
Nil
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/SerializerBuilder.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/SerializerBuilder.scala
index 25e4489ab2..c057fcca22 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/SerializerBuilder.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/SerializerBuilder.scala
@@ -24,10 +24,8 @@
package au.csiro.pathling.encoders
import au.csiro.pathling.encoders.ExtensionSupport.{EXTENSIONS_FIELD_NAME, FID_FIELD_NAME}
-import au.csiro.pathling.encoders.QuantitySupport.{CODE_CANONICALIZED_FIELD_NAME, VALUE_CANONICALIZED_FIELD_NAME}
import au.csiro.pathling.encoders.SerializerBuilderProcessor.{dataTypeToUtf8Expr, getChildExpression, objectTypeFor}
-import au.csiro.pathling.encoders.datatypes.{DataTypeMappings, DecimalCustomCoder}
-import au.csiro.pathling.encoders.terminology.ucum.Ucum
+import au.csiro.pathling.encoders.datatypes.DataTypeMappings
import au.csiro.pathling.schema.SchemaVisitor.isCollection
import au.csiro.pathling.schema._
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum
@@ -36,7 +34,7 @@ import org.apache.spark.sql.catalyst.expressions.objects.{ExternalMapToCatalyst,
import org.apache.spark.sql.catalyst.expressions.{BoundReference, CreateNamedStruct, Expression, If, IsNull, Literal}
import org.apache.spark.sql.types._
import org.apache.spark.unsafe.types.UTF8String
-import org.hl7.fhir.instance.model.api.{IBaseDatatype, IBaseHasExtensions, IBaseResource}
+import org.hl7.fhir.instance.model.api.{IBaseDatatype, IBaseHasExtensions, IBaseReference, IBaseResource}
import org.hl7.fhir.r4.model.{Base, Extension, Quantity}
import org.hl7.fhir.utilities.xhtml.XhtmlNode
@@ -97,7 +95,7 @@ private[encoders] class SerializerBuilderProcessor(expression: Expression,
private def createExtensionsFields(definition: BaseRuntimeElementCompositeDefinition[_]): Seq[(String, Expression)] = {
val maybeExtensionValueField = definition match {
case _: RuntimeResourceDefinition =>
- val collectExtensionsExpression = StaticInvoke(
+ val collectExtensionsExpression = Catalyst.staticInvoke(
classOf[SerializerBuilderProcessor],
ObjectType(classOf[Map[Int, java.util.List[Extension]]]),
"flattenExtensions",
@@ -113,7 +111,7 @@ private[encoders] class SerializerBuilderProcessor(expression: Expression,
case _ => Nil
}
// append _fid serializer
- (FID_FIELD_NAME, StaticInvoke(
+ (FID_FIELD_NAME, Catalyst.staticInvoke(
classOf[System], IntegerType, "identityHashCode",
expression :: Nil)) :: maybeExtensionValueField
}
@@ -190,11 +188,12 @@ private[encoders] object SerializerBuilderProcessor {
private def getChildExpression(parentObject: Expression,
childDefinition: BaseRuntimeChildDefinition,
dataType: DataType): Expression = {
- Invoke(parentObject,
- accessorFor(childDefinition),
- dataType)
+
+ val (hasMethod, getMethod) = accessorsFor(childDefinition)
+ GetHapiValue(parentObject, dataType, hasMethod, getMethod)
}
+
private def getChildExpression(parentObject: Expression,
childDefinition: BaseRuntimeChildDefinition): Expression = {
@@ -209,7 +208,7 @@ private[encoders] object SerializerBuilderProcessor {
}
private def dataTypeToUtf8Expr(inputObject: Expression): Expression = {
- StaticInvoke(
+ Catalyst.staticInvoke(
classOf[UTF8String],
DataTypes.StringType,
"fromString",
@@ -219,23 +218,39 @@ private[encoders] object SerializerBuilderProcessor {
}
/**
- * Returns the accessor method for the given child field.
+ * Returns the accessor methods for the given child field.
+ * These are the 'has' and 'get' methods that test for the presence of the field an retrieve its value.
+ *
+ * @param field the runtime child definition of the field
+ * @return a tuple containing the names of 'has' and 'get' methods
*/
- private def accessorFor(field: BaseRuntimeChildDefinition): String = {
+ private def accessorsFor(field: BaseRuntimeChildDefinition): (String, String) = {
+
+ def defaultAccessors(suffix: String): (String, String) = {
+ ("has" + suffix, "get" + suffix)
+ }
// Primitive single-value types typically use the Element suffix in their
// accessors, with the exception of the "div" field for reasons that are not clear.
//noinspection DuplicatedCode
- if (field.isInstanceOf[RuntimeChildPrimitiveDatatypeDefinition] &&
- field.getMax == 1 &&
- field.getElementName != "div")
- "get" + field.getElementName.capitalize + "Element"
- else {
- if (field.getElementName.equals("class")) {
- "get" + field.getElementName.capitalize + "_"
- } else {
- "get" + field.getElementName.capitalize
- }
+ field match {
+ case p: RuntimeChildPrimitiveDatatypeDefinition if p.getMax == 1 && p
+ .getElementName != "div" =>
+ if ("reference" == p.getElementName && classOf[IBaseReference]
+ .isAssignableFrom(p.getField.getDeclaringClass)) {
+ // Special case for subclasses of IBaseReference.
+ // The accessor getReferenceElement returns IdType rather than
+ // StringType and getReferenceElement_ needs to be used instead.
+ // All subclasses of IBaseReference have a getReferenceElement_
+ // method.
+ ("hasReferenceElement", "getReferenceElement_")
+ } else {
+ defaultAccessors(p.getElementName.capitalize + "Element")
+ }
+ case f if f.getElementName.equals("class") =>
+ defaultAccessors(f.getElementName.capitalize + "_")
+ case _ =>
+ defaultAccessors(field.getElementName.capitalize)
}
}
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/DataTypeMappings.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/DataTypeMappings.scala
index 326c28eb94..51fcda1af7 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/DataTypeMappings.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/DataTypeMappings.scala
@@ -23,7 +23,7 @@
package au.csiro.pathling.encoders.datatypes
-import au.csiro.pathling.encoders.ExpressionWithName
+import au.csiro.pathling.encoders.{Catalyst, ExpressionWithName}
import ca.uhn.fhir.context._
import org.apache.spark.sql.catalyst.expressions.Expression
import org.apache.spark.sql.catalyst.expressions.objects.{Invoke, StaticInvoke}
@@ -60,7 +60,7 @@ trait DataTypeMappings {
*/
def dataTypeToUtf8Expr(inputObject: Expression): Expression = {
- StaticInvoke(
+ Catalyst.staticInvoke(
classOf[UTF8String],
DataTypes.StringType,
"fromString",
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/DecimalCustomCoder.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/DecimalCustomCoder.scala
index eb7ef335d8..f24e1e7c04 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/DecimalCustomCoder.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/DecimalCustomCoder.scala
@@ -24,7 +24,7 @@
package au.csiro.pathling.encoders.datatypes
import au.csiro.pathling.encoders.EncoderUtils.arrayExpression
-import au.csiro.pathling.encoders.ExpressionWithName
+import au.csiro.pathling.encoders.{Catalyst, ExpressionWithName}
import au.csiro.pathling.encoders.datatypes.DecimalCustomCoder.decimalType
import org.apache.spark.sql.catalyst.expressions.objects.{Invoke, NewInstance, StaticInvoke}
import org.apache.spark.sql.catalyst.expressions.{Expression, Literal}
@@ -54,7 +54,7 @@ case class DecimalCustomCoder(elementName: String) extends CustomCoder {
decimalExpression(addToPath)
} else {
// Let's to this manually as we need to zip two independent arrays into into one
- val array = StaticInvoke(
+ val array = Catalyst.staticInvoke(
classOf[DecimalCustomCoder],
ObjectType(classOf[Array[Any]]),
"zipToDecimal",
@@ -66,7 +66,7 @@ case class DecimalCustomCoder(elementName: String) extends CustomCoder {
}
override def customSerializer(evaluator: (Expression => Expression) => Expression): Seq[ExpressionWithName] = {
- val valueExpression = evaluator(exp => StaticInvoke(classOf[Decimal],
+ val valueExpression = evaluator(exp => Catalyst.staticInvoke(classOf[Decimal],
decimalType,
"apply",
Invoke(exp, "getValue", ObjectType(classOf[java.math.BigDecimal])) :: Nil))
@@ -95,7 +95,7 @@ case class DecimalCustomCoder(elementName: String) extends CustomCoder {
}
private def scaleExpression(inputObject: Expression) = {
- StaticInvoke(classOf[Math],
+ Catalyst.staticInvoke(classOf[Math],
IntegerType,
"min", Literal(decimalType.scale) ::
Invoke(Invoke(inputObject, "getValue", ObjectType(classOf[java.math.BigDecimal])),
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/IdCustomCoder.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/IdCustomCoder.scala
index 899ff27f2c..cb1b3e85f8 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/IdCustomCoder.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/IdCustomCoder.scala
@@ -24,7 +24,7 @@
package au.csiro.pathling.encoders.datatypes
import au.csiro.pathling.encoders.EncoderUtils.arrayExpression
-import au.csiro.pathling.encoders.ExpressionWithName
+import au.csiro.pathling.encoders.{Catalyst, ExpressionWithName}
import org.apache.spark.sql.catalyst.expressions.Expression
import org.apache.spark.sql.catalyst.expressions.objects.{Invoke, MapObjects, NewInstance, StaticInvoke}
import org.apache.spark.sql.types._
@@ -72,11 +72,11 @@ case class IdCustomCoder(elementName: String) extends CustomCoder {
override def customSerializer(evaluator: (Expression => Expression) => Expression): Seq[ExpressionWithName] = {
val idExpression = evaluator(
- exp => StaticInvoke(classOf[UTF8String], DataTypes.StringType, "fromString",
+ exp => Catalyst.staticInvoke(classOf[UTF8String], DataTypes.StringType, "fromString",
List(Invoke(exp, "getIdPart", ObjectType(classOf[String])))))
val versionedIdExpression = evaluator(
- exp => StaticInvoke(classOf[UTF8String], DataTypes.StringType, "fromString",
+ exp => Catalyst.staticInvoke(classOf[UTF8String], DataTypes.StringType, "fromString",
List(Invoke(exp, "getValue", ObjectType(classOf[String])))))
Seq((elementName, idExpression), (versionedName, versionedIdExpression))
}
diff --git a/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/R4DataTypeMappings.scala b/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/R4DataTypeMappings.scala
index 920e1afad2..1bf67ed1a4 100644
--- a/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/R4DataTypeMappings.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/encoders/datatypes/R4DataTypeMappings.scala
@@ -24,13 +24,12 @@
package au.csiro.pathling.encoders.datatypes
import au.csiro.pathling.encoders.datatypes.R4DataTypeMappings.{fhirPrimitiveToSparkTypes, isValidOpenElementType}
-import au.csiro.pathling.encoders.{ExpressionWithName, StaticField}
+import au.csiro.pathling.encoders.{Catalyst, ExpressionWithName, StaticField}
import ca.uhn.fhir.context._
import ca.uhn.fhir.model.api.TemporalPrecisionEnum
import org.apache.spark.sql.catalyst.analysis.GetColumnByOrdinal
import org.apache.spark.sql.catalyst.expressions.objects.{InitializeJavaBean, Invoke, NewInstance, StaticInvoke}
import org.apache.spark.sql.catalyst.expressions.{Cast, Expression, Literal}
-import org.apache.spark.sql.internal.SQLConf
import org.apache.spark.sql.types.{DataType, DataTypes, ObjectType}
import org.hl7.fhir.instance.model.api.{IBase, IBaseDatatype, IPrimitiveType}
import org.hl7.fhir.r4.model._
@@ -55,36 +54,13 @@ class R4DataTypeMappings extends DataTypeMappings {
override def baseType(): Class[_ <: IBaseDatatype] = classOf[org.hl7.fhir.r4.model.Type]
- override def overrideCompositeExpression(inputObject: Expression,
+ override def overrideCompositeExpression(inputObject: Expression,
definition: BaseRuntimeElementCompositeDefinition[_]): Option[Seq[ExpressionWithName]] = {
-
- if (definition.getImplementingClass == classOf[Reference]) {
- // Reference type, so return only supported fields. We also explicitly use the IIDType for the
- // reference element, since that differs from the conventions used to infer other types.
- val reference = dataTypeToUtf8Expr(
- Invoke(inputObject,
- "getReferenceElement",
- ObjectType(classOf[IdType])))
-
- val display = dataTypeToUtf8Expr(
- Invoke(inputObject,
- "getDisplayElement",
- ObjectType(classOf[org.hl7.fhir.r4.model.StringType])))
-
- Some(List(("reference", reference), ("display", display)))
- } else {
- None
- }
+ None
}
override def skipField(definition: BaseRuntimeElementCompositeDefinition[_],
child: BaseRuntimeChildDefinition): Boolean = {
-
- // References may be recursive, so include only the reference adn display name.
- val skipRecursiveReference = definition.getImplementingClass == classOf[Reference] &&
- !(child.getElementName == "reference" ||
- child.getElementName == "display")
-
// Contains elements are currently not encoded in our Spark dataset.
val skipContains = definition
.getImplementingClass == classOf[ValueSet.ValueSetExpansionContainsComponent] &&
@@ -95,8 +71,7 @@ class R4DataTypeMappings extends DataTypeMappings {
// "modifierExtensionExtension", not "extensionExtension".
// See: https://github.com/hapifhir/hapi-fhir/issues/3414
val skipModifierExtension = child.getElementName.equals("modifierExtension")
-
- skipRecursiveReference || skipContains || skipModifierExtension
+ skipContains || skipModifierExtension
}
override def primitiveEncoderExpression(inputObject: Expression,
@@ -175,13 +150,13 @@ class R4DataTypeMappings extends DataTypeMappings {
ObjectType(classOf[TemporalPrecisionEnum]),
"MILLI")
- val UTCZone = StaticInvoke(classOf[TimeZone],
+ val UTCZone = Catalyst.staticInvoke(classOf[TimeZone],
ObjectType(classOf[TimeZone]),
"getTimeZone",
Literal("UTC", ObjectType(classOf[String])) :: Nil)
NewInstance(primitiveClass,
- List(StaticInvoke(org.apache.spark.sql.catalyst.util.DateTimeUtils.getClass,
+ List(Catalyst.staticInvoke(org.apache.spark.sql.catalyst.util.DateTimeUtils.getClass,
ObjectType(classOf[java.sql.Timestamp]),
"toJavaTimestamp",
getPath :: Nil),
diff --git a/encoders/src/main/scala/au/csiro/pathling/sql/types/FlexiDecimalSupport.scala b/encoders/src/main/scala/au/csiro/pathling/sql/types/FlexiDecimalSupport.scala
index d4cfebdc7e..808ed5c99b 100644
--- a/encoders/src/main/scala/au/csiro/pathling/sql/types/FlexiDecimalSupport.scala
+++ b/encoders/src/main/scala/au/csiro/pathling/sql/types/FlexiDecimalSupport.scala
@@ -23,6 +23,7 @@
package au.csiro.pathling.sql.types
+import au.csiro.pathling.encoders.Catalyst
import org.apache.spark.sql.Column
import org.apache.spark.sql.catalyst.expressions.objects.{Invoke, StaticInvoke}
import org.apache.spark.sql.catalyst.expressions.{CreateNamedStruct, Expression, If, IsNull, Literal}
@@ -57,12 +58,12 @@ object FlexiDecimalSupport {
// TODO: Performance: consider if the normalized value could be cached in
// a variable
- val normalizedExpression: Expression = StaticInvoke(classOf[FlexiDecimal],
+ val normalizedExpression: Expression = Catalyst.staticInvoke(classOf[FlexiDecimal],
ObjectType(classOf[java.math.BigDecimal]),
"normalize",
expression :: Nil)
- val valueExpression: Expression = StaticInvoke(classOf[Decimal],
+ val valueExpression: Expression = Catalyst.staticInvoke(classOf[Decimal],
FlexiDecimal.DECIMAL_TYPE,
"apply",
Invoke(normalizedExpression, "unscaledValue",
diff --git a/encoders/src/test/java/au/csiro/pathling/encoders/FhirEncodersTest.java b/encoders/src/test/java/au/csiro/pathling/encoders/FhirEncodersTest.java
index 426f55801f..b72a5a7793 100644
--- a/encoders/src/test/java/au/csiro/pathling/encoders/FhirEncodersTest.java
+++ b/encoders/src/test/java/au/csiro/pathling/encoders/FhirEncodersTest.java
@@ -23,11 +23,14 @@
package au.csiro.pathling.encoders;
+import static org.apache.spark.sql.functions.col;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import ca.uhn.fhir.parser.IParser;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.math.BigDecimal;
@@ -273,13 +276,80 @@ public void coding() {
@Test
public void reference() {
+ final Condition conditionWithReferences = TestData.conditionWithReferencesWithIdentifiers();
+
+ final Dataset conditionL3Dataset = spark
+ .createDataset(ImmutableList.of(conditionWithReferences), ENCODERS_L3.of(Condition.class));
+
+ final Condition decodedL3Condition = conditionL3Dataset.head();
+
+ assertEquals(
+ RowFactory.create(
+ "withReferencesWithIdentifiers",
+ "Patient/example",
+ "http://terminology.hl7.org/CodeSystem/v2-0203",
+ "MR",
+ "https://fhir.example.com/identifiers/mrn",
+ "urn:id"
+ ),
+ conditionL3Dataset.select(
+ col("id"),
+ col("subject.reference"),
+ col("subject.identifier.type.coding.system").getItem(0),
+ col("subject.identifier.type.coding.code").getItem(0),
+ col("subject.identifier.system"),
+ col("subject.identifier.value")
+ ).head());
+
+ assertEquals("Patient/example",
+ decodedL3Condition.getSubject().getReference());
+
+ assertEquals("urn:id",
+ decodedL3Condition.getSubject().getIdentifier().getValue());
+
+ // the assigner should be pruned from the reference identifier.
+ assertTrue(conditionWithReferences.getSubject().getIdentifier().hasAssigner());
+ assertFalse(decodedL3Condition.getSubject().getIdentifier().hasAssigner());
+ }
+
- assertEquals(condition.getSubject().getReference(),
- conditionsDataset.select("subject.reference").head().get(0));
- assertEquals(condition.getSubject().getReference(),
- decodedCondition.getSubject().getReference());
+ @Test
+ public void identifier() {
+ final Condition conditionWithIdentifiers = TestData.conditionWithIdentifiersWithReferences();
+
+ final Dataset conditionL3Dataset = spark
+ .createDataset(ImmutableList.of(conditionWithIdentifiers), ENCODERS_L3.of(Condition.class));
+
+ final Condition decodedL3Condition = conditionL3Dataset.head();
+
+ assertEquals(
+ RowFactory.create(
+ "withIdentifiersWithReferences",
+ "http://terminology.hl7.org/CodeSystem/v2-0203",
+ "MR",
+ "https://fhir.example.com/identifiers/mrn",
+ "urn:id01",
+ "Organization/001",
+ "urn:id02"
+ ),
+ conditionL3Dataset.select(
+ col("id"),
+ col("identifier.type.coding").getItem(0).getField("system").getItem(0),
+ col("identifier.type.coding").getItem(0).getField("code").getItem(0),
+ col("identifier.system").getItem(0),
+ col("identifier.value").getItem(0),
+ col("identifier.assigner.reference").getItem(0),
+ col("identifier.assigner.identifier.value").getItem(0)
+ ).head());
+
+ // the assigner should be pruned from the reference identifier.
+ assertTrue(conditionWithIdentifiers.getIdentifier().get(0).getAssigner().getIdentifier()
+ .hasAssigner());
+ assertFalse(
+ decodedL3Condition.getIdentifier().get(0).getAssigner().getIdentifier().hasAssigner());
}
+
@Test
public void integer() {
@@ -325,13 +395,13 @@ public void choiceBigDecimalInQuestionnaire() {
.getAnswerDecimalType().getValue();
final BigDecimal queriedDecimal = (BigDecimal) questionnaireDataset
- .select(functions.col("item").getItem(0).getField("enableWhen").getItem(0)
+ .select(col("item").getItem(0).getField("enableWhen").getItem(0)
.getField("answerDecimal"))
.head()
.get(0);
final int queriedDecimal_scale = questionnaireDataset
- .select(functions.col("item").getItem(0).getField("enableWhen").getItem(0)
+ .select(col("item").getItem(0).getField("enableWhen").getItem(0)
.getField("answerDecimal_scale"))
.head()
.getInt(0);
@@ -360,13 +430,13 @@ public void choiceBigDecimalInQuestionnaireResponse() {
.getValueDecimalType().getValue();
final BigDecimal queriedDecimal = (BigDecimal) questionnaireResponseDataset
- .select(functions.col("item").getItem(0).getField("answer").getItem(0)
+ .select(col("item").getItem(0).getField("answer").getItem(0)
.getField("valueDecimal"))
.head()
.get(0);
final int queriedDecimal_scale = questionnaireResponseDataset
- .select(functions.col("item").getItem(0).getField("answer").getItem(0)
+ .select(col("item").getItem(0).getField("answer").getItem(0)
.getField("valueDecimal_scale"))
.head()
.getInt(0);
@@ -516,13 +586,13 @@ public void testNestedQuestionnaire() {
assertEquals(Stream.of("Item/0", "Item/0", "Item/0", "Item/0").map(RowFactory::create)
.collect(Collectors.toUnmodifiableList()),
- questionnaireDataset_L3.select(functions.col("item").getItem(0).getField("linkId"))
+ questionnaireDataset_L3.select(col("item").getItem(0).getField("linkId"))
.collectAsList());
assertEquals(Stream.of(null, "Item/1.0", "Item/1.0", "Item/1.0").map(RowFactory::create)
.collect(Collectors.toUnmodifiableList()),
questionnaireDataset_L3
- .select(functions.col("item")
+ .select(col("item")
.getItem(1).getField("item")
.getItem(0).getField("linkId"))
.collectAsList());
@@ -530,7 +600,7 @@ public void testNestedQuestionnaire() {
assertEquals(Stream.of(null, null, "Item/2.1.0", "Item/2.1.0").map(RowFactory::create)
.collect(Collectors.toUnmodifiableList()),
questionnaireDataset_L3
- .select(functions.col("item")
+ .select(col("item")
.getItem(2).getField("item")
.getItem(1).getField("item")
.getItem(0).getField("linkId"))
@@ -539,7 +609,7 @@ public void testNestedQuestionnaire() {
assertEquals(Stream.of(null, null, null, "Item/3.2.1.0").map(RowFactory::create)
.collect(Collectors.toUnmodifiableList()),
questionnaireDataset_L3
- .select(functions.col("item")
+ .select(col("item")
.getItem(3).getField("item")
.getItem(2).getField("item")
.getItem(1).getField("item")
@@ -555,4 +625,43 @@ public void testQuantityComparator() {
assertEquals(originalComparator.toCode(), queriedComparator);
}
+
+ @Test
+ public void nullEncoding() {
+ // empty elements of all types should be encoded as nulls
+ final Observation emptyObservation = new Observation();
+ assertFalse(emptyObservation.hasSubject());
+ final Dataset observationsDataset = spark.createDataset(
+ ImmutableList.of(emptyObservation),
+ ENCODERS_L0.of(Observation.class));
+ // 'subject' is a struct
+ // 'identifier' is an array of struct
+ // 'status' is a primitive type
+ final Row subjectRow = observationsDataset.toDF().select("subject", "identifier", "status")
+ .first();
+ assertTrue(subjectRow.isNullAt(0));
+ assertTrue(subjectRow.isNullAt(1));
+ assertTrue(subjectRow.isNullAt(2));
+ }
+
+
+ @Test
+ public void nullEncodingFromJson() {
+ final IParser parser = ENCODERS_L0.getContext().newJsonParser();
+ final Observation emptyObservationFromJson = parser.parseResource(Observation.class,
+ "{ \"resourceType\": \"Observation\"}");
+ assertFalse(emptyObservationFromJson.hasSubject());
+ final Dataset observationsDataset = spark.createDataset(
+ ImmutableList.of(emptyObservationFromJson),
+ ENCODERS_L0.of(Observation.class));
+ // 'subject' is a struct
+ // 'identifier' is an array of struct
+ // 'status' is a primitive type
+ final Row subjectRow = observationsDataset.toDF().select("subject", "identifier", "status")
+ .first();
+ assertTrue(subjectRow.isNullAt(0));
+ assertTrue(subjectRow.isNullAt(1));
+ assertTrue(subjectRow.isNullAt(2));
+ }
+
}
diff --git a/encoders/src/test/java/au/csiro/pathling/encoders/LightweightFhirEncodersTest.java b/encoders/src/test/java/au/csiro/pathling/encoders/LightweightFhirEncodersTest.java
index 849ded3fcd..6f1d11f512 100644
--- a/encoders/src/test/java/au/csiro/pathling/encoders/LightweightFhirEncodersTest.java
+++ b/encoders/src/test/java/au/csiro/pathling/encoders/LightweightFhirEncodersTest.java
@@ -41,16 +41,21 @@
import org.apache.spark.sql.Row;
import org.apache.spark.sql.catalyst.InternalRow;
import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder;
-import org.apache.spark.sql.catalyst.encoders.RowEncoder;
import org.hl7.fhir.r4.model.BaseResource;
+import org.hl7.fhir.r4.model.CodeableConcept;
+import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Condition;
import org.hl7.fhir.r4.model.Device;
+import org.hl7.fhir.r4.model.Expression;
import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Identifier;
+import org.hl7.fhir.r4.model.Identifier.IdentifierUse;
import org.hl7.fhir.r4.model.MolecularSequence;
import org.hl7.fhir.r4.model.MolecularSequence.MolecularSequenceQualityRocComponent;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.PlanDefinition;
import org.hl7.fhir.r4.model.PlanDefinition.PlanDefinitionActionComponent;
+import org.hl7.fhir.r4.model.Reference;
import org.json4s.jackson.JsonMethods;
import org.junit.jupiter.api.Test;
import scala.collection.mutable.WrappedArray;
@@ -162,6 +167,65 @@ public void testHtmlNarrative() {
assertSerDeIsIdentity(encoder, conditionWithNarrative);
}
+ @Test
+ public void testReference() {
+ final ExpressionEncoder encoder = fhirEncoders
+ .of(Condition.class);
+ final Condition conditionWithFullReference = new Condition();
+ final Identifier identifier = new Identifier()
+ .setSystem("urn:id-system")
+ .setValue("id-valule")
+ .setUse(IdentifierUse.OFFICIAL)
+ .setType(new CodeableConcept().addCoding(new Coding().setCode("code").setSystem("system"))
+ .setText("text"));
+ final Reference referenceWithAllFields = new Reference("Patient/1234")
+ .setDisplay("Some Display Name")
+ .setType("Patient")
+ .setIdentifier(identifier);
+ // Set also the Element inherited fields
+ referenceWithAllFields.setId("some-id");
+ conditionWithFullReference.setSubject(referenceWithAllFields);
+ assertSerDeIsIdentity(encoder, conditionWithFullReference);
+ }
+
+ @Test
+ public void testIdentifier() {
+ final ExpressionEncoder encoder = fhirEncoders
+ .of(Condition.class);
+ final Condition conditionWithIdentifierWithAssigner = new Condition();
+
+ final Reference assignerReference = new Reference("Organization/1234")
+ .setDisplay("Some Display Name")
+ .setType("Organization");
+
+ final Identifier identifier = new Identifier()
+ .setSystem("urn:id-system")
+ .setValue("id-valule")
+ .setUse(IdentifierUse.OFFICIAL)
+ .setAssigner(assignerReference)
+ .setType(new CodeableConcept().addCoding(new Coding().setCode("code").setSystem("system"))
+ .setText("text"));
+ conditionWithIdentifierWithAssigner.addIdentifier(identifier);
+ assertSerDeIsIdentity(encoder, conditionWithIdentifierWithAssigner);
+ }
+
+ @Test
+ public void testExpression() {
+
+ // Expression contains 'reference' field
+ // We are checking that it is encoded in generic way not and not the subject to special case for Reference 'reference' field.
+ final ExpressionEncoder encoder = fhirEncoders
+ .of(PlanDefinition.class);
+
+ final PlanDefinition planDefinition = new PlanDefinition();
+
+ final PlanDefinitionActionComponent actionComponent = planDefinition
+ .getActionFirstRep();
+ actionComponent.getConditionFirstRep().setExpression(new Expression().setLanguage("language")
+ .setExpression("expression").setDescription("description"));
+ assertSerDeIsIdentity(encoder, planDefinition);
+ }
+
@Test
public void testThrowsExceptionWhenUnsupportedResource() {
for (final String resourceName : EXCLUDED_RESOURCES) {
@@ -191,7 +255,7 @@ public void testEncodesExtensions() {
// Deserialize the InternalRow to a Row with explicit schema.
final ExpressionEncoder rowEncoder = EncoderUtils
- .defaultResolveAndBind(RowEncoder.apply(encoder.schema()));
+ .defaultResolveAndBind(ExpressionEncoder.apply(encoder.schema()));
final Row conditionRow = rowEncoder.createDeserializer().apply(serializedRow);
// Get the extensionContainer.
@@ -241,7 +305,7 @@ public void testQuantityCanonicalization() {
final InternalRow serializedRow = resolvedEncoder.createSerializer().apply(observation);
final ExpressionEncoder rowEncoder = EncoderUtils.defaultResolveAndBind(
- RowEncoder.apply(encoder.schema()));
+ ExpressionEncoder.apply(encoder.schema()));
final Row observationRow = rowEncoder.createDeserializer().apply(serializedRow);
final Row quantityRow = observationRow.getStruct(observationRow.fieldIndex("valueQuantity"));
@@ -258,13 +322,13 @@ public void testQuantityArrayCanonicalization() {
final InternalRow serializedRow = resolvedEncoder.createSerializer().apply(device);
final ExpressionEncoder rowEncoder = EncoderUtils.defaultResolveAndBind(
- RowEncoder.apply(encoder.schema()));
+ ExpressionEncoder.apply(encoder.schema()));
final Row deviceRow = rowEncoder.createDeserializer().apply(serializedRow);
final List properties = deviceRow.getList(deviceRow.fieldIndex("property"));
final Row propertyRow = properties.get(0);
final List quantityArray = propertyRow.getList(propertyRow.fieldIndex("valueQuantity"));
-
+
final Row quantity1 = quantityArray.get(0);
assertQuantity(quantity1, "0.0010", "m");
diff --git a/encoders/src/test/java/au/csiro/pathling/encoders/SchemaConverterTest.java b/encoders/src/test/java/au/csiro/pathling/encoders/SchemaConverterTest.java
index 02fc596650..eb36ee8467 100644
--- a/encoders/src/test/java/au/csiro/pathling/encoders/SchemaConverterTest.java
+++ b/encoders/src/test/java/au/csiro/pathling/encoders/SchemaConverterTest.java
@@ -25,6 +25,7 @@
import static au.csiro.pathling.test.SchemaAsserts.assertFieldNotPresent;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -93,9 +94,8 @@ public class SchemaConverterTest {
private StructType medRequestSchema;
private StructType questionnaireSchema;
private StructType questionnaireResponseSchema;
-
private StructType deviceSchema;
-
+ private StructType observationSchema_L2;
/**
* Traverses a DataType recursively passing all encountered StructTypes to the provided consumer.
@@ -173,16 +173,17 @@ public void setUp() {
questionnaireSchema = converter_L0.resourceSchema(Questionnaire.class);
questionnaireResponseSchema = converter_L0.resourceSchema(QuestionnaireResponse.class);
deviceSchema = converter_L0.resourceSchema(Device.class);
+ observationSchema_L2 = converter_L2.resourceSchema(Observation.class);
}
@Test
public void resourceHasId() {
- assertTrue(getField(conditionSchema, true, "id") instanceof StringType);
+ assertInstanceOf(StringType.class, getField(conditionSchema, true, "id"));
}
@Test
public void boundCodeToStruct() {
- assertTrue(getField(conditionSchema, true, "verificationStatus") instanceof StructType);
+ assertInstanceOf(StructType.class, getField(conditionSchema, true, "verificationStatus"));
}
@Test
@@ -190,11 +191,11 @@ public void codingToStruct() {
final DataType codingType = getField(conditionSchema, true, "severity", "coding");
- assertTrue(getField(codingType, true, "system") instanceof StringType);
- assertTrue(getField(codingType, true, "version") instanceof StringType);
- assertTrue(getField(codingType, true, "code") instanceof StringType);
- assertTrue(getField(codingType, true, "display") instanceof StringType);
- assertTrue(getField(codingType, true, "userSelected") instanceof BooleanType);
+ assertInstanceOf(StringType.class, getField(codingType, true, "system"));
+ assertInstanceOf(StringType.class, getField(codingType, true, "version"));
+ assertInstanceOf(StringType.class, getField(codingType, true, "code"));
+ assertInstanceOf(StringType.class, getField(codingType, true, "display"));
+ assertInstanceOf(BooleanType.class, getField(codingType, true, "userSelected"));
}
@Test
@@ -202,30 +203,30 @@ public void codeableConceptToStruct() {
final DataType codeableType = getField(conditionSchema, true, "severity");
- assertTrue(codeableType instanceof StructType);
- assertTrue(getField(codeableType, true, "coding") instanceof ArrayType);
- assertTrue(getField(codeableType, true, "text") instanceof StringType);
+ assertInstanceOf(StructType.class, codeableType);
+ assertInstanceOf(ArrayType.class, getField(codeableType, true, "coding"));
+ assertInstanceOf(StringType.class, getField(codeableType, true, "text"));
}
@Test
public void idToString() {
- assertTrue(getField(conditionSchema, true, "id") instanceof StringType);
+ assertInstanceOf(StringType.class, getField(conditionSchema, true, "id"));
}
@Test
public void narrativeToStruct() {
- assertTrue(getField(conditionSchema, true, "text", "status") instanceof StringType);
- assertTrue(getField(conditionSchema, true, "text", "div") instanceof StringType);
+ assertInstanceOf(StringType.class, getField(conditionSchema, true, "text", "status"));
+ assertInstanceOf(StringType.class, getField(conditionSchema, true, "text", "div"));
}
@Test
public void expandChoiceFields() {
- assertTrue(getField(conditionSchema, true, "onsetPeriod") instanceof StructType);
- assertTrue(getField(conditionSchema, true, "onsetRange") instanceof StructType);
- assertTrue(getField(conditionSchema, true, "onsetDateTime") instanceof StringType);
- assertTrue(getField(conditionSchema, true, "onsetString") instanceof StringType);
- assertTrue(getField(conditionSchema, true, "onsetAge") instanceof StructType);
+ assertInstanceOf(StructType.class, getField(conditionSchema, true, "onsetPeriod"));
+ assertInstanceOf(StructType.class, getField(conditionSchema, true, "onsetRange"));
+ assertInstanceOf(StringType.class, getField(conditionSchema, true, "onsetDateTime"));
+ assertInstanceOf(StringType.class, getField(conditionSchema, true, "onsetString"));
+ assertInstanceOf(StructType.class, getField(conditionSchema, true, "onsetAge"));
}
@Test
@@ -244,19 +245,21 @@ public void orderChoiceFields() {
@Test
public void decimalWithinChoiceField() {
- assertTrue(getField(questionnaireSchema, true, "item", "enableWhen",
- "answerDecimal") instanceof DecimalType);
- assertTrue(getField(questionnaireSchema, true, "item", "enableWhen",
- "answerDecimal_scale") instanceof IntegerType);
- assertTrue(getField(questionnaireResponseSchema, true, "item", "answer",
- "valueDecimal") instanceof DecimalType);
- assertTrue(getField(questionnaireResponseSchema, true, "item", "answer",
- "valueDecimal_scale") instanceof IntegerType);
+ assertInstanceOf(DecimalType.class, getField(questionnaireSchema, true, "item", "enableWhen",
+ "answerDecimal"));
+ assertInstanceOf(IntegerType.class, getField(questionnaireSchema, true, "item", "enableWhen",
+ "answerDecimal_scale"));
+ assertInstanceOf(DecimalType.class,
+ getField(questionnaireResponseSchema, true, "item", "answer",
+ "valueDecimal"));
+ assertInstanceOf(IntegerType.class,
+ getField(questionnaireResponseSchema, true, "item", "answer",
+ "valueDecimal_scale"));
}
@Test
public void instantToTimestamp() {
- assertTrue(getField(observationSchema, true, "issued") instanceof TimestampType);
+ assertInstanceOf(TimestampType.class, getField(observationSchema, true, "issued"));
}
@Test
@@ -266,17 +269,59 @@ public void timeToString() {
@Test
public void bigDecimalToDecimal() {
- assertTrue(
- getField(observationSchema, true, "valueQuantity", "value") instanceof DecimalType);
+ assertInstanceOf(DecimalType.class,
+ getField(observationSchema, true, "valueQuantity", "value"));
}
@Test
public void reference() {
- assertTrue(
- getField(observationSchema, true, "subject", "reference") instanceof StringType);
- assertTrue(getField(observationSchema, true, "subject", "display") instanceof StringType);
+ assertInstanceOf(StringType.class, getField(observationSchema, true, "subject", "id"));
+ assertInstanceOf(StringType.class, getField(observationSchema, true, "subject", "reference"));
+ assertInstanceOf(StringType.class, getField(observationSchema, true, "subject", "display"));
+ assertInstanceOf(StringType.class, getField(observationSchema, true, "subject", "type"));
+ assertInstanceOf(StructType.class, getField(observationSchema, true, "subject", "identifier"));
+ assertInstanceOf(StringType.class,
+ getField(observationSchema, true, "subject", "identifier", "value"));
+
}
+ @Test
+ public void identifier() {
+ assertInstanceOf(StringType.class,
+ unArray(getField(observationSchema, true, "identifier", "value")));
+ // `assigner` field should be present in the root level `Identifier` schema.
+ assertInstanceOf(StructType.class,
+ unArray(getField(observationSchema, true, "identifier", "assigner")));
+ assertInstanceOf(StringType.class,
+ unArray(getField(observationSchema, true, "identifier", "assigner", "reference")));
+
+ }
+
+ @Test
+ public void identifierInReference() {
+ //
+ // Identifier (assigner) in root Reference
+ //
+ assertFieldNotPresent("assigner", getField(observationSchema, true, "subject", "identifier"));
+ // The `assigner` field should not be present in Identifier schema of the Reference `identifier` field.
+ assertFieldNotPresent("assigner",
+ getField(observationSchema_L2, true, "subject", "identifier"));
+
+ //
+ // Identifier (assigner) in a Reference nested in an Identifier
+ //
+ // the `identifier` field should not be present because for normal nesting rules for 0-level nesting
+ assertFieldNotPresent("identifier",
+ unArray(getField(observationSchema, true, "identifier", "assigner")));
+ // the `identifier` field should be present because for normal nesting rules for 2-level nesting
+ assertInstanceOf(StructType.class,
+ unArray(getField(observationSchema_L2, true, "identifier", "assigner", "identifier")));
+ // but it should not have the assigner field
+ assertFieldNotPresent("assigner",
+ unArray(getField(observationSchema_L2, true, "identifier", "assigner", "identifier")));
+ }
+
+
@Test
public void preferredNameOnly() {
@@ -374,7 +419,7 @@ public void testExtensions() {
final MapType extensionsContainerType = (MapType) getField(extensionSchema, true,
"_extension");
assertEquals(DataTypes.IntegerType, extensionsContainerType.keyType());
- assertTrue(extensionsContainerType.valueType() instanceof ArrayType);
+ assertInstanceOf(ArrayType.class, extensionsContainerType.valueType());
traverseSchema(extensionSchema, t -> {
assertEquals(DataTypes.IntegerType, t.fields()[t.fieldIndex("_fid")].dataType());
@@ -424,13 +469,13 @@ public void testQuantityArray() {
}
private void assertQuantityType(final DataType quantityType) {
- assertTrue(getField(quantityType, true, "value") instanceof DecimalType);
- assertTrue(getField(quantityType, true, "value_scale") instanceof IntegerType);
- assertTrue(getField(quantityType, true, "comparator") instanceof StringType);
- assertTrue(getField(quantityType, true, "unit") instanceof StringType);
- assertTrue(getField(quantityType, true, "system") instanceof StringType);
- assertTrue(getField(quantityType, true, "code") instanceof StringType);
+ assertInstanceOf(DecimalType.class, getField(quantityType, true, "value"));
+ assertInstanceOf(IntegerType.class, getField(quantityType, true, "value_scale"));
+ assertInstanceOf(StringType.class, getField(quantityType, true, "comparator"));
+ assertInstanceOf(StringType.class, getField(quantityType, true, "unit"));
+ assertInstanceOf(StringType.class, getField(quantityType, true, "system"));
+ assertInstanceOf(StringType.class, getField(quantityType, true, "code"));
assertEquals(FlexiDecimal.DATA_TYPE, getField(quantityType, true, "_value_canonicalized"));
- assertTrue(getField(quantityType, true, "_code_canonicalized") instanceof StringType);
+ assertInstanceOf(StringType.class, getField(quantityType, true, "_code_canonicalized"));
}
}
diff --git a/encoders/src/test/java/au/csiro/pathling/encoders/TestData.java b/encoders/src/test/java/au/csiro/pathling/encoders/TestData.java
index 010d1586a3..1faa47c94b 100644
--- a/encoders/src/test/java/au/csiro/pathling/encoders/TestData.java
+++ b/encoders/src/test/java/au/csiro/pathling/encoders/TestData.java
@@ -138,6 +138,44 @@ public static Condition newCondition() {
return condition;
}
+ public static Condition conditionWithReferencesWithIdentifiers() {
+ final Condition condition = new Condition();
+ condition.setId("withReferencesWithIdentifiers");
+ final Coding typeCoding = new Coding("http://terminology.hl7.org/CodeSystem/v2-0203", "MR",
+ null);
+ final CodeableConcept typeConcept = new CodeableConcept(typeCoding);
+ condition.setSubject(
+ new Reference("Patient/example")
+ .setDisplay("Display name")
+ .setIdentifier(
+ new Identifier()
+ .setType(typeConcept)
+ .setSystem("https://fhir.example.com/identifiers/mrn")
+ .setValue("urn:id")
+ .setAssigner(new Reference("Organization/001"))
+ )
+ );
+ return condition;
+ }
+
+ public static Condition conditionWithIdentifiersWithReferences() {
+ final Condition condition = new Condition();
+ condition.setId("withIdentifiersWithReferences");
+ final Coding typeCoding = new Coding("http://terminology.hl7.org/CodeSystem/v2-0203", "MR",
+ null);
+ final CodeableConcept typeConcept = new CodeableConcept(typeCoding);
+ condition
+ .addIdentifier()
+ .setType(typeConcept)
+ .setSystem("https://fhir.example.com/identifiers/mrn")
+ .setValue("urn:id01")
+ .setAssigner(new Reference("Organization/001")
+ .setIdentifier(new Identifier().setValue("urn:id02")
+ .setAssigner(new Reference("Organization/002"))));
+ return condition;
+ }
+
+
public static Condition conditionWithVersion() {
final Condition condition = new Condition();
final IdType id = new IdType("Condition", "with-version", "1");
@@ -145,6 +183,7 @@ public static Condition conditionWithVersion() {
return condition;
}
+
/**
* Returns a FHIR Observation for testing purposes.
*/
diff --git a/encoders/src/test/java/au/csiro/pathling/sql/types/FlexiDecimalTest.java b/encoders/src/test/java/au/csiro/pathling/sql/types/FlexiDecimalTest.java
index 48026f7a62..ad9abda91b 100644
--- a/encoders/src/test/java/au/csiro/pathling/sql/types/FlexiDecimalTest.java
+++ b/encoders/src/test/java/au/csiro/pathling/sql/types/FlexiDecimalTest.java
@@ -23,12 +23,12 @@
package au.csiro.pathling.sql.types;
-import org.junit.jupiter.api.Test;
-import java.math.BigDecimal;
-
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
+import java.math.BigDecimal;
+import org.junit.jupiter.api.Test;
+
public class FlexiDecimalTest {
@Test
diff --git a/encoders/src/test/resources/logback-test.xml b/encoders/src/test/resources/logback.xml
similarity index 87%
rename from encoders/src/test/resources/logback-test.xml
rename to encoders/src/test/resources/logback.xml
index 13d2caaf38..76d5575d86 100644
--- a/encoders/src/test/resources/logback-test.xml
+++ b/encoders/src/test/resources/logback.xml
@@ -25,7 +25,7 @@
- %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+ [%level] %logger{36} - %msg%n
@@ -36,12 +36,14 @@
-
+
+
+
diff --git a/fhir-server/pom.xml b/fhir-server/pom.xml
index 40da33e48a..dcc3d3c554 100644
--- a/fhir-server/pom.xml
+++ b/fhir-server/pom.xml
@@ -36,8 +36,12 @@
aehrc/pathling
latest
arm64
- 1.36
- amazoncorretto:11
+ 1.37
+ amazoncorretto:17
+ file://${project.basedir}/src/test/resources/test-data
+ parquet
+ 1
+ 5
@@ -65,7 +69,7 @@
io.delta
- delta-core_${pathling.scalaVersion}
+ delta-spark_${pathling.scalaVersion}
org.apache.hadoop
@@ -75,6 +79,10 @@
com.fasterxml.jackson.core
jackson-core
+
+ javax.servlet
+ javax.servlet-api
+
@@ -125,6 +133,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-jetty
+
org.springframework.boot
spring-boot-starter-actuator
@@ -153,7 +165,7 @@
com.auth0
jwks-rsa
- 0.22.0
+ 0.22.1
com.google.code.gson
@@ -178,7 +190,7 @@
io.micrometer
micrometer-registry-prometheus
- 1.11.3
+ 1.13.0
@@ -355,7 +367,7 @@
com.google.cloud.tools
jib-maven-plugin
- 3.3.2
+ 3.4.2
${pathling.dockerBaseImage}
@@ -378,7 +390,8 @@
${project.version}
- -Xmx2g -XX:MaxMetaspaceSize=400m -XX:ReservedCodeCacheSize=240m -Xss1m -Duser.timezone=UTC
+
+ -Xmx2g -XX:MaxMetaspaceSize=400m -XX:ReservedCodeCacheSize=240m -Xss1m -Duser.timezone=UTC --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED
Copyright © 2018-2023, Commonwealth Scientific and Industrial Research Organisation (CSIRO) ABN 41 687 119 230. Licensed under the CSIRO Open Source Software Licence Agreement.
@@ -408,6 +421,13 @@
+
+
+ maven_central
+ Maven Central
+ https://repo.maven.apache.org/maven2/
+
+
@@ -422,29 +442,29 @@
run-benchmark
test
- java
+ exec
- au.csiro.pathling.test.benchmark.PathlingBenchmarkRunner
- test
- false
-
-
- spring.profiles.active
- unit-test
-
-
- pathling.storage.warehouseUrl
- file://${project.basedir}/src/test/resources/test-data
-
-
- pathling.storage.databaseName
- parquet
-
-
+ java
+ -classpath
+
+ -Xmx6g
+ -XX:MaxMetaspaceSize=400m
+ -XX:ReservedCodeCacheSize=240m
+ -Xss1m
+ -Duser.timezone=UTC
+ --add-exports=java.base/sun.nio.ch=ALL-UNNAMED
+ --add-opens=java.base/java.net=ALL-UNNAMED
+ -Dspring.profiles.active=unit-test
+ -Dpathling.storage.warehouseUrl=${pathling.benchmark.warehouseUrl}
+ -Dpathling.storage.databaseName=${pathling.benchmark.databaseName}
+ -Dbenchmark.test.iterations=${pathling.benchmark.testIterations}
+ -Dbenchmark.warmup.iterations=${pathling.benchmark.warmupIterations}
+ au.csiro.pathling.test.benchmark.PathlingBenchmarkRunner
${project.build.directory}/benchmark
+ test
diff --git a/fhir-server/src/main/java/au/csiro/pathling/aggregate/AggregateExecutor.java b/fhir-server/src/main/java/au/csiro/pathling/aggregate/AggregateExecutor.java
index a2aab3b5db..c654ca4c08 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/aggregate/AggregateExecutor.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/aggregate/AggregateExecutor.java
@@ -120,8 +120,7 @@ private Function mapRowToGrouping(
}
for (int i = 0; i < aggregations.size(); i++) {
- //noinspection rawtypes
- final Materializable aggregation = (Materializable) aggregations.get(i);
+ final Materializable aggregation = (Materializable) aggregations.get(i);
// Delegate to the `getValueFromRow` method within each Materializable path class to extract
// the Type value from the Row in the appropriate way.
final Optional result = aggregation.getFhirValueFromRow(row, i + groupings.size());
diff --git a/fhir-server/src/main/java/au/csiro/pathling/async/AsyncAspect.java b/fhir-server/src/main/java/au/csiro/pathling/async/AsyncAspect.java
index 405b559c0a..26ce948d46 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/async/AsyncAspect.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/async/AsyncAspect.java
@@ -25,6 +25,8 @@
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import jakarta.annotation.Nonnull;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays;
import java.util.List;
@@ -34,8 +36,6 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.spark.sql.SparkSession;
import org.aspectj.lang.ProceedingJoinPoint;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/async/JobProvider.java b/fhir-server/src/main/java/au/csiro/pathling/async/JobProvider.java
index 090477ca89..4c498759b1 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/async/JobProvider.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/async/JobProvider.java
@@ -33,11 +33,11 @@
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.OperationOutcome;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/caching/EntityTagInterceptor.java b/fhir-server/src/main/java/au/csiro/pathling/caching/EntityTagInterceptor.java
index c55a187fa6..cb3f7fc6ad 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/caching/EntityTagInterceptor.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/caching/EntityTagInterceptor.java
@@ -30,10 +30,10 @@
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/config/AsyncConfiguration.java b/fhir-server/src/main/java/au/csiro/pathling/config/AsyncConfiguration.java
index 88be9077ea..1a610d3cd3 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/config/AsyncConfiguration.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/config/AsyncConfiguration.java
@@ -17,9 +17,9 @@
package au.csiro.pathling.config;
-import javax.validation.constraints.NotNull;
-import lombok.Data;
+import jakarta.validation.constraints.NotNull;
import java.util.List;
+import lombok.Data;
/**
* Represents configuration relating to asynchronous processing.
@@ -32,7 +32,7 @@ public class AsyncConfiguration {
*/
@NotNull
private boolean enabled;
-
+
/**
* List of headers from exclude from the {@link HttpServerCachingConfiguration#getVary()} list for
* the purpose of server side caching of asynchronous requests. These are likely to include
diff --git a/fhir-server/src/main/java/au/csiro/pathling/config/AuthorizationConfiguration.java b/fhir-server/src/main/java/au/csiro/pathling/config/AuthorizationConfiguration.java
index df47604062..1504e74065 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/config/AuthorizationConfiguration.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/config/AuthorizationConfiguration.java
@@ -19,9 +19,9 @@
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;
-import javax.validation.constraints.NotNull;
import lombok.Data;
import lombok.ToString;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/config/CorsConfiguration.java b/fhir-server/src/main/java/au/csiro/pathling/config/CorsConfiguration.java
index e44f0ebf4f..a89c016852 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/config/CorsConfiguration.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/config/CorsConfiguration.java
@@ -17,9 +17,9 @@
package au.csiro.pathling.config;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
import java.util.List;
-import javax.validation.constraints.Min;
-import javax.validation.constraints.NotNull;
import lombok.Data;
/**
diff --git a/fhir-server/src/main/java/au/csiro/pathling/config/HttpServerCachingConfiguration.java b/fhir-server/src/main/java/au/csiro/pathling/config/HttpServerCachingConfiguration.java
index 22bf908400..411406dcf5 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/config/HttpServerCachingConfiguration.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/config/HttpServerCachingConfiguration.java
@@ -17,8 +17,8 @@
package au.csiro.pathling.config;
+import jakarta.validation.constraints.NotNull;
import java.util.List;
-import javax.validation.constraints.NotNull;
import lombok.Data;
@Data
diff --git a/fhir-server/src/main/java/au/csiro/pathling/config/ImportConfiguration.java b/fhir-server/src/main/java/au/csiro/pathling/config/ImportConfiguration.java
index 259c69def4..e534d85fcc 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/config/ImportConfiguration.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/config/ImportConfiguration.java
@@ -17,8 +17,8 @@
package au.csiro.pathling.config;
+import jakarta.validation.constraints.NotNull;
import java.util.List;
-import javax.validation.constraints.NotNull;
import lombok.Data;
/**
diff --git a/fhir-server/src/main/java/au/csiro/pathling/config/ServerConfiguration.java b/fhir-server/src/main/java/au/csiro/pathling/config/ServerConfiguration.java
index 5cfdfb5cde..6c078b69fd 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/config/ServerConfiguration.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/config/ServerConfiguration.java
@@ -19,8 +19,8 @@
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
import java.util.Optional;
-import javax.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/config/SparkConfiguration.java b/fhir-server/src/main/java/au/csiro/pathling/config/SparkConfiguration.java
index 5511ec2caa..ae47090af9 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/config/SparkConfiguration.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/config/SparkConfiguration.java
@@ -17,8 +17,7 @@
package au.csiro.pathling.config;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotBlank;
import lombok.Builder;
import lombok.Data;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/errors/DiagnosticContext.java b/fhir-server/src/main/java/au/csiro/pathling/errors/DiagnosticContext.java
index 5f27e79b90..44a2ea224f 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/errors/DiagnosticContext.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/errors/DiagnosticContext.java
@@ -25,8 +25,12 @@
import io.sentry.protocol.Request;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.MDC;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/errors/ErrorHandlingInterceptor.java b/fhir-server/src/main/java/au/csiro/pathling/errors/ErrorHandlingInterceptor.java
index e024799627..0d1556db0c 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/errors/ErrorHandlingInterceptor.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/errors/ErrorHandlingInterceptor.java
@@ -34,10 +34,10 @@
import com.google.common.util.concurrent.UncheckedExecutionException;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import org.apache.spark.SparkException;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/errors/ErrorReportingInterceptor.java b/fhir-server/src/main/java/au/csiro/pathling/errors/ErrorReportingInterceptor.java
index 6abf3d1860..30e85ebcba 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/errors/ErrorReportingInterceptor.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/errors/ErrorReportingInterceptor.java
@@ -26,8 +26,8 @@
import io.sentry.Sentry;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/extract/ResultProvider.java b/fhir-server/src/main/java/au/csiro/pathling/extract/ResultProvider.java
index cee45882a4..4ab9d2cfcf 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/extract/ResultProvider.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/extract/ResultProvider.java
@@ -29,11 +29,11 @@
import ca.uhn.fhir.rest.api.server.RequestDetails;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.context.annotation.Profile;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/fhir/ConformanceProvider.java b/fhir-server/src/main/java/au/csiro/pathling/fhir/ConformanceProvider.java
index 5928000462..1c5c81b031 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/fhir/ConformanceProvider.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/fhir/ConformanceProvider.java
@@ -41,6 +41,8 @@
import com.google.common.collect.ImmutableMap;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
@@ -49,6 +51,8 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/fhir/FhirServer.java b/fhir-server/src/main/java/au/csiro/pathling/fhir/FhirServer.java
index 8cc5b948d6..db4e6eb041 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/fhir/FhirServer.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/fhir/FhirServer.java
@@ -39,6 +39,9 @@
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import jakarta.annotation.Nonnull;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServletResponse;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
@@ -46,9 +49,6 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import javax.servlet.ServletException;
-import javax.servlet.annotation.WebServlet;
-import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Enumerations;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/fhir/SmartConfigurationInterceptor.java b/fhir-server/src/main/java/au/csiro/pathling/fhir/SmartConfigurationInterceptor.java
index 2812560ee7..a134497f4e 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/fhir/SmartConfigurationInterceptor.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/fhir/SmartConfigurationInterceptor.java
@@ -30,12 +30,12 @@
import com.google.gson.GsonBuilder;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/search/SearchExecutor.java b/fhir-server/src/main/java/au/csiro/pathling/search/SearchExecutor.java
index 00a6038a38..a3ac80de3e 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/search/SearchExecutor.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/search/SearchExecutor.java
@@ -35,6 +35,8 @@
import ca.uhn.fhir.rest.param.StringParam;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
import java.util.Date;
import java.util.List;
import java.util.Optional;
diff --git a/fhir-server/src/main/java/au/csiro/pathling/security/OidcConfiguration.java b/fhir-server/src/main/java/au/csiro/pathling/security/OidcConfiguration.java
index 7e8349637f..0e6588aed0 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/security/OidcConfiguration.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/security/OidcConfiguration.java
@@ -69,6 +69,15 @@ public OidcConfiguration(@Nonnull final String issuer) {
oidcConfiguration = getConfigurationForIssuerLocation(issuer);
}
+ /**
+ * This constructor is used for testing purposes only.
+ *
+ * @param oidcConfiguration a map of OIDC {@link ConfigItem} values
+ */
+ public OidcConfiguration(@Nonnull final Map oidcConfiguration) {
+ this.oidcConfiguration = oidcConfiguration;
+ }
+
/**
* @param item the {@link ConfigItem} to retrieve
* @return the value, if present
diff --git a/fhir-server/src/main/java/au/csiro/pathling/security/PathlingJwtDecoderBuilder.java b/fhir-server/src/main/java/au/csiro/pathling/security/PathlingJwtDecoderBuilder.java
index 911ba83a0d..41428b516c 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/security/PathlingJwtDecoderBuilder.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/security/PathlingJwtDecoderBuilder.java
@@ -53,6 +53,7 @@
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
@@ -133,10 +134,7 @@ public List extends Key> selectKeys(@Nullable final JWSHeader header,
@Nonnull
protected NimbusJwtDecoder buildDecoderWithValidators(
@Nonnull final List> validators) {
- final OAuth2TokenValidator[] validatorsArray = validators.toArray(new OAuth2TokenValidator[0]);
- @SuppressWarnings("unchecked")
- final OAuth2TokenValidator validator = new DelegatingOAuth2TokenValidator<>(
- validatorsArray);
+ final OAuth2TokenValidator validator = new DelegatingOAuth2TokenValidator<>(validators);
final NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(processor());
jwtDecoder.setJwtValidator(validator);
@@ -172,7 +170,7 @@ public Resource retrieveResource(@Nullable final URL url) throws IOException {
final HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON));
final ResponseEntity response = getResponse(url, headers);
- if (response.getStatusCodeValue() != 200) {
+ if (!HttpStatusCode.valueOf(200).equals(response.getStatusCode())) {
throw new IOException(response.toString());
}
if (response.getBody() == null) {
diff --git a/fhir-server/src/main/java/au/csiro/pathling/security/SecurityConfiguration.java b/fhir-server/src/main/java/au/csiro/pathling/security/SecurityConfiguration.java
index 92fa8573f1..d2c7404497 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/security/SecurityConfiguration.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/security/SecurityConfiguration.java
@@ -17,15 +17,22 @@
package au.csiro.pathling.security;
+import static au.csiro.pathling.utilities.Preconditions.check;
+
import au.csiro.pathling.config.ServerConfiguration;
import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
@@ -41,46 +48,73 @@
* href="https://stackoverflow.com/questions/51079564/spring-security-antmatchers-not-being-applied-on-post-requests-and-only-works-wi/51088555">Spring
* security antMatchers not being applied on POST requests and only works with GET
*/
+@Configuration
@EnableWebSecurity
@Profile("server")
@Slf4j
public class SecurityConfiguration {
+ @Nonnull
private final ServerConfiguration configuration;
+ @Nullable
+ private final JwtAuthenticationConverter authenticationConverter;
+
+ @Nullable
+ private final JwtDecoder jwtDecoder;
+
@Value("${pathling.auth.enabled}")
private boolean authEnabled;
- public SecurityConfiguration(@Nonnull final ServerConfiguration configuration) {
+ /**
+ * Constructs a new {@link SecurityConfiguration} object.
+ *
+ * @param configuration a {@link ServerConfiguration} object
+ */
+ public SecurityConfiguration(@Nonnull final ServerConfiguration configuration,
+ @Nullable final JwtAuthenticationConverter authenticationConverter,
+ @Nullable final JwtDecoder jwtDecoder) {
this.configuration = configuration;
+ this.authenticationConverter = authenticationConverter;
+ this.jwtDecoder = jwtDecoder;
}
+ /**
+ * Configures the security filter chain.
+ *
+ * @param http the {@link HttpSecurity} object
+ * @return the security filter chain
+ * @throws Exception if an error occurs
+ */
@Bean
public SecurityFilterChain securityFilterChain(@Nonnull final HttpSecurity http)
throws Exception {
- // Will use the bean of class CorsConfigurationSource as configuration provider.
- http.cors();
-
if (authEnabled) {
- http.authorizeRequests()
- // The following requests do not require authentication.
- .mvcMatchers(HttpMethod.GET,
- "/metadata", // Server capabilities operation
- "/OperationDefinition/**", // GET on OperationDefinition resources
- "/.well-known/**") // SMART configuration endpoint
- .permitAll()
- // Anything else needs to be authenticated.
- .anyRequest()
- .authenticated()
- .and()
- .oauth2ResourceServer()
- .jwt();
+ check(authenticationConverter != null,
+ "Authentication converter must be provided when authentication is enabled");
+ check(jwtDecoder != null, "JWT decoder must be provided when authentication is enabled");
+ http.authorizeHttpRequests(authz -> authz
+ // The following requests do not require authentication.
+ .requestMatchers(HttpMethod.GET, "/fhir/metadata").permitAll()
+ .requestMatchers(HttpMethod.GET, "/fhir/OperationDefinition/**").permitAll()
+ .requestMatchers(HttpMethod.GET, "/fhir/.well-known/**").permitAll()
+ // Anything else needs to be authenticated.
+ .anyRequest().authenticated())
+ // Enable CORS as per the configuration.
+ .cors((cors) -> cors.configurationSource(corsConfigurationSource()))
+ // Use the provided JWT decoder and authentication converter.
+ .oauth2ResourceServer(oauth2 -> oauth2
+ .jwt((jwt) -> jwt
+ .jwtAuthenticationConverter(authenticationConverter)
+ .decoder(jwtDecoder)
+ ));
} else {
- http
+ http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll())
+ // Enable CORS as per the configuration.
+ .cors((cors) -> cors.configurationSource(corsConfigurationSource()))
// Without this POST requests fail with 403 Forbidden.
- .csrf().disable()
- .authorizeRequests().anyRequest().permitAll();
+ .csrf(AbstractHttpConfigurer::disable);
}
return http.build();
@@ -91,7 +125,6 @@ public SecurityFilterChain securityFilterChain(@Nonnull final HttpSecurity http)
*
* @return CORS configuration source
*/
- @Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedOrigins(configuration.getCors().getAllowedOrigins());
diff --git a/fhir-server/src/main/java/au/csiro/pathling/security/ga4gh/PassportAuthenticationConverter.java b/fhir-server/src/main/java/au/csiro/pathling/security/ga4gh/PassportAuthenticationConverter.java
index bd81283507..32896ac0c6 100644
--- a/fhir-server/src/main/java/au/csiro/pathling/security/ga4gh/PassportAuthenticationConverter.java
+++ b/fhir-server/src/main/java/au/csiro/pathling/security/ga4gh/PassportAuthenticationConverter.java
@@ -32,7 +32,6 @@
import java.util.Collection;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
-import org.apache.hadoop.shaded.com.nimbusds.jose.shaded.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
@@ -42,6 +41,7 @@
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.hl7.fhir.r4.model.Enumerations.ResourceType;
+import org.json.JSONObject;
import org.springframework.context.annotation.Profile;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.core.GrantedAuthority;
diff --git a/fhir-server/src/main/resources/application.yml b/fhir-server/src/main/resources/application.yml
index 566e63490d..407933b126 100644
--- a/fhir-server/src/main/resources/application.yml
+++ b/fhir-server/src/main/resources/application.yml
@@ -133,7 +133,7 @@ pathling:
# This section configures the server's support asynchronous processing of HTTP requests.
async:
# Enables asynchronous processing for those operations that support it,
- # when explicitly requested by a HTTP client.
+ # when explicitly requested by an HTTP client.
enabled: true
# A subset of `pathling.httpCaching.vary` HTTP headers, which should
@@ -160,7 +160,7 @@ spark:
delta:
schema:
autoMerge:
- enabled: true
+ enabled: false
scheduler:
mode: FAIR
diff --git a/library-api/src/main/resources/logback.xml b/fhir-server/src/main/resources/logback.xml
similarity index 93%
rename from library-api/src/main/resources/logback.xml
rename to fhir-server/src/main/resources/logback.xml
index cfd4dbdf57..273f9c4afc 100644
--- a/library-api/src/main/resources/logback.xml
+++ b/fhir-server/src/main/resources/logback.xml
@@ -24,6 +24,7 @@
+
diff --git a/fhir-server/src/test/java/au/csiro/pathling/aggregate/DrillDownBuilderTest.java b/fhir-server/src/test/java/au/csiro/pathling/aggregate/DrillDownBuilderTest.java
index 7dfe02b07d..0832daa4b2 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/aggregate/DrillDownBuilderTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/aggregate/DrillDownBuilderTest.java
@@ -21,6 +21,7 @@
import au.csiro.pathling.test.SpringBootUnitTest;
import au.csiro.pathling.test.helpers.TestHelpers;
import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import java.util.stream.Stream;
import lombok.Value;
import org.apache.spark.sql.SparkSession;
diff --git a/fhir-server/src/test/java/au/csiro/pathling/async/JobRegistryTest.java b/fhir-server/src/test/java/au/csiro/pathling/async/JobRegistryTest.java
index 381eef77a8..aa9991a013 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/async/JobRegistryTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/async/JobRegistryTest.java
@@ -30,7 +30,7 @@
class JobRegistryTest {
- private final static Future MOCK_FUTURE = mock(Future.class);
+ private final static Future MOCK_FUTURE = mock(FutureResource.class);
private final static JobTag JOB_TAG_1 = new JobTag() {
};
private final static JobTag JOB_TAG_2 = new JobTag() {
@@ -49,7 +49,6 @@ void testNewJobCanBeRetrievedById() {
@Test
void testReusesJobWhenTagsAreIdentical() {
- assertEquals(JOB_TAG_1, JOB_TAG_1);
final Job firstJob = registry.getOrCreate(JOB_TAG_1,
id -> new Job(id, "operation", MOCK_FUTURE, Optional.empty()));
final Job otherJob = registry.getOrCreate(JOB_TAG_1,
@@ -73,4 +72,8 @@ void testCreatesNewJobIfTagsDiffer() {
assertNotEquals(firstJob, otherJob);
assertNotEquals(firstJob.getId(), otherJob.getId());
}
+
+ static interface FutureResource extends Future {
+ }
+
}
diff --git a/fhir-server/src/test/java/au/csiro/pathling/fhir/EntityTagInterceptorTest.java b/fhir-server/src/test/java/au/csiro/pathling/fhir/EntityTagInterceptorTest.java
index 6e11170ee7..88a41527a5 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/fhir/EntityTagInterceptorTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/fhir/EntityTagInterceptorTest.java
@@ -33,10 +33,10 @@
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
diff --git a/fhir-server/src/test/java/au/csiro/pathling/fhirpath/parser/ParserTest.java b/fhir-server/src/test/java/au/csiro/pathling/fhirpath/parser/ParserTest.java
index 0bbbc42bdf..208948c8b9 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/fhirpath/parser/ParserTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/fhirpath/parser/ParserTest.java
@@ -83,9 +83,9 @@ private void setupMockDisplayFor_195662009_444814009() {
private void setupMockPropertiesFor_195662009_444814009() {
TerminologyServiceHelpers.setupLookup(terminologyService)
- .withProperty(CD_SNOMED_195662009, "child", (String) null, CD_SNOMED_40055000,
- CD_SNOMED_403190006)
- .withProperty(CD_SNOMED_444814009, "child", (String) null, CD_SNOMED_284551006)
+ .withProperty(CD_SNOMED_195662009, "child", null, List.of(CD_SNOMED_40055000,
+ CD_SNOMED_403190006))
+ .withProperty(CD_SNOMED_444814009, "child", null, List.of(CD_SNOMED_284551006))
.withDesignation(CD_SNOMED_195662009, CD_SNOMED_900000000000003001, "en",
"Acute viral pharyngitis : disorder")
.withDesignation(CD_SNOMED_444814009, CD_SNOMED_900000000000003001, "en",
@@ -905,14 +905,6 @@ void testQuantityAdditionWithOverflow() {
.hasRows(spark, "responses/ParserTest/testQuantityAdditionWithOverflow_code.csv");
}
- @Test
- void testTraversalToUnsupportedReferenceChild() {
- final String expression = "reverseResolve(MedicationRequest.subject).requester.identifier";
- final InvalidUserInputError error = assertThrows(InvalidUserInputError.class,
- expression);
- assertEquals("No such child: " + expression, error.getMessage());
- }
-
@Test
void testResolutionOfExtensionReference() {
mockResource(ResourceType.PATIENT, ResourceType.ENCOUNTER, ResourceType.GOAL);
diff --git a/fhir-server/src/test/java/au/csiro/pathling/jmh/AbstractJmhSpringBootState.java b/fhir-server/src/test/java/au/csiro/pathling/jmh/AbstractJmhSpringBootState.java
index 7c96b4ef25..7f937e8496 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/jmh/AbstractJmhSpringBootState.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/jmh/AbstractJmhSpringBootState.java
@@ -19,9 +19,10 @@
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.ActiveProfiles;
-import org.openjdk.jmh.annotations.State;
/**
@@ -36,7 +37,7 @@
* #wireUp()} comes up first which is now achieved by placing this class in 'au.csiro.pathling.jmh'
* package and all the classes that use it in 'au.csiro.pathling.test.benchmark'.
*/
-@SpringBootTest
+@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public abstract class AbstractJmhSpringBootState {
@Setup(Level.Trial)
diff --git a/fhir-server/src/test/java/au/csiro/pathling/jmh/SpringBootJmhContext.java b/fhir-server/src/test/java/au/csiro/pathling/jmh/SpringBootJmhContext.java
index 9fc0836359..323b9a73fa 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/jmh/SpringBootJmhContext.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/jmh/SpringBootJmhContext.java
@@ -21,6 +21,8 @@
import java.util.HashMap;
import java.util.Map;
import org.springframework.test.context.TestContextManager;
+import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
+import org.springframework.test.context.TestContextManager;
/**
* This class hijacks the mechanism that SpringBoot uses for auto wiring of SpringBootTests.
@@ -52,4 +54,17 @@ public static void autowireWithTestContext(@Nonnull final Object testLikeObject)
throws Exception {
getOrCreate(testLikeObject.getClass()).prepareTestInstance(testLikeObject);
}
+
+
+ /**
+ * Cleans up the contexts of all the classes that have been autowired using this class.
+ * This also destroys and clean up all the test application contexts created thus far.
+ */
+ public static void cleanUpAll() {
+ synchronized (contextManagers) {
+ contextManagers.forEach((k, v) -> v.getTestContext().markApplicationContextDirty(
+ HierarchyMode.EXHAUSTIVE));
+ contextManagers.clear();
+ }
+ }
}
diff --git a/fhir-server/src/test/java/au/csiro/pathling/security/SecurityEnabledOperationsTest.java b/fhir-server/src/test/java/au/csiro/pathling/security/SecurityEnabledOperationsTest.java
index 3cb0f009a0..687beb2ab2 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/security/SecurityEnabledOperationsTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/security/SecurityEnabledOperationsTest.java
@@ -32,7 +32,8 @@
* href="https://stackoverflow.com/questions/58289509/in-spring-boot-test-how-do-i-map-a-temporary-folder-to-a-configuration-property">In
* Spring Boot Test, how do I map a temporary folder to a configuration property?
*/
-@TestPropertySource(properties = {"pathling.auth.enabled=true"})
+@TestPropertySource(properties = {"pathling.auth.enabled=true",
+ "pathling.auth.issuer=https://pathling.acme.com/fhir"})
@MockBean(OidcConfiguration.class)
@MockBean(JwtDecoder.class)
@MockBean(JwtAuthenticationConverter.class)
diff --git a/fhir-server/src/test/java/au/csiro/pathling/security/SecurityTest.java b/fhir-server/src/test/java/au/csiro/pathling/security/SecurityTest.java
index 10b39af6dc..d9569675cc 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/security/SecurityTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/security/SecurityTest.java
@@ -25,9 +25,10 @@
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.function.Executable;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
@Tag("UnitTest")
-@SpringBootTest
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
abstract class SecurityTest {
static void assertThrowsAccessDenied(@Nonnull final Executable executable,
diff --git a/fhir-server/src/test/java/au/csiro/pathling/security/ga4gh/ManifestConverterTest.java b/fhir-server/src/test/java/au/csiro/pathling/security/ga4gh/ManifestConverterTest.java
index 3c2d4edae3..3530d68eec 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/security/ga4gh/ManifestConverterTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/security/ga4gh/ManifestConverterTest.java
@@ -41,6 +41,7 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.context.DynamicPropertyRegistry;
@@ -52,7 +53,7 @@
"pathling.storage.databaseName=parquet"})
@Tag("Tranche2")
@Slf4j
-@SpringBootTest
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class ManifestConverterTest extends AbstractParserTest {
@Autowired
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/IntegrationTestDependencies.java b/fhir-server/src/test/java/au/csiro/pathling/test/IntegrationTestDependencies.java
index 7366af1463..0191551564 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/IntegrationTestDependencies.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/IntegrationTestDependencies.java
@@ -18,14 +18,21 @@
package au.csiro.pathling.test;
import au.csiro.pathling.config.ServerConfiguration;
+import au.csiro.pathling.security.OidcConfiguration;
+import au.csiro.pathling.security.OidcConfiguration.ConfigItem;
import au.csiro.pathling.terminology.DefaultTerminologyServiceFactory;
import ca.uhn.fhir.context.FhirContext;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import jakarta.annotation.Nonnull;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@@ -56,4 +63,10 @@ public DefaultTerminologyServiceFactory terminologyServiceFactory(
return new DefaultTerminologyServiceFactory(fhirContext.getVersion().getVersion(),
configuration.getTerminology());
}
+
+ @Bean
+ public RestTemplateBuilder restTemplateBuilder() {
+ return new RestTemplateBuilder().setReadTimeout(Duration.ofSeconds(60));
+ }
+
}
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/TestDataImporter.java b/fhir-server/src/test/java/au/csiro/pathling/test/TestDataImporter.java
index 356bbc8fc8..22389af2e5 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/TestDataImporter.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/TestDataImporter.java
@@ -76,7 +76,7 @@ public void run(final String... args) {
final String sourcePath = args[0];
final File srcNdJsonDir = new File(sourcePath);
- final FileFilter fileFilter = new WildcardFileFilter("*.ndjson");
+ final FileFilter fileFilter = WildcardFileFilter.builder().setWildcards("*.ndjson").get();
final File[] srcNdJsonFiles = srcNdJsonDir.listFiles(fileFilter);
final List sources = Stream.of(requireNonNull(srcNdJsonFiles))
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/benchmark/AggregateBenchmark.java b/fhir-server/src/test/java/au/csiro/pathling/test/benchmark/AggregateBenchmark.java
index 8d1228e577..4d7c58aee8 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/benchmark/AggregateBenchmark.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/benchmark/AggregateBenchmark.java
@@ -98,7 +98,7 @@ public void setUp() {
SharedMocks.resetAll();
mockResource(ResourceType.PATIENT, ResourceType.CONDITION, ResourceType.ENCOUNTER,
ResourceType.PROCEDURE, ResourceType.MEDICATIONREQUEST, ResourceType.OBSERVATION,
- ResourceType.DIAGNOSTICREPORT, ResourceType.ORGANIZATION, ResourceType.QUESTIONNAIRE,
+ ResourceType.DIAGNOSTICREPORT, ResourceType.ORGANIZATION,
ResourceType.CAREPLAN);
executor = new AggregateExecutor(configuration, fhirContext, spark, database,
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/benchmark/PathlingBenchmarkRunner.java b/fhir-server/src/test/java/au/csiro/pathling/test/benchmark/PathlingBenchmarkRunner.java
index d83df4ef93..6ab38e0ff4 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/benchmark/PathlingBenchmarkRunner.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/benchmark/PathlingBenchmarkRunner.java
@@ -21,6 +21,7 @@
import java.io.File;
import java.util.Properties;
+import au.csiro.pathling.jmh.SpringBootJmhContext;
import lombok.extern.slf4j.Slf4j;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
@@ -42,7 +43,7 @@ public static void main(final String... argc) throws Exception {
? argc[0]
: "target/benchmark";
- final Properties properties = PropertiesLoaderUtils.loadAllProperties("benchmark.properties");
+ final Properties properties = System.getProperties();
final int warmup = Integer
.parseInt(properties.getProperty("benchmark.warmup.iterations", "1"));
@@ -79,7 +80,12 @@ public static void main(final String... argc) throws Exception {
.shouldFailOnError(true)
.jvmArgs("-server -Xmx4g -ea -Duser.timezone=UTC")
.build();
- new Runner(opt).run();
+ try {
+ new Runner(opt).run();
+ } finally {
+ // clean up the test application contexts created by the benchmarks
+ SpringBootJmhContext.cleanUpAll();
+ }
}
}
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/integration/AsyncTest.java b/fhir-server/src/test/java/au/csiro/pathling/test/integration/AsyncTest.java
index a8fe7c9efa..35b36e2f2d 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/integration/AsyncTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/integration/AsyncTest.java
@@ -35,12 +35,13 @@
import org.hl7.fhir.r4.model.Enumerations.ResourceType;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
-import org.sparkproject.jetty.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpStatusCode;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
@@ -78,9 +79,9 @@ void asyncExtract() throws URISyntaxException, MalformedURLException, Interrupte
log.info("Sending kick-off request");
final ResponseEntity response =
restTemplate.exchange(uri, HttpMethod.GET, request, String.class);
- assertEquals(HttpStatus.ACCEPTED_202, response.getStatusCode().value());
+ assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
- assertAsyncResponse(response, HttpStatus.OK_200, true);
+ assertAsyncResponse(response, HttpStatus.OK, true);
}
@Test
@@ -103,7 +104,7 @@ void error() throws URISyntaxException, MalformedURLException, InterruptedExcept
.exchange(uri, HttpMethod.GET, request, String.class);
assertTrue(response.getStatusCode().is2xxSuccessful());
- assertAsyncResponse(response, HttpStatus.BAD_REQUEST_400, false);
+ assertAsyncResponse(response, HttpStatus.BAD_REQUEST, false);
}
@Test
@@ -111,13 +112,13 @@ void nonExistentJob() throws URISyntaxException {
final String uri = "http://localhost:" + port + "/fhir/job?id=foo";
final ResponseEntity response = restTemplate
.exchange(uri, HttpMethod.GET, RequestEntity.get(new URI(uri)).build(), String.class);
- assertEquals(HttpStatus.NOT_FOUND_404, response.getStatusCode().value());
+ assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
}
void assertAsyncResponse(@Nonnull final ResponseEntity response,
- final int expectedStatus, final boolean inProgressRequired)
+ final HttpStatusCode expectedStatus, final boolean inProgressRequired)
throws MalformedURLException, URISyntaxException, InterruptedException {
- int statusCode;
+ HttpStatusCode statusCode;
boolean encounteredInProgressResponse = false;
final long startTime = new Date().getTime();
do {
@@ -132,13 +133,13 @@ void assertAsyncResponse(@Nonnull final ResponseEntity response,
final ResponseEntity statusResponse =
restTemplate.exchange(statusUrl.toURI(), HttpMethod.GET, statusRequest, String.class);
- statusCode = statusResponse.getStatusCodeValue();
+ statusCode = statusResponse.getStatusCode();
final List eTag = statusResponse.getHeaders().get("ETag");
final List cacheControl = statusResponse.getHeaders().get("Cache-Control");
assertNotNull(eTag);
assertNotNull(cacheControl);
- if (statusCode != expectedStatus) {
- assertEquals(HttpStatus.ACCEPTED_202, statusCode);
+ if (!expectedStatus.equals(statusCode)) {
+ assertEquals(HttpStatus.ACCEPTED, statusCode);
assertTrue(eTag.contains("W/\"0\""));
assertTrue(cacheControl.contains("no-store"));
encounteredInProgressResponse = true;
@@ -158,7 +159,7 @@ void assertAsyncResponse(@Nonnull final ResponseEntity response,
}
@Nonnull
- private String getContentLocation(@Nonnull ResponseEntity response) {
+ private String getContentLocation(@Nonnull final ResponseEntity response) {
final List contentLocations = response.getHeaders().get("Content-Location");
assertNotNull(contentLocations);
final String contentLocation = contentLocations.get(0);
@@ -178,11 +179,11 @@ void identicalAsyncRequestsReturnTheSameJobId()
log.info("Sending kick-off request");
final ResponseEntity response1 =
restTemplate.exchange(uri, HttpMethod.GET, request, String.class);
- assertEquals(HttpStatus.ACCEPTED_202, response1.getStatusCode().value());
+ assertEquals(HttpStatus.ACCEPTED, response1.getStatusCode());
final ResponseEntity response2 =
restTemplate.exchange(uri, HttpMethod.GET, request, String.class);
- assertEquals(HttpStatus.ACCEPTED_202, response1.getStatusCode().value());
+ assertEquals(HttpStatus.ACCEPTED, response1.getStatusCode());
assertEquals(getContentLocation(response1), getContentLocation(response2));
}
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/integration/AuthorizationConfigurationTest.java b/fhir-server/src/test/java/au/csiro/pathling/test/integration/AuthorizationConfigurationTest.java
index 976e001bbd..c35e119b36 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/integration/AuthorizationConfigurationTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/integration/AuthorizationConfigurationTest.java
@@ -20,7 +20,6 @@
import static au.csiro.pathling.test.TestResources.assertJson;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.when;
import au.csiro.pathling.security.OidcConfiguration;
import au.csiro.pathling.security.OidcConfiguration.ConfigItem;
@@ -29,19 +28,22 @@
import com.google.gson.GsonBuilder;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
-import java.util.Optional;
+import java.util.Map;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONException;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -74,30 +76,11 @@ class AuthorizationConfigurationTest extends IntegrationTest {
TestRestTemplate restTemplate;
@MockBean
- OidcConfiguration oidcConfiguration;
-
- @MockBean
- @SuppressWarnings("unused")
JwtDecoder jwtDecoder;
@MockBean
- @SuppressWarnings("unused")
JwtAuthenticationConverter jwtAuthenticationConverter;
-
- @BeforeEach
- void setUp() {
- when(oidcConfiguration.get(ConfigItem.AUTH_URL)).thenReturn(
- Optional
- .of("https://auth.ontoserver.csiro.au/auth/realms/aehrc/protocol/openid-connect/auth"));
- when(oidcConfiguration.get(ConfigItem.TOKEN_URL)).thenReturn(
- Optional
- .of("https://auth.ontoserver.csiro.au/auth/realms/aehrc/protocol/openid-connect/token"));
- when(oidcConfiguration.get(ConfigItem.REVOKE_URL)).thenReturn(
- Optional
- .of("https://auth.ontoserver.csiro.au/auth/realms/aehrc/protocol/openid-connect/revoke"));
- }
-
@Test
void capabilityStatement() {
final String response = restTemplate
@@ -178,4 +161,22 @@ static class SmartConfiguration {
}
+ @TestConfiguration
+ public static class AuthorizationConfigurationTestDependencies {
+
+ @Bean
+ @Primary
+ OidcConfiguration oidcConfiguration() {
+ final Map oidcConfiguration = new HashMap<>();
+ oidcConfiguration.put(ConfigItem.AUTH_URL.getKey(),
+ "https://auth.ontoserver.csiro.au/auth/realms/aehrc/protocol/openid-connect/auth");
+ oidcConfiguration.put(ConfigItem.TOKEN_URL.getKey(),
+ "https://auth.ontoserver.csiro.au/auth/realms/aehrc/protocol/openid-connect/token");
+ oidcConfiguration.put(ConfigItem.REVOKE_URL.getKey(),
+ "https://auth.ontoserver.csiro.au/auth/realms/aehrc/protocol/openid-connect/revoke");
+ return new OidcConfiguration(oidcConfiguration);
+ }
+
+ }
+
}
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/integration/CapabilityStatementTest.java b/fhir-server/src/test/java/au/csiro/pathling/test/integration/CapabilityStatementTest.java
index eecd0f23c6..551e4c8b88 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/integration/CapabilityStatementTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/integration/CapabilityStatementTest.java
@@ -18,7 +18,10 @@
package au.csiro.pathling.test.integration;
import static au.csiro.pathling.test.TestResources.assertJson;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.util.Arrays;
+import java.util.Collections;
import org.json.JSONException;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@@ -29,11 +32,19 @@
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.TestPropertySource;
/**
* @author John Grimes
*/
+@TestPropertySource(properties = {
+ "pathling.cors.maxAge=800",
+ "pathling.cors.allowedMethods=GET,POST",
+ "pathling.cors.allowedOrigins=http://foo.bar,http://boo.bar",
+ "pathling.cors.allowedHeaders=X-Mine,X-Other"
+})
@Tag("Tranche2")
class CapabilityStatementTest extends IntegrationTest {
@@ -56,12 +67,20 @@ void cors() throws JSONException {
final HttpHeaders corsHeaders = new HttpHeaders();
corsHeaders.setOrigin("http://foo.bar");
corsHeaders.setAccessControlRequestMethod(HttpMethod.GET);
+ corsHeaders.setAccessControlRequestHeaders(Arrays.asList("X-Mine", "X-Skip"));
final ResponseEntity response = restTemplate.exchange(
"http://localhost:" + port + "/fhir/metadata", HttpMethod.OPTIONS,
new HttpEntity(corsHeaders), String.class);
- System.out.println(response);
+ final HttpHeaders responseHeaders = response.getHeaders();
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertEquals("http://foo.bar", responseHeaders.getAccessControlAllowOrigin());
+ assertEquals(Arrays.asList(HttpMethod.GET, HttpMethod.POST),
+ responseHeaders.getAccessControlAllowMethods());
+ assertEquals(800L, responseHeaders.getAccessControlMaxAge());
+ assertEquals(Collections.singletonList("X-Mine"),
+ responseHeaders.getAccessControlAllowHeaders());
}
}
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/integration/OperationDefinitionTest.java b/fhir-server/src/test/java/au/csiro/pathling/test/integration/OperationDefinitionTest.java
index f7784de370..9d2e81ca65 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/integration/OperationDefinitionTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/integration/OperationDefinitionTest.java
@@ -49,7 +49,7 @@ class OperationDefinitionTest extends IntegrationTest {
private static final List OPERATIONS = List.of("aggregate", "search", "extract", "import",
"result", "job");
- private static final String SUFFIX = "6";
+ private static final String SUFFIX = "7";
@Test
void operationDefinitions() throws MalformedURLException, URISyntaxException {
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/integration/SentryTest.java b/fhir-server/src/test/java/au/csiro/pathling/test/integration/SentryTest.java
index a9953962de..446e8508c8 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/integration/SentryTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/integration/SentryTest.java
@@ -38,7 +38,7 @@
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpStatusCode;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
@@ -72,7 +72,7 @@ void reportsToSentry() throws URISyntaxException, InterruptedException {
null);
final ResponseEntity response = restTemplate
.exchange(uri, HttpMethod.GET, RequestEntity.get(uri).build(), String.class);
- final HttpStatus statusCode = response.getStatusCode();
+ final HttpStatusCode statusCode = response.getStatusCode();
assertTrue(statusCode.is5xxServerError());
// Give the asynchronous request sender within Sentry time to actually send the error report.
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/integration/TerminologyServiceWithLanguageIntegrationTest.java b/fhir-server/src/test/java/au/csiro/pathling/test/integration/TerminologyServiceWithLanguageIntegrationTest.java
index 1d637f7679..6169cbf145 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/integration/TerminologyServiceWithLanguageIntegrationTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/integration/TerminologyServiceWithLanguageIntegrationTest.java
@@ -19,13 +19,11 @@
import static au.csiro.pathling.test.helpers.TerminologyHelpers.LC_29463_7;
import static au.csiro.pathling.test.helpers.TerminologyHelpers.LC_55915_3;
-import static com.github.tomakehurst.wiremock.client.WireMock.containing;
import static com.github.tomakehurst.wiremock.client.WireMock.proxyAllTo;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static org.junit.jupiter.api.Assertions.assertEquals;
import au.csiro.pathling.io.CacheableDatabase;
-import au.csiro.pathling.terminology.DefaultTerminologyService;
import au.csiro.pathling.terminology.DefaultTerminologyServiceFactory;
import au.csiro.pathling.terminology.TerminologyService;
import au.csiro.pathling.terminology.TerminologyService.Property;
@@ -42,7 +40,6 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/integration/modification/ImportTest.java b/fhir-server/src/test/java/au/csiro/pathling/test/integration/modification/ImportTest.java
index 975749dc68..90d39f3918 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/integration/modification/ImportTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/integration/modification/ImportTest.java
@@ -19,8 +19,8 @@
import static au.csiro.pathling.test.TestResources.getResourceAsUrl;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import au.csiro.pathling.errors.ErrorHandlingInterceptor;
import au.csiro.pathling.errors.InvalidUserInputError;
@@ -203,7 +203,7 @@ void throwsOnMissingId() {
() -> importExecutor.execute(buildImportParameters(jsonURL, ResourceType.PATIENT)));
final BaseServerResponseException convertedError =
ErrorHandlingInterceptor.convertError(error);
- assertTrue(convertedError instanceof InvalidRequestException);
+ assertInstanceOf(InvalidRequestException.class, convertedError);
assertEquals("Encountered a resource with no ID", convertedError.getMessage());
}
diff --git a/fhir-server/src/test/java/au/csiro/pathling/test/system/DockerImageTest.java b/fhir-server/src/test/java/au/csiro/pathling/test/system/DockerImageTest.java
index f5a6430440..768d2e1bc9 100644
--- a/fhir-server/src/test/java/au/csiro/pathling/test/system/DockerImageTest.java
+++ b/fhir-server/src/test/java/au/csiro/pathling/test/system/DockerImageTest.java
@@ -153,7 +153,7 @@ static File[] getResourceFolderFiles(
final URL url = loader.getResource(folder);
assertThat(url).isNotNull();
final String path = url.getPath();
- final FileFilter fileFilter = new WildcardFileFilter("*.ndjson");
+ final FileFilter fileFilter = WildcardFileFilter.builder().setWildcards("*.ndjson").get();
@Nullable final File[] files = new File(path).listFiles(fileFilter);
assertNotNull(files);
return files;
diff --git a/fhir-server/src/test/resources/application-unit-test.yml b/fhir-server/src/test/resources/application-unit-test.yml
new file mode 100644
index 0000000000..0638655e6b
--- /dev/null
+++ b/fhir-server/src/test/resources/application-unit-test.yml
@@ -0,0 +1,7 @@
+spark:
+ ui:
+ enabled: false
+
+spring:
+ main:
+ banner-mode: off
diff --git a/fhir-server/src/test/resources/logback-test.xml b/fhir-server/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..58d982a435
--- /dev/null
+++ b/fhir-server/src/test/resources/logback-test.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ [%level] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fhir-server/src/test/resources/responses/CapabilityStatementTest/capabilityStatement.CapabilityStatement.json b/fhir-server/src/test/resources/responses/CapabilityStatementTest/capabilityStatement.CapabilityStatement.json
index 1880c7b2d1..ca88baa797 100644
--- a/fhir-server/src/test/resources/responses/CapabilityStatementTest/capabilityStatement.CapabilityStatement.json
+++ b/fhir-server/src/test/resources/responses/CapabilityStatementTest/capabilityStatement.CapabilityStatement.json
@@ -1,6 +1,6 @@
{
"resourceType": "CapabilityStatement",
- "url": "https://pathling.csiro.au/fhir/CapabilityStatement/pathling-fhir-api-6",
+ "url": "https://pathling.csiro.au/fhir/CapabilityStatement/pathling-fhir-api-7",
"name": "pathling-fhir-api",
"title": "Pathling FHIR API",
"status": "active",
@@ -40,15 +40,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -69,15 +69,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -98,15 +98,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -127,15 +127,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -156,15 +156,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -185,15 +185,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -214,15 +214,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -243,15 +243,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -272,15 +272,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -301,15 +301,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -330,15 +330,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -359,15 +359,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -388,15 +388,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -417,15 +417,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -446,15 +446,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -475,15 +475,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -504,15 +504,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -533,15 +533,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -562,15 +562,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -591,15 +591,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -620,15 +620,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -649,15 +649,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -678,15 +678,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -707,15 +707,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -736,15 +736,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -765,15 +765,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -794,15 +794,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -823,15 +823,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -852,15 +852,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -881,15 +881,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -910,15 +910,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -939,15 +939,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -968,15 +968,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -997,15 +997,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1026,15 +1026,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1055,15 +1055,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1084,15 +1084,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1113,15 +1113,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1142,15 +1142,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1171,15 +1171,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1200,15 +1200,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1229,15 +1229,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1258,15 +1258,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1287,15 +1287,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1316,15 +1316,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1345,15 +1345,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1374,15 +1374,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1403,15 +1403,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1432,15 +1432,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1461,15 +1461,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1490,15 +1490,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1519,15 +1519,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1548,15 +1548,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1577,15 +1577,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1606,15 +1606,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1635,15 +1635,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1664,15 +1664,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1693,15 +1693,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1722,15 +1722,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1751,15 +1751,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1780,15 +1780,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1809,15 +1809,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1838,15 +1838,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1867,15 +1867,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1896,15 +1896,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1925,15 +1925,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1954,15 +1954,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -1983,15 +1983,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2012,15 +2012,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2041,15 +2041,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2070,15 +2070,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2099,15 +2099,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2128,15 +2128,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2157,15 +2157,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2186,15 +2186,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2215,15 +2215,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2244,15 +2244,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2273,15 +2273,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2302,15 +2302,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2331,15 +2331,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2360,15 +2360,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2389,15 +2389,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2418,15 +2418,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2447,15 +2447,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2476,15 +2476,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2505,15 +2505,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2534,15 +2534,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2563,15 +2563,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2592,15 +2592,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2621,15 +2621,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2650,15 +2650,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2679,15 +2679,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2708,15 +2708,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2737,15 +2737,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2766,15 +2766,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2795,15 +2795,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2824,15 +2824,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2853,15 +2853,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2882,15 +2882,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2911,15 +2911,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2940,15 +2940,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2969,15 +2969,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -2998,15 +2998,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3027,15 +3027,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3056,15 +3056,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3085,15 +3085,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3114,15 +3114,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3143,15 +3143,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3172,15 +3172,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3201,15 +3201,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3230,15 +3230,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3259,15 +3259,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3288,15 +3288,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3317,15 +3317,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3346,15 +3346,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3375,15 +3375,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3404,15 +3404,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3433,15 +3433,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3462,15 +3462,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3491,15 +3491,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3520,15 +3520,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3549,15 +3549,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3578,15 +3578,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3607,15 +3607,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3636,15 +3636,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3665,15 +3665,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3694,15 +3694,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3723,15 +3723,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3752,15 +3752,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3781,15 +3781,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3810,15 +3810,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3839,15 +3839,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3868,15 +3868,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3897,15 +3897,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3926,15 +3926,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3955,15 +3955,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -3984,15 +3984,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -4013,15 +4013,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -4042,15 +4042,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -4071,15 +4071,15 @@
"operation": [
{
"name": "aggregate",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/aggregate-7"
},
{
"name": "extract",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/extract-7"
},
{
"name": "fhirPath",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/search-7"
}
]
},
@@ -4101,15 +4101,15 @@
"operation": [
{
"name": "import",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/import-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/import-7"
},
{
"name": "result",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/result-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/result-7"
},
{
"name": "job",
- "definition": "https://pathling.csiro.au/fhir/OperationDefinition/job-6"
+ "definition": "https://pathling.csiro.au/fhir/OperationDefinition/job-7"
}
]
}
diff --git a/fhirpath/pom.xml b/fhirpath/pom.xml
index 9461f5f849..6c08e110ed 100644
--- a/fhirpath/pom.xml
+++ b/fhirpath/pom.xml
@@ -51,7 +51,7 @@
io.delta
- delta-core_${pathling.scalaVersion}
+ delta-spark_${pathling.scalaVersion}
provided
@@ -59,6 +59,10 @@
hadoop-aws
provided
+
+ javax.servlet
+ javax.servlet-api
+
@@ -81,6 +85,11 @@
org.hibernate.validator
hibernate-validator
+
+ jakarta.validation
+ jakarta.validation-api
+ provided
+
com.google.code.findbugs
@@ -253,51 +262,13 @@
+
+
+ **/logback.xml
+
+
-
-
-
- com.google.cloud.tools
- jib-maven-plugin
- 3.3.2
-
-
- ${pathling.dockerBaseImage}
-
-
- ${pathling.dockerArchitecture}
- linux
-
-
-
-
- ${pathling.fhirServerDockerRepo}
-
- ${pathling.fhirServerDockerTag}
- ${project.version}
- ${project.majorVersion}
- ${git.commit.id}
-
-
-
-
- ${project.version}
- -Xmx2g -XX:MaxMetaspaceSize=400m -XX:ReservedCodeCacheSize=240m -Xss1m -Duser.timezone=UTC
-
-
- Copyright © 2018-2023, Commonwealth Scientific and Industrial Research Organisation (CSIRO) ABN 41 687 119 230. Licensed under the CSIRO Open Source Software Licence Agreement.
- John Grimes <John.Grimes@csiro.au>
-
-
- 8080
- 4040
-
-
-
-
-
-
diff --git a/fhirpath/src/license/THIRD-PARTY.properties b/fhirpath/src/license/THIRD-PARTY.properties
index c855b989f6..a5dd080b04 100644
--- a/fhirpath/src/license/THIRD-PARTY.properties
+++ b/fhirpath/src/license/THIRD-PARTY.properties
@@ -10,7 +10,6 @@
# - Apache License Version 2.0
# - Apache License, 2.0
# - Apache License, Version 2.0
-# - Apache License, version 2.0
# - Apache Software License 2.0
# - Apache Software License, version 1.1
# - Apache-2.0
@@ -24,22 +23,26 @@
# - BSD licence
# - BSD-3-Clause
# - Bouncy Castle Licence
-# - CC0
+# - CDDL + GPLv2 with classpath exception
# - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
# - Dual license consisting of the CDDL v1.1 and GPL v2
# - EDL 1.0
# - EPL 2.0
+# - EPL-2.0
# - Eclipse Distribution License - v 1.0
# - Eclipse Public License - v 1.0
# - Eclipse Public License v2.0
# - GNU General Public License, version 2
# - GNU Lesser General Public License
+# - GPL-2.0-with-classpath-exception
# - GPL2 w/ CPE
# - Indiana University Extreme! Lab Software License, vesion 1.1.1
# - LGPL 2.1
# - LGPL-2.1-or-later
+# - MIT
# - MIT License
# - MIT license
+# - MIT-0
# - MPL 1.1
# - Modified BSD
# - New BSD License
@@ -60,5 +63,5 @@
# Please fill the missing licenses for dependencies :
#
#
-#Fri Dec 01 16:33:30 AEST 2023
+#Mon May 20 15:43:36 AEST 2024
oro--oro--2.0.8=
diff --git a/fhirpath/src/main/java/au/csiro/pathling/config/QueryConfiguration.java b/fhirpath/src/main/java/au/csiro/pathling/config/QueryConfiguration.java
index 2e8cbc537d..0556581535 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/config/QueryConfiguration.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/config/QueryConfiguration.java
@@ -17,11 +17,10 @@
package au.csiro.pathling.config;
+import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Data;
-import javax.validation.constraints.NotNull;
-
/**
* Represents configuration that controls the behaviour of query executors.
*
diff --git a/fhirpath/src/main/java/au/csiro/pathling/config/StorageConfiguration.java b/fhirpath/src/main/java/au/csiro/pathling/config/StorageConfiguration.java
index f01dc6081b..1dd482df1a 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/config/StorageConfiguration.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/config/StorageConfiguration.java
@@ -17,11 +17,12 @@
package au.csiro.pathling.config;
-import javax.validation.constraints.Min;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Pattern;
-import javax.validation.constraints.Size;
+import jakarta.annotation.Nonnull;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
diff --git a/fhirpath/src/main/java/au/csiro/pathling/extract/ExtractQueryExecutor.java b/fhirpath/src/main/java/au/csiro/pathling/extract/ExtractQueryExecutor.java
index a2ce6199cd..34c659f746 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/extract/ExtractQueryExecutor.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/extract/ExtractQueryExecutor.java
@@ -20,6 +20,7 @@
import au.csiro.pathling.view.UnnestingSelection;
import ca.uhn.fhir.context.FhirContext;
import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
diff --git a/fhirpath/src/main/java/au/csiro/pathling/fhirpath/comparison/CodingComparator.java b/fhirpath/src/main/java/au/csiro/pathling/fhirpath/comparison/CodingComparator.java
index 0f0bf0c260..81f768308c 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/fhirpath/comparison/CodingComparator.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/fhirpath/comparison/CodingComparator.java
@@ -25,6 +25,7 @@
import au.csiro.pathling.fhirpath.operator.Comparable;
import au.csiro.pathling.fhirpath.operator.Comparable.ComparisonOperation;
import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
diff --git a/fhirpath/src/main/java/au/csiro/pathling/fhirpath/parser/Visitor.java b/fhirpath/src/main/java/au/csiro/pathling/fhirpath/parser/Visitor.java
index eb9fb872a8..9f98b1320d 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/fhirpath/parser/Visitor.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/fhirpath/parser/Visitor.java
@@ -27,6 +27,7 @@
import au.csiro.pathling.fhirpath.operator.BinaryOperatorType;
import au.csiro.pathling.fhirpath.operator.CollectionOperations;
import au.csiro.pathling.fhirpath.operator.MethodDefinedOperator;
+import au.csiro.pathling.fhirpath.operator.SubsettingOperations;
import au.csiro.pathling.fhirpath.parser.generated.FhirPathBaseVisitor;
import au.csiro.pathling.fhirpath.parser.generated.FhirPathParser.AdditiveExpressionContext;
import au.csiro.pathling.fhirpath.parser.generated.FhirPathParser.AndExpressionContext;
@@ -190,7 +191,7 @@ public FhirPath visitIndexerExpression(
new Visitor().visit(ctx.expression(1)),
// Get a wrapped version of the index operator.
MethodDefinedOperator.build(
- CollectionOperations.class.getDeclaredMethod("index", Collection.class,
+ SubsettingOperations.class.getDeclaredMethod("index", Collection.class,
IntegerCollection.class)));
} catch (final NoSuchMethodException e) {
throw new RuntimeException(e);
diff --git a/fhirpath/src/main/java/au/csiro/pathling/sql/misc/TemporalDifferenceFunction.java b/fhirpath/src/main/java/au/csiro/pathling/sql/misc/TemporalDifferenceFunction.java
index 305e23e7f5..cb7063aec8 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/sql/misc/TemporalDifferenceFunction.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/sql/misc/TemporalDifferenceFunction.java
@@ -23,6 +23,7 @@
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.time.LocalDate;
+import java.time.Year;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
@@ -105,8 +106,12 @@ public static ZonedDateTime parse(final @Nonnull String encodedFrom) {
try {
return LocalDate.parse(encodedFrom).atStartOfDay(ZoneId.of("UTC"));
} catch (final DateTimeParseException ex) {
- // If we can't parse the value as a date or datetime, return null.
- return null;
+ try {
+ return Year.parse(encodedFrom).atDay(1).atStartOfDay(ZoneId.of("UTC"));
+ } catch (final DateTimeParseException exc) {
+ // If we can't parse the value as a date or datetime, return null.
+ return null;
+ }
}
}
}
diff --git a/fhirpath/src/main/java/au/csiro/pathling/views/Column.java b/fhirpath/src/main/java/au/csiro/pathling/views/Column.java
index 428dc697ec..f04c4b3c3a 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/views/Column.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/views/Column.java
@@ -1,9 +1,9 @@
package au.csiro.pathling.views;
import jakarta.annotation.Nullable;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Pattern;
-import javax.validation.constraints.Size;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
diff --git a/fhirpath/src/main/java/au/csiro/pathling/views/ColumnSelect.java b/fhirpath/src/main/java/au/csiro/pathling/views/ColumnSelect.java
index 07c991467b..afb3ad62b5 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/views/ColumnSelect.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/views/ColumnSelect.java
@@ -2,10 +2,10 @@
import com.google.gson.annotations.SerializedName;
import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
import java.util.Collections;
import java.util.List;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
diff --git a/fhirpath/src/main/java/au/csiro/pathling/views/ConstantDeclaration.java b/fhirpath/src/main/java/au/csiro/pathling/views/ConstantDeclaration.java
index 496073f0ee..0d9d98d130 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/views/ConstantDeclaration.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/views/ConstantDeclaration.java
@@ -1,7 +1,7 @@
package au.csiro.pathling.views;
import jakarta.annotation.Nonnull;
-import javax.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hl7.fhir.instance.model.api.IBase;
diff --git a/fhirpath/src/main/java/au/csiro/pathling/views/FhirView.java b/fhirpath/src/main/java/au/csiro/pathling/views/FhirView.java
index 579b14a5ea..fe46fa5858 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/views/FhirView.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/views/FhirView.java
@@ -1,9 +1,9 @@
package au.csiro.pathling.views;
import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
import java.util.List;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
import lombok.Data;
/**
diff --git a/fhirpath/src/main/java/au/csiro/pathling/views/ForEachOrNullSelect.java b/fhirpath/src/main/java/au/csiro/pathling/views/ForEachOrNullSelect.java
index db5692f069..9040ecfb90 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/views/ForEachOrNullSelect.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/views/ForEachOrNullSelect.java
@@ -1,10 +1,10 @@
package au.csiro.pathling.views;
+import com.google.gson.annotations.SerializedName;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
import java.util.Collections;
import java.util.List;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
-import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -30,8 +30,8 @@ public class ForEachOrNullSelect extends SelectClause {
String path;
@NotNull
- List column = Collections.emptyList();
-
+ List column = Collections.emptyList();
+
/**
* Nested select relative to the {@link #path}.
*
diff --git a/fhirpath/src/main/java/au/csiro/pathling/views/ForEachSelect.java b/fhirpath/src/main/java/au/csiro/pathling/views/ForEachSelect.java
index d758e0aeeb..60a60e9bea 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/views/ForEachSelect.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/views/ForEachSelect.java
@@ -1,10 +1,10 @@
package au.csiro.pathling.views;
+import com.google.gson.annotations.SerializedName;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
import java.util.Collections;
import java.util.List;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
-import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
diff --git a/fhirpath/src/main/java/au/csiro/pathling/views/WhereClause.java b/fhirpath/src/main/java/au/csiro/pathling/views/WhereClause.java
index cb78bcd7bd..67f8ecaae0 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/views/WhereClause.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/views/WhereClause.java
@@ -2,7 +2,7 @@
import com.google.gson.annotations.SerializedName;
import jakarta.annotation.Nullable;
-import javax.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
diff --git a/fhirpath/src/test/java/au/csiro/pathling/fhirpath/function/UntilFunctionTest.java b/fhirpath/src/test/java/au/csiro/pathling/fhirpath/function/UntilFunctionTest.java
index b1e8f806c9..c9dd3129a6 100644
--- a/fhirpath/src/test/java/au/csiro/pathling/fhirpath/function/UntilFunctionTest.java
+++ b/fhirpath/src/test/java/au/csiro/pathling/fhirpath/function/UntilFunctionTest.java
@@ -275,6 +275,39 @@ class UntilFunctionTest {
// }
//
// @Test
+ // void yearOnlyDateInput() throws ParseException {
+ // final Dataset leftDataset = new DatasetBuilder(spark)
+ // .withIdColumn(ID_ALIAS)
+ // .withColumn(DataTypes.StringType)
+ // .withRow("patient-1", "2020")
+ // .build();
+ // final ElementPath input = new ElementPathBuilder(spark)
+ // .fhirType(FHIRDefinedType.DATE)
+ // .dataset(leftDataset)
+ // .idAndValueColumns()
+ // .singular(true)
+ // .build();
+ // final DateTimeLiteralPath argument = DateTimeLiteralPath.fromString("2020-01-02T00:00:00Z",
+ // input);
+ // final ParserContext context = new ParserContextBuilder(spark, fhirContext)
+ // .groupingColumns(Collections.singletonList(input.getIdColumn()))
+ // .build();
+ // final Dataset expectedResult = new DatasetBuilder(spark)
+ // .withIdColumn(ID_ALIAS)
+ // .withColumn(DataTypes.IntegerType)
+ // .withRow("patient-1", 86400000)
+ // .build();
+ // final List arguments = List.of(argument,
+ // StringLiteralPath.fromString("millisecond", input));
+ // final NamedFunctionInput functionInput = new NamedFunctionInput(context, input, arguments);
+ // final FhirPath result = NamedFunction.getInstance("until").invoke(functionInput);
+ // assertThat(result)
+ // .isElementPath(IntegerPath.class)
+ // .selectResult()
+ // .hasRows(expectedResult);
+ // }
+ //
+ // @Test
// void invalidCalendarDuration() {
// final Dataset leftDataset = new DatasetBuilder(spark)
// .withIdColumn(ID_ALIAS)
diff --git a/fhirpath/src/test/java/au/csiro/pathling/sql/udf/TerminologyUdfTest.java b/fhirpath/src/test/java/au/csiro/pathling/sql/udf/TerminologyUdfTest.java
index 383cac8b82..483816ac81 100644
--- a/fhirpath/src/test/java/au/csiro/pathling/sql/udf/TerminologyUdfTest.java
+++ b/fhirpath/src/test/java/au/csiro/pathling/sql/udf/TerminologyUdfTest.java
@@ -319,8 +319,8 @@ public void testTranslateCoding() {
final Dataset expectedResult = DatasetBuilder.of(spark).withIdColumn("id")
.withColumn("result", TranslateUdf.RETURN_TYPE)
.withRow("uc-null", null)
- .withRow("uc-coding_1", CodingEncoding.encodeList(Arrays.asList(CODING_5, CODING_4)))
- .withRow("uc-coding_2", Collections.emptyList())
+ .withRow("uc-coding_1", CodingEncoding.encodeListToArray(List.of(CODING_5, CODING_4)))
+ .withRow("uc-coding_2", new Row[]{})
.build();
DatasetAssert.of(result).hasRows(expectedResult);
}
@@ -334,11 +334,11 @@ public void testTranslateCodingArray() {
.withIdColumn("id")
.withColumn("codings", DataTypes.createArrayType(CodingEncoding.DATA_TYPE))
.withRow("uc-null", null)
- .withRow("uc-empty", Collections.emptyList())
- .withRow("uc-coding_1", Collections.singletonList(CodingEncoding.encode(CODING_1)))
- .withRow("uc-coding_2", Collections.singletonList(CodingEncoding.encode(CODING_2)))
- .withRow("uc-coding_1+coding_1", Arrays.asList(CodingEncoding.encode(CODING_1),
- CodingEncoding.encode(CODING_1)))
+ .withRow("uc-empty", new Row[]{})
+ .withRow("uc-coding_1", new Row[]{CodingEncoding.encode(CODING_1)})
+ .withRow("uc-coding_2", new Row[]{CodingEncoding.encode(CODING_2)})
+ .withRow("uc-coding_1+coding_1",
+ CodingEncoding.encodeListToArray(List.of(CODING_1, CODING_1)))
.build();
final Dataset result = ds.select(ds.col("id"),
@@ -350,11 +350,11 @@ public void testTranslateCodingArray() {
final Dataset expectedResult = DatasetBuilder.of(spark).withIdColumn("id")
.withColumn("result", TranslateUdf.RETURN_TYPE)
.withRow("uc-null", null)
- .withRow("uc-empty", Collections.emptyList())
- .withRow("uc-coding_1", CodingEncoding.encodeList(Arrays.asList(CODING_5, CODING_4)))
- .withRow("uc-coding_2", Collections.emptyList())
+ .withRow("uc-empty", new Row[]{})
+ .withRow("uc-coding_1", CodingEncoding.encodeListToArray(List.of(CODING_5, CODING_4)))
+ .withRow("uc-coding_2", new Row[]{})
.withRow("uc-coding_1+coding_1",
- CodingEncoding.encodeList(Arrays.asList(CODING_5, CODING_4)))
+ CodingEncoding.encodeListToArray(List.of(CODING_5, CODING_4)))
.build();
DatasetAssert.of(result).hasRows(expectedResult);
}
@@ -514,8 +514,8 @@ public void testDisplayWithLanguage() {
@ParameterizedTest
@MethodSource("propertyParameters")
public void testProperty(final String propertyType, final DataType resultDataType,
- final Type[] propertyAFhirValues, final Type[] propertyBFhirValues,
- final Object[] propertyASqlValues, final Object[] propertyBSqlValues) {
+ final List propertyAFhirValues, final List propertyBFhirValues,
+ final List propertyASqlValues, final List propertyBSqlValues) {
TerminologyServiceHelpers.setupLookup(terminologyService)
.withProperty(CODING_1, "property_a", null, propertyAFhirValues)
.withProperty(CODING_2, "property_b", null, propertyBFhirValues);
@@ -536,8 +536,8 @@ public void testProperty(final String propertyType, final DataType resultDataTyp
.withColumn("result", DataTypes.createArrayType(resultDataType))
.withRow("uc-null", null)
.withRow("uc-invalid", null)
- .withRow("uc-codingA", Arrays.asList(propertyASqlValues))
- .withRow("uc-codingB", Collections.emptyList())
+ .withRow("uc-codingA", propertyASqlValues.toArray())
+ .withRow("uc-codingB", new Object[]{})
.build();
DatasetAssert.of(resultA)
@@ -552,8 +552,8 @@ public void testProperty(final String propertyType, final DataType resultDataTyp
.withColumn("result", DataTypes.createArrayType(resultDataType))
.withRow("uc-null", null)
.withRow("uc-invalid", null)
- .withRow("uc-codingA", Collections.emptyList())
- .withRow("uc-codingB", Arrays.asList(propertyBSqlValues))
+ .withRow("uc-codingA", new Object[]{})
+ .withRow("uc-codingB", propertyBSqlValues.toArray())
.build();
DatasetAssert.of(resultB)
@@ -563,7 +563,7 @@ public void testProperty(final String propertyType, final DataType resultDataTyp
@Test
public void testPropertyWithDefaultType() {
TerminologyServiceHelpers.setupLookup(terminologyService)
- .withProperty(CODING_1, "property_a", (String) null, new StringType("value_a"));
+ .withProperty(CODING_1, "property_a", null, List.of(new StringType("value_a")));
final Dataset ds = DatasetBuilder.of(spark)
.withIdColumn("id")
@@ -590,8 +590,8 @@ public void testPropertyWithDefaultType() {
@Test
public void testPropertyWithLanguage() {
TerminologyServiceHelpers.setupLookup(terminologyService)
- .withProperty(CODING_1, "property_a", "de", new StringType("value_a_de"))
- .withProperty(CODING_2, "property_b", "fr", new StringType("value_b_fr"));
+ .withProperty(CODING_1, "property_a", "de", List.of(new StringType("value_a_de")))
+ .withProperty(CODING_2, "property_b", "fr", List.of(new StringType("value_b_fr")));
final Dataset ds = DatasetBuilder.of(spark)
.withIdColumn("id")
diff --git a/fhirpath/src/test/java/au/csiro/pathling/test/SpringBootUnitTest.java b/fhirpath/src/test/java/au/csiro/pathling/test/SpringBootUnitTest.java
index 954aa491b8..9aaae4b1bf 100644
--- a/fhirpath/src/test/java/au/csiro/pathling/test/SpringBootUnitTest.java
+++ b/fhirpath/src/test/java/au/csiro/pathling/test/SpringBootUnitTest.java
@@ -2,14 +2,13 @@
import au.csiro.pathling.UnitTestDependencies;
-import org.junit.jupiter.api.Tag;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.ActiveProfiles;
-
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.junit.jupiter.api.Tag;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
diff --git a/fhirpath/src/test/java/au/csiro/pathling/test/assertions/Assertions.java b/fhirpath/src/test/java/au/csiro/pathling/test/assertions/Assertions.java
index 81309def6f..af5fd211be 100644
--- a/fhirpath/src/test/java/au/csiro/pathling/test/assertions/Assertions.java
+++ b/fhirpath/src/test/java/au/csiro/pathling/test/assertions/Assertions.java
@@ -21,6 +21,7 @@
import au.csiro.pathling.fhirpath.execution.CollectionDataset;
import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
diff --git a/fhirpath/src/test/java/au/csiro/pathling/test/builders/ElementPathBuilder.java b/fhirpath/src/test/java/au/csiro/pathling/test/builders/ElementPathBuilder.java
index f8e2b3b68f..e047ad38f4 100644
--- a/fhirpath/src/test/java/au/csiro/pathling/test/builders/ElementPathBuilder.java
+++ b/fhirpath/src/test/java/au/csiro/pathling/test/builders/ElementPathBuilder.java
@@ -28,6 +28,7 @@
import au.csiro.pathling.test.helpers.SparkHelpers.IdAndValueColumns;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
+import java.util.Optional;
import org.apache.spark.sql.Column;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
diff --git a/fhirpath/src/test/java/au/csiro/pathling/test/helpers/SparkHelpers.java b/fhirpath/src/test/java/au/csiro/pathling/test/helpers/SparkHelpers.java
index f2642a38d0..40e21b58bc 100644
--- a/fhirpath/src/test/java/au/csiro/pathling/test/helpers/SparkHelpers.java
+++ b/fhirpath/src/test/java/au/csiro/pathling/test/helpers/SparkHelpers.java
@@ -29,6 +29,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
import lombok.Value;
import org.apache.spark.sql.Column;
import org.apache.spark.sql.Dataset;
diff --git a/fhirpath/src/test/resources/logback-spring.xml b/fhirpath/src/test/resources/logback-spring.xml
deleted file mode 100644
index 38bfcfb1d3..0000000000
--- a/fhirpath/src/test/resources/logback-spring.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
- [%level] %logger{36} - %msg%n
-
-
-
- target/timing.log
-
- [%d] %msg%n
-
-
-
-
-
-
-
-
-
-
-
diff --git a/fhirpath/src/test/resources/logback.xml b/fhirpath/src/test/resources/logback.xml
index 71e0ba327c..0a2a613af8 100644
--- a/fhirpath/src/test/resources/logback.xml
+++ b/fhirpath/src/test/resources/logback.xml
@@ -31,7 +31,10 @@
-
+
+
+
+
diff --git a/lib/R/DESCRIPTION.src b/lib/R/DESCRIPTION.src
index 19b3b1d786..04f9bc5dad 100644
--- a/lib/R/DESCRIPTION.src
+++ b/lib/R/DESCRIPTION.src
@@ -26,12 +26,12 @@ Encoding: UTF-8
Depends:
R (>= 3.5.0)
Imports:
- rlang(>= 1.0.0), sparklyr(>= 1.8.4)
+ rlang(>= 1.0.0), sparklyr(>= 1.8.1)
Suggests:
- testthat(>= 3.0.0)
+ testthat(>= 3.2.1.1)
Config/testthat/edition: 3
RoxygenNote: 7.2.3
-SystemRequirements: Spark: 3.4.x
+SystemRequirements: Spark: 3.5.x
Config/pathling/Version: %pathling.version%
Config/pathling/SparkVersion: %pathling.Rapi.sparkVersion%
Config/pathling/ScalaVersion: %pathling.Rapi.scalaVersion%
diff --git a/lib/R/R/context.R b/lib/R/R/context.R
index 9565afbc8a..5f5bc157df 100644
--- a/lib/R/R/context.R
+++ b/lib/R/R/context.R
@@ -146,12 +146,6 @@ pathling_connect <- function(
new_spark_connection()
}
- spark_runtime_version <- sparklyr::spark_version(spark)
- if (package_version(spark_runtime_version) < spark_info$spark_version) {
- warning(sprintf("Incompatible version of spark: %s, while Pathling requires at least: %s",
- spark_runtime_version, spark_info$spark_version))
- }
-
encoders_config <- spark %>%
j_invoke_static(
"au.csiro.pathling.config.EncodingConfiguration", "builder") %>%
diff --git a/lib/R/R/datasource.R b/lib/R/R/datasource.R
index 8248ae8ca6..131ba6d712 100644
--- a/lib/R/R/datasource.R
+++ b/lib/R/R/datasource.R
@@ -39,6 +39,22 @@ ImportMode <- list(
MERGE = "merge"
)
+#' SaveMode
+#'
+#' The following save modes are supported:
+#' \itemize{
+#' \item{\code{OVERWRITE}: Overwrite any existing data.}
+#' \item{\code{APPEND}: Append the new data to the existing data.}
+#' \item{\code{IGNORE}: Only save the data if the file does not already exist.}
+#' \item{\code{ERROR}: Raise an error if the file already exists.}
+#' }
+SaveMode <- list(
+ OVERWRITE = "overwrite",
+ APPEND = "append",
+ IGNORE = "ignore",
+ ERROR = "error"
+)
+
#' Create a data source from NDJSON
#'
#' Creates a data source from a directory containing NDJSON files. The files must be named with the
@@ -250,6 +266,7 @@ invoke_datasink <- function(ds, name, ...) {
#'
#' @param ds The DataSource object.
#' @param path The URI of the directory to write the files to.
+#' @param save_mode The save mode to use when writing the data.
#' @param file_name_mapper An optional function that can be used to customise the mapping
#' of the resource type to the file name. Currently not implemented.
#'
@@ -269,10 +286,10 @@ invoke_datasink <- function(ds, name, ...) {
#' @family data sink functions
#'
#' @export
-ds_write_ndjson <- function(ds, path, file_name_mapper = NULL) {
+ds_write_ndjson <- function(ds, path, save_mode = SaveMode$ERROR, file_name_mapper = NULL) {
#See: issue #1601 (Implement file_name_mappers in R sparkly API)
stopifnot(file_name_mapper == NULL)
- invoke_datasink(ds, "ndjson", path)
+ invoke_datasink(ds, "ndjson", path, save_mode)
}
#' Write FHIR data to Parquet files
@@ -281,6 +298,7 @@ ds_write_ndjson <- function(ds, path, file_name_mapper = NULL) {
#'
#' @param ds The DataSource object.
#' @param path The URI of the directory to write the files to.
+#' @param save_mode The save mode to use when writing the data.
#'
#' @return No return value, called for side effects only.
#'
@@ -298,8 +316,8 @@ ds_write_ndjson <- function(ds, path, file_name_mapper = NULL) {
#' @family data sink functions
#'
#' @export
-ds_write_parquet <- function(ds, path) {
- invoke_datasink(ds, "parquet", path)
+ds_write_parquet <- function(ds, path, save_mode = SaveMode$ERROR) {
+ invoke_datasink(ds, "parquet", path, save_mode)
}
#' Write FHIR data to Delta files
diff --git a/lib/R/R/dependencies.R b/lib/R/R/dependencies.R
index 3540621b21..c792c675a4 100644
--- a/lib/R/R/dependencies.R
+++ b/lib/R/R/dependencies.R
@@ -18,7 +18,7 @@ spark_dependencies <- function(spark_version, scala_version, ...) {
sparklyr::spark_dependency(
packages = c(
paste0("au.csiro.pathling:library-runtime:", pathling_version()),
- paste0("io.delta:delta-core_", spark_info$scala_version, ":", spark_info$delta_version),
+ paste0("io.delta:delta-spark_", spark_info$scala_version, ":", spark_info$delta_version),
paste0("org.apache.hadoop:hadoop-aws:", spark_info$hadoop_version)
)
)
diff --git a/lib/R/README.md b/lib/R/README.md
index 9c3f6bf8b9..3b5c7457b5 100644
--- a/lib/R/README.md
+++ b/lib/R/README.md
@@ -2,7 +2,7 @@ R API for Pathling
==================
``pathling`` package is the R API for [Pathling](https://pathling.csiro.au),
-based on [sparklyr](https://spark.rstudio.com/). It provides a set of functions
+based on [sparklyr](https://spark.posit.co/). It provides a set of functions
that aid the use of FHIR terminology services and FHIR data within R code.
## Local installation
diff --git a/lib/R/pom.xml b/lib/R/pom.xml
index c454b94ac7..8a75b30582 100644
--- a/lib/R/pom.xml
+++ b/lib/R/pom.xml
@@ -63,7 +63,7 @@
of the sparklyr version that we depend upon that is on the archive site. The reason we can't
use the latest from the CDN is that the files drop off the CDN when a new version is released.
-->
- 3.4.0
+ 3.5.1
${pathling.scalaVersion}
${pathling.hadoopMajorVersion}
${pathling.hadoopVersion}
@@ -225,6 +225,21 @@
+
+ generate-docs
+ process-sources
+
+ exec
+
+
+ Rscript
+ ${project.basedir}
+
+ -e
+ roxygen2::roxygenise()
+
+
+
test
test
@@ -308,21 +323,6 @@
org.codehaus.mojo
exec-maven-plugin
-
- generate-docs
- process-sources
-
- exec
-
-
- Rscript
- ${project.basedir}
-
- -e
- roxygen2::roxygenise()
-
-
-
generate-site
process-sources
diff --git a/lib/R/src/license/THIRD-PARTY.properties b/lib/R/src/license/THIRD-PARTY.properties
index 3716e1a8e6..00719fb2e0 100644
--- a/lib/R/src/license/THIRD-PARTY.properties
+++ b/lib/R/src/license/THIRD-PARTY.properties
@@ -9,7 +9,6 @@
# - Apache License V2.0
# - Apache License, 2.0
# - Apache License, Version 2.0
-# - Apache License, version 2.0
# - Apache Software License 2.0
# - Apache Software License, version 1.1
# - Apache-2.0
@@ -21,19 +20,22 @@
# - BSD 3-clause
# - BSD licence
# - BSD-3-Clause
-# - CC0
+# - CDDL + GPLv2 with classpath exception
# - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
# - Dual license consisting of the CDDL v1.1 and GPL v2
# - EDL 1.0
# - EPL 2.0
+# - EPL-2.0
# - Eclipse Public License - v 1.0
# - GNU General Public License, version 2
# - GNU Lesser General Public License
+# - GPL-2.0-with-classpath-exception
# - GPL2 w/ CPE
# - Indiana University Extreme! Lab Software License, vesion 1.1.1
# - LGPL 2.1
# - MIT License
# - MIT license
+# - MIT-0
# - MPL 1.1
# - Modified BSD
# - New BSD License
@@ -53,5 +55,5 @@
# Please fill the missing licenses for dependencies :
#
#
-#Fri Dec 01 16:35:15 AEST 2023
+#Mon May 20 15:45:54 AEST 2024
oro--oro--2.0.8=
diff --git a/lib/import/azure-pipelines.yml b/lib/import/azure-pipelines.yml
index 69f0bff1c2..d3a5cceac0 100644
--- a/lib/import/azure-pipelines.yml
+++ b/lib/import/azure-pipelines.yml
@@ -34,7 +34,7 @@ stages:
options: "-pl lib/import -am"
mavenOptions: "$(mavenOptions)"
javaHomeOption: "JDKVersion"
- jdkVersionOption: "1.11"
+ jdkVersionOption: "1.17"
jdkArchitectureOption: "x64"
goals: "package"
timeoutInMinutes: 5
diff --git a/lib/python/README.md b/lib/python/README.md
index 42f38cfb05..242fcac9a2 100644
--- a/lib/python/README.md
+++ b/lib/python/README.md
@@ -271,11 +271,11 @@ Maven package. Once the cluster is restarted, the libraries should be available
for import and use within all notebooks.
By default, Databricks uses Java 8 within its clusters, while Pathling requires
-Java 11. To enable Java 11 support within your cluster, navigate to __Advanced
+Java 17. To enable Java 17 support within your cluster, navigate to __Advanced
Options > Spark > Environment Variables__ and add the following:
```bash
-JNAME=zulu11-ca-amd64
+JNAME=zulu17-ca-amd64
```
See the Databricks documentation on
diff --git a/lib/python/examples/query.py b/lib/python/examples/query.py
index f52e20db48..5dab019bf3 100755
--- a/lib/python/examples/query.py
+++ b/lib/python/examples/query.py
@@ -14,9 +14,8 @@
# limitations under the License.
import os
-from tempfile import mkdtemp
-
from pyspark.sql import DataFrame, SparkSession
+from tempfile import mkdtemp
from pathling import PathlingContext, DataSource, Expression as exp
from pathling._version import __java_version__
@@ -36,7 +35,7 @@
spark = (
SparkSession.builder.config(
"spark.jars.packages",
- f"au.csiro.pathling:library-runtime:{__java_version__},io.delta:delta-core_2.12:2.2.0",
+ f"au.csiro.pathling:library-runtime:{__java_version__},io.delta:delta-spark_2.12:3.2.0",
)
.config(
"spark.sql.extensions",
diff --git a/lib/python/pathling/context.py b/lib/python/pathling/context.py
index fb887615c8..72e0e27e3f 100644
--- a/lib/python/pathling/context.py
+++ b/lib/python/pathling/context.py
@@ -184,7 +184,7 @@ def _new_spark_session():
SparkSession.builder.config(
"spark.jars.packages",
f"au.csiro.pathling:library-runtime:{__java_version__},"
- f"io.delta:delta-core_{__scala_version__}:{__delta_version__},"
+ f"io.delta:delta-spark_{__scala_version__}:{__delta_version__},"
f"org.apache.hadoop:hadoop-aws:{__hadoop_version__}",
)
.config(
diff --git a/lib/python/pathling/datasink.py b/lib/python/pathling/datasink.py
index 1f1e00573f..d8d6c47d69 100644
--- a/lib/python/pathling/datasink.py
+++ b/lib/python/pathling/datasink.py
@@ -28,6 +28,22 @@ class ImportMode:
MERGE: str = "merge"
+class SaveMode:
+ """
+ Constants that represent the different save modes.
+
+ OVERWRITE: Overwrite any existing data.
+ APPEND: Append the new data to the existing data.
+ IGNORE: Only save the data if the file does not already exist.
+ ERROR: Raise an error if the file already exists.
+ """
+
+ OVERWRITE: str = "overwrite"
+ APPEND: str = "append"
+ IGNORE: str = "ignore"
+ ERROR: str = "error"
+
+
class DataSinks(SparkConversionsMixin):
"""
A class for writing FHIR data to a variety of different targets.
@@ -41,12 +57,22 @@ def __init__(self, datasource: DataSource):
)
)
- def ndjson(self, path: str, file_name_mapper: Callable[[str], str] = None) -> None:
+ def ndjson(
+ self,
+ path: str,
+ save_mode: Optional[str] = SaveMode.ERROR,
+ file_name_mapper: Callable[[str], str] = None,
+ ) -> None:
"""
Writes the data to a directory of NDJSON files. The files will be named using the resource
type and the ".ndjson" extension.
:param path: The URI of the directory to write the files to.
+ :param save_mode: The save mode to use when writing the data:
+ - "overwrite" will overwrite any existing data.
+ - "append" will append the new data to the existing data.
+ - "ignore" will only save the data if the file does not already exist.
+ - "error" will raise an error if the file already exists.
:param file_name_mapper: An optional function that can be used to customise the mapping of
the resource type to the file name.
"""
@@ -54,17 +80,22 @@ def ndjson(self, path: str, file_name_mapper: Callable[[str], str] = None) -> No
wrapped_mapper = StringMapper(
self.spark._jvm._gateway_client, file_name_mapper
)
- self._datasinks.ndjson(path, wrapped_mapper)
+ self._datasinks.ndjson(path, save_mode, wrapped_mapper)
else:
- self._datasinks.ndjson(path)
+ self._datasinks.ndjson(path, save_mode)
- def parquet(self, path: str) -> None:
+ def parquet(self, path: str, save_mode: Optional[str] = SaveMode.ERROR) -> None:
"""
Writes the data to a directory of Parquet files.
:param path: The URI of the directory to write the files to.
+ :param save_mode: The save mode to use when writing the data:
+ - "overwrite" will overwrite any existing data.
+ - "append" will append the new data to the existing data.
+ - "ignore" will only save the data if the file does not already exist.
+ - "error" will raise an error if the file already exists.
"""
- self._datasinks.parquet(path)
+ self._datasinks.parquet(path, save_mode)
def delta(
self, path: str, import_mode: Optional[str] = ImportMode.OVERWRITE
diff --git a/lib/python/pom.xml b/lib/python/pom.xml
index 608512e36f..9e883134f6 100644
--- a/lib/python/pom.xml
+++ b/lib/python/pom.xml
@@ -222,10 +222,8 @@
spark-submit
- --jars
- ${project.build.directory}/dependency/*
--packages
- au.csiro.pathling:library-runtime:${project.version},io.delta:delta-core_${pathling.scalaVersion}:${pathling.deltaVersion}
+ au.csiro.pathling:library-runtime:${project.version},io.delta:delta-spark_${pathling.scalaVersion}:${pathling.deltaVersion}
--conf
spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension
--conf
diff --git a/lib/python/requirements/dev.txt b/lib/python/requirements/dev.txt
index 68e21c2110..7e6014def7 100644
--- a/lib/python/requirements/dev.txt
+++ b/lib/python/requirements/dev.txt
@@ -1,9 +1,9 @@
-r package.txt
Sphinx>=1.6,<8
sphinx-rtd-theme==1.2.2
-pytest==7.4.0
+pytest==8.2.0
ipython==8.10.0
-wheel==0.40.0
-twine==4.0.2
-build==0.10.0
-pytest-cov==4.1.0
+wheel==0.43.0
+twine==5.0.0
+build==1.2.1
+pytest-cov==5.0.0
diff --git a/lib/python/requirements/package.txt b/lib/python/requirements/package.txt
index 7de8ec0292..15af9fa8a4 100644
--- a/lib/python/requirements/package.txt
+++ b/lib/python/requirements/package.txt
@@ -1,2 +1,2 @@
-pyspark==3.4.1
+pyspark==3.5.1
deprecated==1.2.14
diff --git a/lib/python/setup.py b/lib/python/setup.py
index 244e815bdc..291bfd50be 100644
--- a/lib/python/setup.py
+++ b/lib/python/setup.py
@@ -56,14 +56,13 @@
classifiers=[
"Development Status :: 3 - Alpha",
"License :: OSI Approved :: Apache Software License",
- "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
],
license="Apache License, version 2.0",
- python_requires=">=3.7",
- install_requires=["pyspark>=3.4.0,<3.5.0", "deprecated>=1.2.13"],
+ python_requires=">=3.8",
+ install_requires=["pyspark>=3.5.0,<3.6.0", "deprecated>=1.2.13"],
include_package_data=True,
data_files=[
("share/pathling/examples", glob.glob("examples/*.py")),
diff --git a/lib/python/tests/conftest.py b/lib/python/tests/conftest.py
index e3b249f6d6..3aee982b7c 100644
--- a/lib/python/tests/conftest.py
+++ b/lib/python/tests/conftest.py
@@ -74,7 +74,7 @@ def pathling_ctx(request, temp_warehouse_dir):
.config(
"spark.jars.packages",
f"au.csiro.pathling:library-runtime:{__java_version__},"
- f"io.delta:delta-core_{__scala_version__}:{__delta_version__},"
+ f"io.delta:delta-spark_{__scala_version__}:{__delta_version__},"
f"org.apache.hadoop:hadoop-aws:{__hadoop_version__}",
)
.config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension")
diff --git a/library-api/pom.xml b/library-api/pom.xml
index b4cd90aea5..42bad4487b 100644
--- a/library-api/pom.xml
+++ b/library-api/pom.xml
@@ -70,9 +70,13 @@
io.delta
- delta-core_${pathling.scalaVersion}
+ delta-spark_${pathling.scalaVersion}
provided
+
+ javax.servlet
+ javax.servlet-api
+
org.apache.hadoop
hadoop-client-api
@@ -88,6 +92,11 @@
lombok
provided
+
+ org.apache.derby
+ derbytools
+ test
+
@@ -232,6 +241,11 @@
+
+
+ **/logback.xml
+
+
diff --git a/library-api/src/license/THIRD-PARTY.properties b/library-api/src/license/THIRD-PARTY.properties
index d948b4656a..5175cd141a 100644
--- a/library-api/src/license/THIRD-PARTY.properties
+++ b/library-api/src/license/THIRD-PARTY.properties
@@ -9,7 +9,6 @@
# - Apache License V2.0
# - Apache License, 2.0
# - Apache License, Version 2.0
-# - Apache License, version 2.0
# - Apache Software License 2.0
# - Apache Software License, version 1.1
# - Apache v2
@@ -23,23 +22,28 @@
# - BSD License
# - BSD licence
# - BSD-3-Clause
-# - CC0
+# - CDDL + GPLv2 with classpath exception
# - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
# - Dual license consisting of the CDDL v1.1 and GPL v2
# - EDL 1.0
# - EPL 2.0
+# - EPL-2.0
# - Eclipse Public License - v 1.0
# - Eclipse Public License v2.0
# - GNU General Public License, version 2
# - GNU Lesser General Public License
+# - GPL-2.0-with-classpath-exception
# - GPL2 w/ CPE
# - Indiana University Extreme! Lab Software License, vesion 1.1.1
# - LGPL 2.1
+# - MIT
# - MIT License
# - MIT license
+# - MIT-0
# - MPL 1.1
# - Modified BSD
# - New BSD License
+# - New BSD license
# - Public Domain
# - The Apache License, Version 2.0
# - The Apache Software License, Version 2.0
@@ -57,7 +61,7 @@
# Please fill the missing licenses for dependencies :
#
#
-#Fri Dec 01 16:33:56 AEST 2023
+#Mon May 20 15:44:02 AEST 2024
javax.transaction--transaction-api--1.1=
javax.transaction--jta--1.1=
oro--oro--2.0.8=
diff --git a/library-api/src/main/java/au/csiro/pathling/library/PathlingContext.java b/library-api/src/main/java/au/csiro/pathling/library/PathlingContext.java
index d55262c453..ce722a40df 100644
--- a/library-api/src/main/java/au/csiro/pathling/library/PathlingContext.java
+++ b/library-api/src/main/java/au/csiro/pathling/library/PathlingContext.java
@@ -171,8 +171,7 @@ public static PathlingContext create(@Nonnull final SparkSession sparkSession,
@Nonnull
public static PathlingContext create(@Nonnull final SparkSession sparkSession) {
final EncodingConfiguration encodingConfig = EncodingConfiguration.builder().build();
- final TerminologyConfiguration terminologyConfig = TerminologyConfiguration.builder().build();
- return create(sparkSession, encodingConfig, terminologyConfig);
+ return create(sparkSession, encodingConfig);
}
/**
@@ -250,10 +249,20 @@ public Dataset encode(@Nonnull final Dataset stringResourcesDF,
return encode(stringResources, definition.getImplementingClass(), inputMimeType).toDF();
}
+ /**
+ * Takes a dataframe of encoded resources of the specified type and decodes them into a dataset of
+ * string representations, based on the requested output MIME type.
+ *
+ * @param resources the dataframe of encoded resources
+ * @param resourceName the name of the resources to decode
+ * @param outputMimeType the MIME type of the output strings
+ * @param the type of the resource
+ * @return a dataset of string representations of the resources
+ */
@Nonnull
public Dataset decode(@Nonnull final Dataset resources,
@Nonnull final String resourceName, @Nonnull final String outputMimeType) {
- final BaseRuntimeElementDefinition definition = FhirEncoders.contextFor(fhirVersion)
+ final RuntimeResourceDefinition definition = FhirEncoders.contextFor(fhirVersion)
.getResourceDefinition(resourceName);
@SuppressWarnings("unchecked")
@@ -261,7 +270,7 @@ public Dataset decode(@Nonnull final Dataset encoder = fhirEncoders.of(resourceClass);
final Dataset typedResources = resources.as(encoder);
- final DecodeResourceMapPartitions mapper = new DecodeResourceMapPartitions(fhirVersion,
+ final DecodeResourceMapPartitions mapper = new DecodeResourceMapPartitions<>(fhirVersion,
outputMimeType, resourceClass);
return typedResources.mapPartitions(mapper, Encoders.STRING());
diff --git a/library-api/src/main/java/au/csiro/pathling/library/io/sink/DataSinkBuilder.java b/library-api/src/main/java/au/csiro/pathling/library/io/sink/DataSinkBuilder.java
index b96ed1b5b6..72d2c0cbb5 100644
--- a/library-api/src/main/java/au/csiro/pathling/library/io/sink/DataSinkBuilder.java
+++ b/library-api/src/main/java/au/csiro/pathling/library/io/sink/DataSinkBuilder.java
@@ -25,6 +25,8 @@
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.function.UnaryOperator;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
/**
* This class knows how to take an @link{EnumerableDataSource} and write it to a variety of
@@ -34,6 +36,15 @@
*/
public class DataSinkBuilder {
+ @Nonnull
+ private static final Map SAVE_MODES = new ImmutableMap.Builder()
+ .put("error", SaveMode.ErrorIfExists)
+ .put("errorifexists", SaveMode.ErrorIfExists)
+ .put("overwrite", SaveMode.Overwrite)
+ .put("append", SaveMode.Append)
+ .put("ignore", SaveMode.Ignore)
+ .build();
+
@Nonnull
private final PathlingContext context;
@@ -50,9 +61,16 @@ public DataSinkBuilder(@Nonnull final PathlingContext context, @Nonnull final Da
* "ndjson" extension.
*
* @param path the directory to write the files to
+ * @param saveMode the save mode to use:
+ *
+ * "error" - throw an error if the files already exist
+ * "overwrite" - overwrite any existing files
+ * "append" - append to any existing files
+ * "ignore" - do nothing if the files already exist
+ *
*/
- public void ndjson(@Nullable final String path) {
- new NdjsonSink(context, requireNonNull(path)).write(source);
+ public void ndjson(@Nullable final String path, @Nullable final String saveMode) {
+ new NdjsonSink(context, requireNonNull(path), resolveSaveMode(saveMode)).write(source);
}
/**
@@ -60,11 +78,19 @@ public void ndjson(@Nullable final String path) {
* custom file name mapper.
*
* @param path the directory to write the files to
+ * @param saveMode the save mode to use:
+ *
+ * "error" - throw an error if the files already exist
+ * "overwrite" - overwrite any existing files
+ * "append" - append to any existing files
+ * "ignore" - do nothing if the files already exist
+ *
* @param fileNameMapper a function that maps a resource type to a file name
*/
- public void ndjson(@Nullable final String path,
+ public void ndjson(@Nullable final String path, @Nullable final String saveMode,
@Nullable final UnaryOperator fileNameMapper) {
- new NdjsonSink(context, requireNonNull(path), requireNonNull(fileNameMapper)).write(source);
+ new NdjsonSink(context, requireNonNull(path), resolveSaveMode(saveMode),
+ requireNonNull(fileNameMapper)).write(source);
}
/**
@@ -72,9 +98,16 @@ public void ndjson(@Nullable final String path,
* "parquet" extension.
*
* @param path the directory to write the files to
+ * @param saveMode the save mode to use:
+ *
+ * "error" - throw an error if the files already exist
+ * "overwrite" - overwrite any existing files
+ * "append" - append to any existing files
+ * "ignore" - do nothing if the files already exist
+ *
*/
- public void parquet(@Nullable final String path) {
- new ParquetSink(requireNonNull(path)).write(source);
+ public void parquet(@Nullable final String path, @Nullable final String saveMode) {
+ new ParquetSink(requireNonNull(path), resolveSaveMode(saveMode)).write(source);
}
/**
@@ -134,4 +167,11 @@ public void tables(@Nullable final String importMode, @Nullable final String sch
new CatalogSink(context, ImportMode.fromCode(importMode), requireNonNull(schema)).write(source);
}
+ @Nonnull
+ private static SaveMode resolveSaveMode(final @Nullable String saveMode) {
+ return saveMode == null
+ ? SaveMode.ErrorIfExists
+ : SAVE_MODES.get(saveMode);
+ }
+
}
diff --git a/library-api/src/main/java/au/csiro/pathling/library/io/sink/NdjsonSink.java b/library-api/src/main/java/au/csiro/pathling/library/io/sink/NdjsonSink.java
index fe0e55de0c..e8239cc3d3 100644
--- a/library-api/src/main/java/au/csiro/pathling/library/io/sink/NdjsonSink.java
+++ b/library-api/src/main/java/au/csiro/pathling/library/io/sink/NdjsonSink.java
@@ -26,6 +26,7 @@
import jakarta.annotation.Nonnull;
import java.util.function.UnaryOperator;
import org.apache.spark.sql.Dataset;
+import org.apache.spark.sql.SaveMode;
import org.hl7.fhir.r4.model.Enumerations.ResourceType;
/**
@@ -42,26 +43,33 @@ public class NdjsonSink implements DataSink {
@Nonnull
private final String path;
+ @Nonnull
+ private final SaveMode saveMode;
+
@Nonnull
private final UnaryOperator fileNameMapper;
/**
* @param context the {@link PathlingContext} to use
* @param path the path to write the NDJSON files to
+ * @param saveMode the {@link SaveMode} to use
*/
- public NdjsonSink(@Nonnull final PathlingContext context, @Nonnull final String path) {
- this(context, path, UnaryOperator.identity());
+ public NdjsonSink(@Nonnull final PathlingContext context, @Nonnull final String path,
+ @Nonnull final SaveMode saveMode) {
+ this(context, path, saveMode, UnaryOperator.identity());
}
/**
* @param context the {@link PathlingContext} to use
* @param path the path to write the NDJSON files to
+ * @param saveMode the {@link SaveMode} to use
* @param fileNameMapper a function that maps resource type to file name
*/
public NdjsonSink(@Nonnull final PathlingContext context, @Nonnull final String path,
- @Nonnull final UnaryOperator fileNameMapper) {
+ @Nonnull final SaveMode saveMode, @Nonnull final UnaryOperator fileNameMapper) {
this.context = context;
this.path = path;
+ this.saveMode = saveMode;
this.fileNameMapper = fileNameMapper;
}
@@ -77,7 +85,7 @@ public void write(@Nonnull final DataSource source) {
"ndjson");
final String resultUrl = safelyJoinPaths(path, fileName);
final String resultUrlPartitioned = resultUrl + ".partitioned";
- jsonStrings.coalesce(1).write().text(resultUrlPartitioned);
+ jsonStrings.coalesce(1).write().mode(saveMode).text(resultUrlPartitioned);
// Remove the partitioned directory and replace it with a single file.
departitionResult(context.getSpark(), resultUrlPartitioned, resultUrl, "txt");
diff --git a/library-api/src/main/java/au/csiro/pathling/library/io/sink/ParquetSink.java b/library-api/src/main/java/au/csiro/pathling/library/io/sink/ParquetSink.java
index 3b89c7c1c6..e40ae78430 100644
--- a/library-api/src/main/java/au/csiro/pathling/library/io/sink/ParquetSink.java
+++ b/library-api/src/main/java/au/csiro/pathling/library/io/sink/ParquetSink.java
@@ -23,6 +23,7 @@
import jakarta.annotation.Nonnull;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
+import org.apache.spark.sql.SaveMode;
import org.hl7.fhir.r4.model.Enumerations.ResourceType;
/**
@@ -36,11 +37,16 @@ public class ParquetSink implements DataSink {
@Nonnull
private final String path;
+ @Nonnull
+ private final SaveMode saveMode;
+
/**
* @param path the path to write the Parquet files to
+ * @param saveMode the {@link SaveMode} to use
*/
- public ParquetSink(@Nonnull final String path) {
+ public ParquetSink(@Nonnull final String path, @Nonnull final SaveMode saveMode) {
this.path = path;
+ this.saveMode = saveMode;
}
@Override
@@ -48,7 +54,7 @@ public void write(@Nonnull final DataSource source) {
for (final ResourceType resourceType : source.getResourceTypes()) {
final Dataset dataset = source.read(resourceType);
final String resultUrl = safelyJoinPaths(path, resourceType.toCode() + ".parquet");
- dataset.write().parquet(resultUrl);
+ dataset.write().mode(saveMode).parquet(resultUrl);
}
}
diff --git a/library-api/src/test/java/au/csiro/pathling/library/PathlingContextTest.java b/library-api/src/test/java/au/csiro/pathling/library/PathlingContextTest.java
index 58d3fc26d4..e382d816b4 100644
--- a/library-api/src/test/java/au/csiro/pathling/library/PathlingContextTest.java
+++ b/library-api/src/test/java/au/csiro/pathling/library/PathlingContextTest.java
@@ -47,6 +47,7 @@
import au.csiro.pathling.terminology.TerminologyServiceFactory;
import ca.uhn.fhir.context.FhirVersionEnum;
import jakarta.annotation.Nonnull;
+import jakarta.validation.ConstraintViolationException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -58,7 +59,6 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import javax.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.spark.sql.Column;
import org.apache.spark.sql.Dataset;
diff --git a/library-api/src/test/java/au/csiro/pathling/library/ResourceParserTest.java b/library-api/src/test/java/au/csiro/pathling/library/ResourceParserTest.java
index 23fa61aad9..285703c3fc 100644
--- a/library-api/src/test/java/au/csiro/pathling/library/ResourceParserTest.java
+++ b/library-api/src/test/java/au/csiro/pathling/library/ResourceParserTest.java
@@ -17,9 +17,12 @@
package au.csiro.pathling.library;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import ca.uhn.fhir.context.FhirVersionEnum;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
+import java.io.IOException;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Claim;
import org.hl7.fhir.r4.model.Condition;
@@ -27,9 +30,6 @@
import org.hl7.fhir.r4.model.Reference;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
public class ResourceParserTest {
diff --git a/library-api/src/test/java/au/csiro/pathling/library/io/DataSourcesTest.java b/library-api/src/test/java/au/csiro/pathling/library/io/DataSourcesTest.java
index 2f8c71b131..b3cfa2ead5 100644
--- a/library-api/src/test/java/au/csiro/pathling/library/io/DataSourcesTest.java
+++ b/library-api/src/test/java/au/csiro/pathling/library/io/DataSourcesTest.java
@@ -107,7 +107,7 @@ void ndjsonReadWrite() {
queryNdjsonData(data);
// Write the data back out to a temporary location.
- data.write().ndjson(temporaryDirectory.resolve("ndjson").toString());
+ data.write().ndjson(temporaryDirectory.resolve("ndjson").toString(), "error");
// Read the data back in.
final QueryableDataSource newData = pathlingContext.read()
@@ -151,7 +151,7 @@ void ndjsonReadWriteCustom() {
queryNdjsonData(data);
// Write the data back out to a temporary location.
- data.write().ndjson(temporaryDirectory.resolve("ndjson-custom").toString(),
+ data.write().ndjson(temporaryDirectory.resolve("ndjson-custom").toString(), "error",
(baseName) -> baseName.replaceFirst("Custom", ""));
// Read the data back in.
@@ -211,7 +211,7 @@ void parquetReadWrite() {
queryParquetData(data);
// Write the data back out to a temporary location.
- data.write().parquet(temporaryDirectory.resolve("parquet").toString());
+ data.write().parquet(temporaryDirectory.resolve("parquet").toString(), "error");
// Read the data back in.
final QueryableDataSource newData = pathlingContext.read()
diff --git a/library-api/src/test/resources/logback.xml b/library-api/src/test/resources/logback.xml
new file mode 100644
index 0000000000..4e0d318b6e
--- /dev/null
+++ b/library-api/src/test/resources/logback.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+ [%level] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
diff --git a/library-runtime/pom.xml b/library-runtime/pom.xml
index 39d4ad416b..900f641874 100644
--- a/library-runtime/pom.xml
+++ b/library-runtime/pom.xml
@@ -124,6 +124,13 @@
com/fasterxml/jackson/**
+
+ org/
+ ${shaded.dependency.prefix}.org.
+
+ org/hibernate/validator/**
+
+
diff --git a/licenses/apache 2.0 license - license-2.0.html b/licenses/apache 2.0 license - license-2.0.html
index 535c113155..15f2c60ac4 100644
--- a/licenses/apache 2.0 license - license-2.0.html
+++ b/licenses/apache 2.0 license - license-2.0.html
@@ -30,6 +30,7 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+}
+
+#pre-footer .wpcf7 input {
+ width: 50%;
+ border-radius: 0;
+ display:inline-block;
+ border: 2px black solid;
+ margin:0;
+ padding: 15px 20px;
+}
+#pre-footer .wpcf7 input[type=submit] {
+ background-color:black;
+ color:white;
+}
+
+.sidebar-post-loop ul li .wp-block-post-date {
+ margin-top: 0;
+}
+.sidebar-post-loop ul li.wp-block-post {
+ border-bottom : #CECECE 1px solid;
+ padding-bottom : 10px;
+ margin-bottom : 10px;
+ margin-top : 0;
+}
+
+.sidebar-post-loop ul li.wp-block-post:last-child {
+ border-bottom : none;
+}
+
+.sidebar-post-title.wp-block-post-title {
+ margin-bottom : 3px !important;
+ margin-top : 0;
+ font-family : 'Poppins', sans-serif;
+}
+
+.sidebar-comment-posts {
+ padding-left : 0;
+}
+
+.sidebar-comment-posts li.wp-block-latest-comments__comment {
+ padding-bottom : 10px;
+ margin-bottom : 10px;
+ border-bottom : #CECECE 1px solid;
+}
+
+.sidebar-comment-posts li.wp-block-latest-comments__comment:last-child {
+ border-bottom : none;
+}
+
+.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {
+ font-size : 16px;
+ line-height : 24px;
+}
+
+.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta a {
+ text-decoration : none;
+ color : #3DA639;
+}
+
+.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt {
+ font-size : 16px;
+ line-height : 28px;
+ color : #767676;
+}
+
+.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt p {
+ margin-bottom : 0;
+}
+
+.sidebar-terms {
+ padding-left : 0;
+ margin-left: 0;
+ list-style : none;
+}
+
+.sidebar-terms li {
+ border-bottom : 1px solid #CECECE;
+ padding : 0 0 10px;
+ margin : 0 0 10px;
+}
+
+.sidebar-terms li:last-child {
+ border-bottom : none;
+}
+
+.sidebar-terms li a {
+ text-decoration : none;
+ color : #3DA639;
+}
+
+.main-post-loop ul li .wp-block-post-featured-image {
+ border : 1px solid #E1E1E1;
+}
+
+#more-blog-link a {
+ text-decoration : none;
+ color : #1D1D1D;
+}
+
+#wp--skip-link--target {
+ margin-top : 24px !important;
+}
+
+.blog-page--main-post-query .wp-block-post-featured-image {
+ margin-bottom: 15px;
+}
+
+
+.page-id-9688 .content--page .entry-header {
+ display: none;
+}
+
+.special-sep {
+ position : relative;
+}
+
+.special-sep:before {
+ content : '';
+ top : 50%;
+ left : 0;
+ right : 0;
+ height : 1px;
+ content : '';
+ position : absolute;
+ background-color : #000000;
+}
+
+.special-sep:after {
+ content : '';
+ left : calc(50% - 80px / 2);
+ width : 80px;
+ content : '';
+ position : absolute;
+ height : 40px;
+ top : -20px;
+ background : #FFFFFF url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4yIiBiYXNlUHJvZmlsZT0idGlueSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoJIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjU2IDI0OCIgb3ZlcmZsb3c9InZpc2libGUiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPHBhdGggZmlsbD0ibm9uZSIgZD0iTTE0My4zLDE2Ny44YzIyLjEtOC41LDMzLjEtMzMuMiwyNC42LTU1LjNjLTguNS0yMi4xLTMzLjItMzMuMS01NS4zLTI0LjZjLTIyLjEsOC41LTMzLjEsMzMuMi0yNC42LDU1LjMNCgkJYzQuMywxMS4zLDEzLjMsMjAuMywyNC42LDI0LjZMODMuOCwyNDNjLTYzLjUtMjQuNC05NS4zLTk1LjctNzAuOS0xNTkuM3M5NS43LTk1LjMsMTU5LjItNzAuOXM5NS4zLDk1LjcsNzAuOSwxNTkuMw0KCQljLTEyLjUsMzIuNi0zOC4zLDU4LjQtNzAuOSw3MC45TDE0My4zLDE2Ny44Ii8+DQoJPHBhdGggZmlsbD0iIzIzMUYyMCIgZD0iTTE3Mi4yLDI0Ny40Yy0wLjYsMC0xLjItMC4xLTEuOC0wLjRjLTEuMS0wLjUtMS45LTEuNC0yLjMtMi40bC0yOC44LTc1LjFjLTAuOS0yLjMsMC4zLTQuOCwyLjUtNS43DQoJCWM5LjYtMy43LDE3LjEtMTAuOSwyMS4zLTIwLjJjNC4yLTkuNCw0LjQtMTkuOCwwLjgtMjkuNEMxNTYuMiw5NC4zLDEzNCw4NC40LDExNC4yLDkyYy0xOS44LDcuNi0yOS43LDI5LjgtMjIuMSw0OS42DQoJCWMzLjksMTAuMSwxMS45LDE4LjIsMjIuMSwyMi4xYzIuMywwLjksMy40LDMuNCwyLjUsNS43TDg4LDI0NC41Yy0wLjQsMS4xLTEuMywyLTIuMywyLjRjLTEuMSwwLjUtMi4zLDAuNS0zLjQsMC4xDQoJCWMtMzEuOC0xMi4yLTU3LTM2LjEtNzAuOS02Ny4zQy0yLjUsMTQ4LjctMy40LDExNCw4LjgsODIuMXMzNi4xLTU3LDY3LjItNzAuOXM2NS44LTE0LjgsOTcuNy0yLjZzNTcsMzYuMSw3MC45LDY3LjMNCgkJYzEzLjksMzEuMiwxNC44LDY1LjgsMi42LDk3LjdjLTEyLjksMzMuNy0zOS43LDYwLjUtNzMuNCw3My41QzE3My4yLDI0Ny4zLDE3Mi43LDI0Ny40LDE3Mi4yLDI0Ny40TDE3Mi4yLDI0Ny40eiBNMTI4LDguOQ0KCQljLTE2LjQsMC0zMi44LDMuNS00OC4zLDEwLjNjLTI5LDEyLjktNTEuMiwzNi40LTYyLjYsNjZjLTExLjQsMjkuNi0xMC41LDYxLjksMi40LDkwLjljMTIuMywyNy43LDM0LjIsNDkuMiw2MS45LDYxbDI1LjctNjcNCgkJYy0xMC42LTUuMi0xOC45LTE0LjMtMjMuMS0yNS40Yy05LjMtMjQuMywyLjktNTEuNywyNy4xLTYxYzI0LjMtOS4zLDUxLjYsMi45LDYxLDI3LjJjNC41LDExLjgsNC4yLDI0LjYtMC45LDM2LjENCgkJYy00LjUsMTAuMi0xMi4zLDE4LjItMjIuMiwyMy4xbDI1LjcsNjdjMjkuNS0xMi42LDUyLjgtMzYuNyw2NC4zLTY2LjdjMTEuNC0yOS42LDEwLjUtNjEuOS0yLjQtOTAuOWMtMTIuOS0yOS0zNi4zLTUxLjItNjYtNjIuNg0KCQlDMTU2LjgsMTEuNiwxNDIuMyw4LjksMTI4LDguOUwxMjgsOC45eiIvPg0KCTxwYXRoIGZpbGw9IiMyMzFGMjAiIGQ9Ik0yMzcuNywyMjkuNmMtMS42LDEuNi0yLjQsMy42LTIuNCw1LjhjMCwyLjMsMC44LDQuMywyLjQsNS45YzEuNiwxLjYsMy42LDIuNCw1LjksMi40DQoJCWMyLjMsMCw0LjItMC44LDUuOC0yLjRjMS42LTEuNiwyLjQtMy42LDIuNC01LjljMC0yLjItMC44LTQuMi0yLjQtNS44Yy0xLjYtMS43LTMuNi0yLjUtNS45LTIuNQ0KCQlDMjQxLjMsMjI3LjEsMjM5LjQsMjI3LjksMjM3LjcsMjI5LjZMMjM3LjcsMjI5LjZ6IE0yNTAuNywyNDIuNmMtMiwxLjktNC4zLDIuOC03LDIuOGMtMi44LDAtNS4yLTEtNy4xLTIuOQ0KCQljLTEuOS0xLjktMi45LTQuMy0yLjktNy4xYzAtMi45LDEtNS40LDMuMS03LjNjMi0xLjgsNC4zLTIuNyw2LjktMi43YzIuOCwwLDUuMSwxLDcuMSwyLjljMiwyLDIuOSw0LjMsMi45LDcuMQ0KCQlDMjUzLjcsMjM4LjIsMjUyLjcsMjQwLjYsMjUwLjcsMjQyLjZMMjUwLjcsMjQyLjZ6IE0yNDQuNSwyMzJjLTAuNC0wLjItMS0wLjItMS43LTAuMmgtMC43djMuMmgxLjFjMC43LDAsMS4yLTAuMSwxLjYtMC40DQoJCXMwLjYtMC43LDAuNi0xLjNDMjQ1LjQsMjMyLjcsMjQ1LjEsMjMyLjMsMjQ0LjUsMjMyTDI0NC41LDIzMnogTTIzOS4yLDI0MXYtMTEuMWMwLjcsMCwxLjcsMCwzLjEsMHMyLjEsMCwyLjMsMA0KCQljMC45LDAuMSwxLjYsMC4zLDIuMiwwLjZjMSwwLjUsMS41LDEuNCwxLjUsMi43YzAsMC45LTAuMywxLjYtMC44LDJjLTAuNSwwLjQtMS4yLDAuNy0xLjksMC43YzAuNywwLjEsMS4yLDAuNCwxLjYsMC42DQoJCWMwLjcsMC41LDEsMS40LDEsMi41djFjMCwwLjEsMCwwLjIsMCwwLjNzMCwwLjIsMC4xLDAuM2wwLjEsMC4zaC0yLjhjLTAuMS0wLjQtMC4xLTAuOS0wLjItMS41cy0wLjEtMS4xLTAuMi0xLjQNCgkJYy0wLjEtMC40LTAuNC0wLjctMC44LTAuOGMtMC4yLTAuMS0wLjUtMC4xLTEtMC4ybC0wLjYsMGgtMC42djMuOUgyMzkuMnoiLz4NCjwvZz4NCjwvc3ZnPg0K) no-repeat center;
+}
+
+.sidebar .wp-block-latest-posts__list li,
+.sidebar .wp-block-latest-comments li {
+ border-bottom: #CECECE 1px solid;
+ padding-bottom: 10px;
+ margin-bottom: 10px;
+ margin-top: 0;
+}
+.sidebar .wp-block-latest-posts__post-date,
+.sidebar .wp-block-post-date, .wp-block-coblocks-posts__date {
+ color: #1d1d1d;
+ font-size: 13px;
+ font-weight: 400;
+ margin: 0;
+}
+.sidebar .wp-block-latest-comments__comment-excerpt p,
+.sidebar .wp-block-latest-comments__comment-excerpt,
+.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt,
+.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {
+ margin: 0;
+ margin-left:0;
+ line-height: 28px;
+ color: #767676;
+}
+
+.blog .sidebar .wp-block-latest-posts__post-title,
+.blog .sidebar .wp-block-latest-comments__comment-link,
+.blog .sidebar .wp-block-latest-comments__comment-author,
+.blog .sidebar .widget_top-posts a,
+.blog .sidebar .widget_recent_entries,
+.blog .sidebar .widget_categories,
+.blog .sidebar #recentcomments {
+ font-weight: 400;
+ color: #3da639;
+ font-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.217), 16px);
+ line-height: 1.8;
+ text-decoration: none;
+}
+.blog .sidebar h2 {
+ font-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.435), 18px);
+}
+
+
+.blog .sidebar .wp-block-categories-list,
+.blog .sidebar .widget_top-posts ul,
+.blog .sidebar .widget_recent_entries,
+.blog .sidebar .widget_recent_entries ul,
+.blog .sidebar #recentcomments,
+.blog .sidebar .widget_categories ul {
+ padding-left: 0px;
+}
+.blog .sidebar .wp-block-categories-list li,
+.blog .sidebar .widget_top-posts li,
+.blog .sidebar .widget_recent_entries li,
+.blog .sidebar #recentcomments li,
+.blog .sidebar .widget_categories li {
+ list-style: none;
+ border-bottom: 1px solid #CECECE;
+ padding: 0 0 10px;
+ margin: 0 0 10px;
+}
-
+.blog .sidebar .wp-block-categories-list li a,
+.blog .sidebar .widget_recent_entries a,
+.blog .sidebar .widget_categories a,
+.blog .sidebar #recentcomments a {
+ text-decoration: none;
+ cursor: pointer !important;
+ line-height: 1.8;
+ font-weight: 400;
+ color: #3da639;
+}
+
+#comments ul.reaction-list {
+ list-style-type: none;
+}
+#comments ul.reaction-list li {
+ display:inline-block;
+ padding:0;
+ margin:0
+}
+#comments ul.reaction-list li .hide-name,
+#comments ul.reaction-list li .emoji-overlay {
+ display:none;
+}
+#comments ul.reaction-list li img {
+ width:50px;
+ max-width: auto;
+}
+#comments ul.reaction-list li a.customize-unpreviewable {
+ cursor:pointer !important;
+ display:inline-block;
+}
+.comment-body {
+ width: auto;
+}
+.email-block-wrap {
+ display:block;
+ width:100%;
+ clear:both;
+ margin-bottom: -5em;
+ z-index: 0;
+ position: relative;
+}
+.email-block-wrap input {
+ width: 50%;
+ display:inline-block;
+ border-radius: 0;
+ border: 2px black solid;
+ height: 60px;
+}
+.email-block-wrap span.wpcf7-not-valid-tip {
+ position: absolute;
+ color:white
+}
+.email-block-wrap span.wpcf7-form-control-wrap {
+ display: inline-block;
+ width:50%;
+}
+.email-block-wrap span.wpcf7-form-control-wrap input {
+ width:100%;
+}
+.email-block-wrap input[type=submit] {
+ background: black;
+ color:white;
+}
+.email-block-wrap input[type=submit]:hover {
+ background: white;
+ color: black;
+}
+.email-block-wrap input:hover,
+.email-block-wrap form.customize-unpreviewable input:hover,
+form.customize-unpreviewable {
+ cursor:pointer !important;
+}
+.email-block-wrap input[type=email]:hover,
+.email-block-wrap form.customize-unpreviewable input[type=email]:hover {
+ cursor:text !important;
+}
+.email-block-wrap .wpcf7-response-output {
+ border-color:white !important;
+ margin:10px 0 !important;
+ color:white;
+}
+.email-block-wrap form p {
+ margin: 0;
+ padding: 0;
+}
+.email-block-wrap {
+ z-index: 1;
+ border-bottom: 1px solid black;
+}
+.footer-cta {
+ z-index: 2;
+ position: relative;
+}
+.footer-main {
+ padding-top: 50px
+}
+.blog .first-post .post--byline {
+ font-size:clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);
+ color: gray;
+}
+.blog .first-post .entry-header ul {
+ list-style-type: none;
+ padding-left: 0;
+ margin-left: 0;
+ font-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);
+}
+
+.blog .first-post h2 {
+ font-size: 35px;
+ line-height: 45px;
+}
+
+.blog .content .content-full .content--page {
+ max-width: 1550px;
+}
+.syndication-links {
+ margin-top: 0;
+}
+@media only screen and (min-width: 600px) {
+ .archive-columns {
+ gap: 4%;
+ }
+}
+@media only screen and (min-width: 782px) {
+ .wp-block-column.two-column {
+ max-width: 46%;
+ min-width: 46%;
+ }
+}
+.blog .post-archive-wrap .wp-block-column h2,
+.archive h2.post--title.entry-title {
+ margin-top: 35px;
+ margin-bottom: 35px;
+ line-height: 37px;
+}
+.archive .entry-meta.post--byline a {
+ color: #767676;
+ font-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);
+}
+.error-404 label,
+.widget_top-posts .widget-inner > p {
+ display:none;
+}
+
+.archive .archive-title.page--title {
+ margin-bottom: 0.75rem !important;
+}
+
+.archive-description {
+ padding-bottom: 2.75rem;
+}
+
+.archive-description p {
+ margin: 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Skip to content
@@ -495,17 +783,19 @@
-
@@ -619,6 +905,7 @@ The MIT License
@@ -628,40 +915,52 @@ The MIT License