diff --git a/.github/ct/config.yaml b/.github/ct/config.yaml index 484e994cd51..9d1ae820466 100644 --- a/.github/ct/config.yaml +++ b/.github/ct/config.yaml @@ -11,6 +11,4 @@ helm-extra-args: --timeout 300s upgrade: true skip-missing-values: true release-label: release -chart-repos: - - bitnami=https://charts.bitnami.com/bitnami release-name-template: "helm-v{{ .Version }}" diff --git a/.github/workflows/chart-release.yaml b/.github/workflows/chart-release.yaml index ae2045ceb05..38c4fd2f56e 100644 --- a/.github/workflows/chart-release.yaml +++ b/.github/workflows/chart-release.yaml @@ -9,10 +9,14 @@ on: jobs: release: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: + - name: Add workspace as safe directory + run: | + git config --global --add safe.directory /__w/hapi-fhir-jpaserver-starter/hapi-fhir-jpaserver-starter + - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: fetch-depth: 0 @@ -21,14 +25,11 @@ jobs: git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - - name: Add bitnami repo - run: helm repo add bitnami https://charts.bitnami.com/bitnami - - name: Update dependencies run: find charts/ ! -path charts/ -maxdepth 1 -type d -exec helm dependency update {} \; - name: Run chart-releaser - uses: helm/chart-releaser-action@v1.2.0 + uses: helm/chart-releaser-action@be16258da8010256c6e82849661221415f031968 # v1.5.0 with: config: .github/ct/config.yaml env: diff --git a/.github/workflows/chart-test.yaml b/.github/workflows/chart-test.yaml index f4357fb35cc..30d29329dd7 100644 --- a/.github/workflows/chart-test.yaml +++ b/.github/workflows/chart-test.yaml @@ -9,8 +9,8 @@ on: jobs: lint: - runs-on: ubuntu-20.04 - container: quay.io/helmpack/chart-testing:v3.4.0 + runs-on: ubuntu-22.04 + container: quay.io/helmpack/chart-testing:v3.8.0@sha256:f058c660a28d99a9394ae081d98921efe068079531f247c86b8054e3c9d407aa steps: - name: Install helm-docs working-directory: /tmp @@ -22,11 +22,14 @@ jobs: chmod +x /usr/local/bin/helm-docs && \ helm-docs --version + - name: Add workspace as safe directory + run: | + git config --global --add safe.directory /__w/hapi-fhir-jpaserver-starter/hapi-fhir-jpaserver-starter + - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: fetch-depth: 0 - - name: Check if documentation is up-to-date run: helm-docs && git diff --exit-code HEAD @@ -34,20 +37,20 @@ jobs: run: ct lint --config .github/ct/config.yaml test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: - k8s-version: [1.22.9, 1.23.6, 1.24.1] + k8s-version: [1.25.9, 1.26.4, 1.27.2] needs: - lint steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: fetch-depth: 0 - name: Set up chart-testing - uses: helm/chart-testing-action@v2.2.1 + uses: helm/chart-testing-action@e8788873172cb653a90ca2e819d79d65a66d4e76 # v2.4.0 - name: Run chart-testing (list-changed) id: list-changed @@ -58,13 +61,12 @@ jobs: fi - name: Create k8s Kind Cluster - uses: helm/kind-action@v1.2.0 - if: steps.list-changed.outputs.changed == 'true' + uses: helm/kind-action@fa81e57adff234b2908110485695db0f181f3c67 # v1.7.0 + if: ${{ steps.list-changed.outputs.changed == 'true' }} with: - version: v0.14.0 cluster_name: kind-cluster-k8s-${{ matrix.k8s-version }} node_image: kindest/node:v${{ matrix.k8s-version }} - name: Run chart-testing (install) run: ct install --config .github/ct/config.yaml - if: steps.list-changed.outputs.changed == 'true' + if: ${{ steps.list-changed.outputs.changed == 'true' }} diff --git a/.gitignore b/.gitignore index 6e006b7fbc5..26e62463234 100644 --- a/.gitignore +++ b/.gitignore @@ -168,3 +168,6 @@ Temporary Items # Helm Chart dependencies **/charts/*.tgz + +# Visual Studio Code +.vscode diff --git a/Dockerfile b/Dockerfile index d7d3e816b05..8e6c8b065c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,24 @@ -FROM maven:3.8-openjdk-17-slim as build-hapi -WORKDIR /usr/app/hapi-fhir-jpaserver-starter +FROM docker.io/library/maven:3.9.2-eclipse-temurin-17 AS build-hapi +WORKDIR /tmp/hapi-fhir-jpaserver-starter -ARG OPENTELEMETRY_JAVA_AGENT_VERSION=1.17.0 +ARG OPENTELEMETRY_JAVA_AGENT_VERSION=1.26.0 RUN curl -LSsO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OPENTELEMETRY_JAVA_AGENT_VERSION}/opentelemetry-javaagent.jar COPY pom.xml . COPY server.xml . RUN mvn -ntp dependency:go-offline -COPY src/ /usr/app/hapi-fhir-jpaserver-starter/src/ +COPY src/ /tmp/hapi-fhir-jpaserver-starter/src/ RUN mvn clean install -DskipTests -Djdk.lang.Process.launchMechanism=vfork FROM build-hapi AS build-distroless RUN mvn package spring-boot:repackage -Pboot -RUN mkdir /app && cp /usr/app/hapi-fhir-jpaserver-starter/target/ROOT.war /app/main.war +RUN mkdir /app && cp /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /app/main.war ########### bitnami tomcat version is suitable for debugging and comes with a shell ########### it can be built using eg. `docker build --target tomcat .` -FROM bitnami/tomcat:9.0 as tomcat +FROM bitnami/tomcat:9.0 AS tomcat RUN rm -rf /opt/bitnami/tomcat/webapps/ROOT && \ mkdir -p /opt/bitnami/hapi/data/hapi/lucenefiles && \ @@ -30,13 +30,13 @@ USER 1001 COPY --chown=1001:1001 catalina.properties /opt/bitnami/tomcat/conf/catalina.properties COPY --chown=1001:1001 server.xml /opt/bitnami/tomcat/conf/server.xml -COPY --from=build-hapi --chown=1001:1001 /usr/app/hapi-fhir-jpaserver-starter/target/ROOT.war /opt/bitnami/tomcat/webapps/ROOT.war -COPY --from=build-hapi --chown=1001:1001 /usr/app/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app +COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /opt/bitnami/tomcat/webapps/ROOT.war +COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app ENV ALLOW_EMPTY_PASSWORD=yes ########### distroless brings focus on security and runs on plain spring boot - this is the default image -FROM gcr.io/distroless/java17-debian11:nonroot as default +FROM gcr.io/distroless/java17-debian11:nonroot AS default # 65532 is the nonroot user's uid # used here instead of the name to allow Kubernetes to easily detect that the container # is running as a non-root (uid != 0) user. @@ -44,6 +44,6 @@ USER 65532:65532 WORKDIR /app COPY --chown=nonroot:nonroot --from=build-distroless /app /app -COPY --chown=nonroot:nonroot --from=build-hapi /usr/app/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app +COPY --chown=nonroot:nonroot --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app -ENTRYPOINT ["java", "--class-path", "/app/main.war", "-Dloader.path=main.war!/WEB-INF/classes/,main.war!/WEB-INF/,/app/extra-classes", "org.springframework.boot.loader.PropertiesLauncher", "app/main.war"] +ENTRYPOINT ["java", "--class-path", "/app/main.war", "-Dloader.path=main.war!/WEB-INF/classes/,main.war!/WEB-INF/,/app/extra-classes", "org.springframework.boot.loader.PropertiesLauncher"] diff --git a/README.md b/README.md index e06d76042a1..1f41fe79ef3 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ Server will then be accessible at http://localhost:8080/ and eg. http://localhos ```bash mvn clean package spring-boot:repackage -Pboot && java -jar target/ROOT.war ``` -Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust you overlay configuration in the application.yaml to eg. +Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust your overlay configuration in the application.yaml to eg. ```yaml tester: @@ -250,7 +250,7 @@ Server will then be accessible at http://localhost:8080/ and eg. http://localhos ```bash mvn clean package com.google.cloud.tools:jib-maven-plugin:dockerBuild -Dimage=distroless-hapi && docker run -p 8080:8080 distroless-hapi ``` -Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust you overlay configuration in the application.yaml to eg. +Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust your overlay configuration in the application.yaml to eg. ```yaml tester: @@ -266,7 +266,7 @@ Server will then be accessible at http://localhost:8080/ and eg. http://localhos ```bash ./build-docker-image.sh && docker run -p 8080:8080 hapi-fhir/hapi-fhir-jpaserver-starter:latest ``` -Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust you overlay configuration in the application.yaml to eg. +Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust your overlay configuration in the application.yaml to eg. ```yaml tester: @@ -315,7 +315,7 @@ spring: # Then comment all hibernate.search.backend.* ``` -Because the integration tests within the project rely on the default H2 database configuration, it is important to either explicity skip the integration tests during the build process, i.e., `mvn install -DskipTests`, or delete the tests altogether. Failure to skip or delete the tests once you've configured PostgreSQL for the datasource.driver, datasource.url, and hibernate.dialect as outlined above will result in build errors and compilation failure. +Because the integration tests within the project rely on the default H2 database configuration, it is important to either explicitly skip the integration tests during the build process, i.e., `mvn install -DskipTests`, or delete the tests altogether. Failure to skip or delete the tests once you've configured PostgreSQL for the datasource.driver, datasource.url, and hibernate.dialect as outlined above will result in build errors and compilation failure. ### Microsoft SQL Server configuration @@ -330,14 +330,14 @@ spring: driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver ``` -Also, make sure you are not setting the Hibernate dialect explicitly, in other words remove any lines similar to: +Also, make sure you are not setting the Hibernate dialect explicitly, in other words, remove any lines similar to: ``` hibernate.dialect: {some none Microsoft SQL dialect} ``` -Because the integration tests within the project rely on the default H2 database configuration, it is important to either explicity skip the integration tests during the build process, i.e., `mvn install -DskipTests`, or delete the tests altogether. Failure to skip or delete the tests once you've configured PostgreSQL for the datasource.driver, datasource.url, and hibernate.dialect as outlined above will result in build errors and compilation failure. +Because the integration tests within the project rely on the default H2 database configuration, it is important to either explicitly skip the integration tests during the build process, i.e., `mvn install -DskipTests`, or delete the tests altogether. Failure to skip or delete the tests once you've configured PostgreSQL for the datasource.driver, datasource.url, and hibernate.dialect as outlined above will result in build errors and compilation failure. NOTE: MS SQL Server by default uses a case-insensitive codepage. This will cause errors with some operations - such as when expanding case-sensitive valuesets (UCUM) as there are unique indexes defined on the terminology tables for codes. @@ -381,7 +381,7 @@ Again, browse to the following link to use the server (note that the port 8080 m [http://localhost:8080/](http://localhost:8080/) -You will then be able access the JPA server e.g. using http://localhost:8080/fhir/metadata. +You will then be able to access the JPA server e.g. using http://localhost:8080/fhir/metadata. If you would like it to be hosted at eg. hapi-fhir-jpaserver, eg. http://localhost:8080/hapi-fhir-jpaserver/ or http://localhost:8080/hapi-fhir-jpaserver/fhir/metadata - then rename the WAR file to ```hapi-fhir-jpaserver.war``` and adjust the overlay configuration accordingly e.g. @@ -398,7 +398,7 @@ If you would like it to be hosted at eg. hapi-fhir-jpaserver, eg. http://localho ## Deploy with docker compose -Docker compose is a simple option to build and deploy container. To deploy with docker compose, you should build the project +Docker compose is a simple option to build and deploy containers. To deploy with docker compose, you should build the project with `mvn clean install` and then bring up the containers with `docker-compose up -d --build`. The server can be reached at http://localhost:8080/. diff --git a/charts/hapi-fhir-jpaserver/Chart.lock b/charts/hapi-fhir-jpaserver/Chart.lock index 5c8ec4a2289..98ba8480d43 100644 --- a/charts/hapi-fhir-jpaserver/Chart.lock +++ b/charts/hapi-fhir-jpaserver/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: postgresql - repository: https://charts.bitnami.com/bitnami - version: 12.1.2 -digest: sha256:525689611a29f90b0bc8cd674df5d97024c99eda8104216390f6747904fd0208 -generated: "2022-11-21T22:55:45.1699395+01:00" + repository: oci://registry-1.docker.io/bitnamicharts + version: 12.5.6 +digest: sha256:4d21dbc02bbdb55b957b0093e37376853727de82396abfadfaf1d738bd51b8e6 +generated: "2023-06-03T20:58:45.922102213+02:00" diff --git a/charts/hapi-fhir-jpaserver/Chart.yaml b/charts/hapi-fhir-jpaserver/Chart.yaml index 91580077790..a81e1082a60 100644 --- a/charts/hapi-fhir-jpaserver/Chart.yaml +++ b/charts/hapi-fhir-jpaserver/Chart.yaml @@ -7,17 +7,21 @@ sources: - https://github.com/hapifhir/hapi-fhir-jpaserver-starter dependencies: - name: postgresql - version: 12.1.2 - repository: https://charts.bitnami.com/bitnami + version: 12.5.6 + repository: oci://registry-1.docker.io/bitnamicharts condition: postgresql.enabled -appVersion: 6.2.2 -version: 0.11.1 +appVersion: 6.6.0 +version: 0.13.0 annotations: artifacthub.io/license: Apache-2.0 artifacthub.io/changes: | # When using the list of objects option the valid supported kinds are # added, changed, deprecated, removed, fixed, and security. + - kind: added + description: allow specifying application properties via yaml config + - kind: added + description: allow setting resource limits and requests for the Helm test pods - kind: changed - description: updated HAPI FHIR JPA Server app image version to v6.2.2 + description: updated curl used by helm tests to version to v8.2.0 - kind: changed - description: updated curl used by helm tests to version to v7.87.0 + description: allow disabling the liveness-, readiness-, and startup-probes entirely diff --git a/charts/hapi-fhir-jpaserver/README.md b/charts/hapi-fhir-jpaserver/README.md index 8b4b4619d43..7d4d338db4d 100644 --- a/charts/hapi-fhir-jpaserver/README.md +++ b/charts/hapi-fhir-jpaserver/README.md @@ -1,6 +1,6 @@ # HAPI FHIR JPA Server Starter Helm Chart -![Version: 0.11.1](https://img.shields.io/badge/Version-0.11.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.2.2](https://img.shields.io/badge/AppVersion-6.2.2-informational?style=flat-square) +![Version: 0.13.0](https://img.shields.io/badge/Version-0.13.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.6.0](https://img.shields.io/badge/AppVersion-6.6.0-informational?style=flat-square) This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. @@ -8,11 +8,14 @@ This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes e ```sh helm repo add hapifhir https://hapifhir.github.io/hapi-fhir-jpaserver-starter/ -helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver +helm install hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver ``` -> ⚠ By default, the included [PostgreSQL Helm chart](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#upgrading) -> auto-generates a random password for the database which may cause problems when upgrading the chart (see [here for details](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#upgrading)). +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| oci://registry-1.docker.io/bitnamicharts | postgresql | 12.5.6 | ## Values @@ -27,12 +30,13 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | externalDatabase.password | string | `""` | database password | | externalDatabase.port | int | `5432` | database port number | | externalDatabase.user | string | `"fhir"` | username for the external database | +| extraConfig | string | `""` | additional Spring Boot application config. Mounted as a file and automatically loaded by the application. | | extraEnv | list | `[]` | extra environment variables to set on the server container | | fullnameOverride | string | `""` | override the chart fullname | | image.pullPolicy | string | `"IfNotPresent"` | image pullPolicy to use | | image.registry | string | `"docker.io"` | registry where the HAPI FHIR server image is hosted | | image.repository | string | `"hapiproject/hapi"` | the path inside the repository | -| image.tag | string | `"v6.2.2@sha256:9c4e8af94d81ac0049dbb589e4cd855bf78c9c13be6f6844e814c63d63545b44"` | the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. | +| image.tag | string | `"v6.6.0@sha256:c00367865ae5dad4e171cbb68bfc1c39818854079d1565bee4c86a45e78335d0"` | the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. | | imagePullSecrets | list | `[]` | image pull secrets to use when pulling the image | | ingress.annotations | object | `{}` | provide any additional annotations which may be required. Evaluated as a template. | | ingress.enabled | bool | `false` | whether to create an Ingress to expose the FHIR server HTTP endpoint | @@ -40,11 +44,6 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | ingress.hosts[0].pathType | string | `"ImplementationSpecific"` | | | ingress.hosts[0].paths[0] | string | `"/"` | | | ingress.tls | list | `[]` | ingress TLS config | -| livenessProbe.failureThreshold | int | `5` | | -| livenessProbe.initialDelaySeconds | int | `30` | | -| livenessProbe.periodSeconds | int | `20` | | -| livenessProbe.successThreshold | int | `1` | | -| livenessProbe.timeoutSeconds | int | `30` | | | metrics.service.port | int | `8081` | | | metrics.serviceMonitor.additionalLabels | object | `{}` | additional labels to apply to the ServiceMonitor object, e.g. `release: prometheus` | | metrics.serviceMonitor.enabled | bool | `false` | if enabled, creates a ServiceMonitor instance for Prometheus Operator-based monitoring | @@ -62,11 +61,6 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | postgresql.primary.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | | postgresql.primary.containerSecurityContext.runAsNonRoot | bool | `true` | | | postgresql.primary.containerSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | | -| readinessProbe.failureThreshold | int | `5` | | -| readinessProbe.initialDelaySeconds | int | `30` | | -| readinessProbe.periodSeconds | int | `20` | | -| readinessProbe.successThreshold | int | `1` | | -| readinessProbe.timeoutSeconds | int | `20` | | | replicaCount | int | `1` | number of replicas to deploy | | resources | object | `{}` | configure the FHIR server's resource requests and limits | | securityContext.allowPrivilegeEscalation | bool | `false` | | @@ -79,18 +73,14 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | securityContext.seccompProfile.type | string | `"RuntimeDefault"` | | | service.port | int | `8080` | port where the server will be exposed at | | service.type | string | `"ClusterIP"` | service type | -| startupProbe.failureThreshold | int | `10` | | -| startupProbe.initialDelaySeconds | int | `30` | | -| startupProbe.periodSeconds | int | `30` | | -| startupProbe.successThreshold | int | `1` | | -| startupProbe.timeoutSeconds | int | `30` | | +| tests.resources | object | `{}` | configure the test pods resource requests and limits | | tolerations | list | `[]` | pod tolerations | | topologySpreadConstraints | list | `[]` | pod topology spread configuration see: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#api | ## Development To update the Helm chart when a new version of the `hapiproject/hapi` image is released, [values.yaml](values.yaml) `image.tag` and the [Chart.yaml](Chart.yaml)'s -`version` and optionally the `appVersion` field on major releases need to be updated. Afterwards, re-generate the [README.md](README.md) +`version` and optionally the `appVersion` field need to be updated. Afterwards, re-generate the [README.md](README.md) by running: ```sh diff --git a/charts/hapi-fhir-jpaserver/README.md.gotmpl b/charts/hapi-fhir-jpaserver/README.md.gotmpl index bfea0325fe1..46473954b59 100644 --- a/charts/hapi-fhir-jpaserver/README.md.gotmpl +++ b/charts/hapi-fhir-jpaserver/README.md.gotmpl @@ -8,18 +8,17 @@ This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes e ```sh helm repo add hapifhir https://hapifhir.github.io/hapi-fhir-jpaserver-starter/ -helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver +helm install hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver ``` -> ⚠ By default, the included [PostgreSQL Helm chart](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#upgrading) -> auto-generates a random password for the database which may cause problems when upgrading the chart (see [here for details](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#upgrading)). +{{ template "chart.requirementsSection" . }} {{ template "chart.valuesSection" . }} ## Development To update the Helm chart when a new version of the `hapiproject/hapi` image is released, [values.yaml](values.yaml) `image.tag` and the [Chart.yaml](Chart.yaml)'s -`version` and optionally the `appVersion` field on major releases need to be updated. Afterwards, re-generate the [README.md](README.md) +`version` and optionally the `appVersion` field need to be updated. Afterwards, re-generate the [README.md](README.md) by running: ```sh diff --git a/charts/hapi-fhir-jpaserver/ci/extra-config-values.yaml b/charts/hapi-fhir-jpaserver/ci/extra-config-values.yaml new file mode 100644 index 00000000000..d2406ac2142 --- /dev/null +++ b/charts/hapi-fhir-jpaserver/ci/extra-config-values.yaml @@ -0,0 +1,17 @@ +extraConfig: | + hapi: + fhir: + cr_enabled: true + tester: + home: + name: Hello HAPI FHIR + server_address: "http://fhir-server.127.0.0.1.nip.io/fhir" + refuse_to_fetch_third_party_urls: true + fhir_version: R4 + +ingress: + enabled: true + hosts: + - host: fhir-server.127.0.0.1.nip.io + pathType: ImplementationSpecific + paths: ["/"] diff --git a/charts/hapi-fhir-jpaserver/templates/application-config.yaml b/charts/hapi-fhir-jpaserver/templates/application-config.yaml new file mode 100644 index 00000000000..e4df9cea30f --- /dev/null +++ b/charts/hapi-fhir-jpaserver/templates/application-config.yaml @@ -0,0 +1,11 @@ +{{- if .Values.extraConfig -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "hapi-fhir-jpaserver.fullname" . }}-application-config + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} +data: + application-extra.yaml: |- + {{ .Values.extraConfig | nindent 4 }} +{{- end }} diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index 8f3c65e3137..c15609f443d 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -30,7 +30,7 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} initContainers: - name: wait-for-db-to-be-ready - image: docker.io/bitnami/postgresql:15.1.0-debian-11-r0@sha256:27915588d5203a10a1c23624d9c81644437f33b7c224e25f79bcd9bd09bbb8e2 + image: docker.io/bitnami/postgresql:15.3.0-debian-11-r7@sha256:cc301eef743685f4f69d1d719853988e8a9650c90fd9521f4742ce400b3fdf6a imagePullPolicy: IfNotPresent {{- with .Values.restrictedContainerSecurityContext }} securityContext: @@ -63,38 +63,17 @@ spec: - name: http-metrics containerPort: 8081 protocol: TCP - startupProbe: - httpGet: - path: /readyz - port: http {{- with .Values.startupProbe }} - initialDelaySeconds: {{ .initialDelaySeconds }} - periodSeconds: {{ .periodSeconds }} - timeoutSeconds: {{ .timeoutSeconds }} - successThreshold: {{ .successThreshold }} - failureThreshold: {{ .failureThreshold }} - {{- end }} - readinessProbe: - httpGet: - path: /readyz - port: http - {{- with .Values.readinessProbe }} - initialDelaySeconds: {{ .initialDelaySeconds }} - periodSeconds: {{ .periodSeconds }} - timeoutSeconds: {{ .timeoutSeconds }} - successThreshold: {{ .successThreshold }} - failureThreshold: {{ .failureThreshold }} + startupProbe: + {{- toYaml . | nindent 12 }} {{- end }} - livenessProbe: - httpGet: - path: /livez - port: http {{- with .Values.livenessProbe }} - initialDelaySeconds: {{ .initialDelaySeconds }} - periodSeconds: {{ .periodSeconds }} - timeoutSeconds: {{ .timeoutSeconds }} - successThreshold: {{ .successThreshold }} - failureThreshold: {{ .failureThreshold }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} @@ -118,6 +97,10 @@ spec: value: "true" - name: MANAGEMENT_SERVER_PORT value: "8081" + {{- if .Values.extraConfig }} + - name: SPRING_CONFIG_IMPORT + value: "/app/config/application-extra.yaml" + {{- end }} {{- if .Values.extraEnv }} {{ toYaml .Values.extraEnv | nindent 12 }} {{- end }} @@ -126,6 +109,12 @@ spec: name: tmp-volume - mountPath: /app/target name: lucenefiles-volume + {{- if .Values.extraConfig }} + - name: application-extra-config + mountPath: /app/config/application-extra.yaml + readOnly: true + subPath: application-extra.yaml + {{- end }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} @@ -147,3 +136,8 @@ spec: emptyDir: {} - name: lucenefiles-volume emptyDir: {} + {{- if .Values.extraConfig }} + - name: application-extra-config + configMap: + name: {{ include "hapi-fhir-jpaserver.fullname" . }}-application-config + {{- end }} diff --git a/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml b/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml index 034efb12e02..bd81c4aa450 100644 --- a/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml +++ b/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml @@ -11,20 +11,17 @@ spec: restartPolicy: Never containers: - name: test-metadata-endpoint - image: docker.io/curlimages/curl:7.87.0@sha256:f7f265d5c64eb4463a43a99b6bf773f9e61a50aaa7cefaf564f43e42549a01dd + image: "{{ .Values.curl.image.registry }}/{{ .Values.curl.image.repository }}:{{ .Values.curl.image.tag }}" command: ["curl", "--fail-with-body"] args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/metadata?_summary=true"] {{- with .Values.restrictedContainerSecurityContext }} securityContext: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.tests.resources }} resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi + {{- toYaml . | nindent 8 }} + {{- end }} livenessProbe: exec: command: ["true"] @@ -32,20 +29,17 @@ spec: exec: command: ["true"] - name: test-patient-endpoint - image: docker.io/curlimages/curl:7.87.0@sha256:f7f265d5c64eb4463a43a99b6bf773f9e61a50aaa7cefaf564f43e42549a01dd + image: "{{ .Values.curl.image.registry }}/{{ .Values.curl.image.repository }}:{{ .Values.curl.image.tag }}" command: ["curl", "--fail-with-body"] args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/Patient?_count=1&_summary=true"] {{- with .Values.restrictedContainerSecurityContext }} securityContext: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.tests.resources }} resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi + {{- toYaml . | nindent 8 }} + {{- end }} livenessProbe: exec: command: ["true"] @@ -53,20 +47,17 @@ spec: exec: command: ["true"] - name: test-metrics-endpoint - image: docker.io/curlimages/curl:7.87.0@sha256:f7f265d5c64eb4463a43a99b6bf773f9e61a50aaa7cefaf564f43e42549a01dd + image: "{{ .Values.curl.image.registry }}/{{ .Values.curl.image.repository }}:{{ .Values.curl.image.tag }}" command: ["curl", "--fail-with-body"] args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.metrics.service.port }}/actuator/prometheus"] {{- with .Values.restrictedContainerSecurityContext }} securityContext: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.tests.resources }} resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi + {{- toYaml . | nindent 8 }} + {{- end }} livenessProbe: exec: command: ["true"] diff --git a/charts/hapi-fhir-jpaserver/values.yaml b/charts/hapi-fhir-jpaserver/values.yaml index be02b18983b..9e9c18746b3 100644 --- a/charts/hapi-fhir-jpaserver/values.yaml +++ b/charts/hapi-fhir-jpaserver/values.yaml @@ -7,7 +7,7 @@ image: # -- the path inside the repository repository: hapiproject/hapi # -- the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. - tag: "v6.2.2@sha256:9c4e8af94d81ac0049dbb589e4cd855bf78c9c13be6f6844e814c63d63545b44" + tag: "v6.6.0@sha256:c00367865ae5dad4e171cbb68bfc1c39818854079d1565bee4c86a45e78335d0" # -- image pullPolicy to use pullPolicy: IfNotPresent @@ -131,24 +131,39 @@ postgresql: seccompProfile: type: RuntimeDefault +# -- readiness probe +# @ignored readinessProbe: + httpGet: + path: /readyz + port: http failureThreshold: 5 initialDelaySeconds: 30 periodSeconds: 20 successThreshold: 1 timeoutSeconds: 20 -startupProbe: - failureThreshold: 10 +# -- liveness probe +# @ignored +livenessProbe: + httpGet: + path: /livez + port: http + failureThreshold: 5 initialDelaySeconds: 30 - periodSeconds: 30 + periodSeconds: 20 successThreshold: 1 timeoutSeconds: 30 -livenessProbe: - failureThreshold: 5 +# -- startup probe +# @ignored +startupProbe: + httpGet: + path: /readyz + port: http + failureThreshold: 10 initialDelaySeconds: 30 - periodSeconds: 20 + periodSeconds: 30 successThreshold: 1 timeoutSeconds: 30 @@ -208,3 +223,32 @@ restrictedContainerSecurityContext: runAsGroup: 65534 seccompProfile: type: RuntimeDefault + +# @ignored +curl: + image: + registry: docker.io + repository: curlimages/curl + tag: 8.2.0@sha256:daf3f46a2639c1613b25e85c9ee4193af8a1d538f92483d67f9a3d7f21721827 + +tests: + # -- configure the test pods resource requests and limits + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# -- additional Spring Boot application config. Mounted as a file and automatically loaded by the application. +extraConfig: "" + # # For example: + # | + # hapi: + # fhir: + # implementationguides: + # gh_0_1_0: + # url: https://build.fhir.org/ig/hl7-eu/gravitate-health/package.tgz + # name: hl7.eu.fhir.gh + # version: 0.1.0 diff --git a/docker-compose.yml b/docker-compose.yml index dfdb523cea6..bf1daebf8c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,15 +8,20 @@ services: - "8080:8080" environment: fhir_version: 'R4' - spring.datasource.url: 'jdbc:postgresql://hapi-fhir-postgres:5432/hapi' spring.datasource.username: admin spring.datasource.password: admin + spring.config.location: classpath:/application-custom.yaml + + # Enable these for MySQL + # spring.datasource.url: 'jdbc:mysql://hapi-fhir-mysql:3306/hapi' + # spring.datasource.driverClassName: com.mysql.jdbc.Driver + # spring.jpa.properties.hibernate.dialect: org.hibernate.dialect.MySQL5InnoDBDialect + + # Enable these for PostgreSQL + spring.datasource.url: 'jdbc:postgresql://hapi-fhir-postgres:5432/hapi' spring.datasource.driverClassName: org.postgresql.Driver spring.jpa.properties.hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect - spring.config.location: classpath:/application-custom.yaml - reuse_cached_search_results_millis: 0 - subscription.resthook.enabled: true - subscription.websocket.enabled: true + OAUTH_ENABLED: true OAUTH_CLIENT_ID: fhir4-api OAUTH_USER_ROLE: fhir4-user @@ -32,6 +37,20 @@ services: SMART_INTROSPECTION_URL: https://auth-internal.elimuinformatics.com/auth/realms/product/protocol/openid-connect/token/introspect SMART_REVOCATION_URL: https://auth-internal.elimuinformatics.com/auth/realms/product/protocol/openid-connect/revoke SMART_MANAGE_URL: https://auth-internal.elimuinformatics.com/auth/realms/product/account + # hapi-fhir-mysql: + # platform: linux/x86_64 + # image: mysql:5.7 + # container_name: hapi-fhir-mysql + # restart: always + # environment: + # MYSQL_DATABASE: "hapi" + # MYSQL_USER: "admin" + # MYSQL_PASSWORD: "admin" + # MYSQL_ROOT_PASSWORD: "admin" + # ports: + # - '3306:3306' + # volumes: + # - hapi-fhir-mysql:/var/lib/mysql hapi-fhir-postgres: image: postgres:13-alpine container_name: hapi-fhir-postgres @@ -43,4 +62,5 @@ services: volumes: - hapi-fhir-postgres:/var/lib/postgresql/data volumes: + # hapi-fhir-mysql: hapi-fhir-postgres: diff --git a/pom.xml b/pom.xml index 9d469b1e01e..662fcd6e65b 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.6.0 + 6.8.0 hapi-fhir-jpaserver-starter @@ -25,7 +25,6 @@ 1.2.11 1.7.25 7.2 - 2.14.1 @@ -90,7 +89,6 @@ com.fasterxml.jackson.core jackson-databind - ${jackson.version} @@ -267,6 +265,12 @@ + + ca.uhn.hapi.fhir + hapi-fhir-jpaserver-test-utilities + ${project.version} + test + org.eclipse.jetty jetty-servlets @@ -407,7 +411,7 @@ ${logback-classic.version} - + com.auth0 java-jwt @@ -428,8 +432,6 @@ json-path 2.8.0 - - diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 21fdeaf057d..0da0bb8d672 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -4,17 +4,19 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings.ClientIdStrategyEnum; import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel; +import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; import ca.uhn.fhir.rest.api.EncodingEnum; -import com.google.common.collect.ImmutableList; import org.hl7.fhir.r4.model.Bundle; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; @ConfigurationProperties(prefix = "hapi.fhir") @Configuration @@ -33,6 +35,7 @@ public class AppProperties { private Boolean allow_multiple_delete = false; private Boolean allow_override_default_search_params = true; private Boolean auto_create_placeholder_reference_targets = false; + private final Set auto_version_reference_at_paths = new HashSet<>(); private Boolean dao_scheduling_enabled = true; private Boolean delete_expunge_enabled = false; private Boolean enable_index_missing_fields = false; @@ -75,9 +78,11 @@ public class AppProperties { private Partitioning partitioning = null; private Boolean install_transitive_ig_dependencies = true; private Boolean reload_existing_implementationguides = false; - private Map implementationGuides = null; + private Map implementationGuides = null; - private String staticLocation = null; + private String staticLocation = null; + + private String staticLocationPrefix = "/static"; private Boolean lastn_enabled = false; private boolean store_resource_in_lucene_index_enabled = false; @@ -88,33 +93,39 @@ public class AppProperties { private Integer bundle_batch_pool_size = 20; private Integer bundle_batch_pool_max_size = 100; - private final List local_base_urls = new ArrayList<>(); + private final Set local_base_urls = new HashSet<>(); private final List custom_interceptor_classes = new ArrayList<>(); - public List getCustomInterceptorClasses() { - return custom_interceptor_classes; + public String getStaticLocationPrefix() { + return staticLocationPrefix; } + public void setStaticLocationPrefix(String staticLocationPrefix) { + this.staticLocationPrefix = staticLocationPrefix; + } - public String getStaticLocation() { - return staticLocation; - } + public List getCustomInterceptorClasses() { + return custom_interceptor_classes; + } - public void setStaticLocation(String staticLocation) { - this.staticLocation = staticLocation; - } + public String getStaticLocation() { + return staticLocation; + } + public void setStaticLocation(String staticLocation) { + this.staticLocation = staticLocation; + } - public Boolean getOpenapi_enabled() { - return openapi_enabled; - } + public Boolean getOpenapi_enabled() { + return openapi_enabled; + } - public void setOpenapi_enabled(Boolean openapi_enabled) { - this.openapi_enabled = openapi_enabled; - } + public void setOpenapi_enabled(Boolean openapi_enabled) { + this.openapi_enabled = openapi_enabled; + } - public Boolean getUse_apache_address_strategy() { + public Boolean getUse_apache_address_strategy() { return use_apache_address_strategy; } @@ -138,11 +149,11 @@ public void setDefer_indexing_for_codesystems_of_size(Integer defer_indexing_for this.defer_indexing_for_codesystems_of_size = defer_indexing_for_codesystems_of_size; } - public Map getImplementationGuides() { + public Map getImplementationGuides() { return implementationGuides; } - public void setImplementationGuides(Map implementationGuides) { + public void setImplementationGuides(Map implementationGuides) { this.implementationGuides = implementationGuides; } @@ -163,11 +174,11 @@ public void setCr_enabled(Boolean cr_enabled) { } public Boolean getIps_enabled() { - return ips_enabled; + return ips_enabled; } public void setIps_enabled(Boolean ips_enabled) { - this.ips_enabled = ips_enabled; + this.ips_enabled = ips_enabled; } @@ -259,7 +270,7 @@ public void setSmart(Smart smart) { this.smart = smart; } - public Logger getLogger() { + public Logger getLogger() { return logger; } @@ -276,15 +287,15 @@ public void setClient_id_strategy( this.client_id_strategy = client_id_strategy; } - public boolean getAdvanced_lucene_indexing() { - return this.advanced_lucene_indexing; - } + public boolean getAdvanced_lucene_indexing() { + return this.advanced_lucene_indexing; + } - public void setAdvanced_lucene_indexing(boolean theAdvanced_lucene_indexing) { - advanced_lucene_indexing = theAdvanced_lucene_indexing; - } + public void setAdvanced_lucene_indexing(boolean theAdvanced_lucene_indexing) { + advanced_lucene_indexing = theAdvanced_lucene_indexing; + } - public Boolean getAllow_cascading_deletes() { + public Boolean getAllow_cascading_deletes() { return allow_cascading_deletes; } @@ -334,6 +345,10 @@ public void setAuto_create_placeholder_reference_targets( this.auto_create_placeholder_reference_targets = auto_create_placeholder_reference_targets; } + public Set getAuto_version_reference_at_paths() { + return auto_version_reference_at_paths; + } + public Integer getDefault_page_size() { return default_page_size; } @@ -366,23 +381,23 @@ public void setEnable_index_missing_fields(Boolean enable_index_missing_fields) this.enable_index_missing_fields = enable_index_missing_fields; } - public Boolean getEnable_index_contained_resource() { - return enable_index_contained_resource; - } + public Boolean getEnable_index_contained_resource() { + return enable_index_contained_resource; + } - public void setEnable_index_contained_resource(Boolean enable_index_contained_resource) { - this.enable_index_contained_resource = enable_index_contained_resource; - } + public void setEnable_index_contained_resource(Boolean enable_index_contained_resource) { + this.enable_index_contained_resource = enable_index_contained_resource; + } - public Boolean getEnable_repository_validating_interceptor() { - return enable_repository_validating_interceptor; - } + public Boolean getEnable_repository_validating_interceptor() { + return enable_repository_validating_interceptor; + } - public void setEnable_repository_validating_interceptor(Boolean theEnable_repository_validating_interceptor) { - enable_repository_validating_interceptor = theEnable_repository_validating_interceptor; - } + public void setEnable_repository_validating_interceptor(Boolean theEnable_repository_validating_interceptor) { + enable_repository_validating_interceptor = theEnable_repository_validating_interceptor; + } - public Boolean getEnforce_referential_integrity_on_delete() { + public Boolean getEnforce_referential_integrity_on_delete() { return enforce_referential_integrity_on_delete; } @@ -448,15 +463,15 @@ public void setBinary_storage_enabled(Boolean binary_storage_enabled) { this.binary_storage_enabled = binary_storage_enabled; } - public Integer getInline_resource_storage_below_size() { - return inline_resource_storage_below_size; - } + public Integer getInline_resource_storage_below_size() { + return inline_resource_storage_below_size; + } - public void setInline_resource_storage_below_size(Integer inline_resource_storage_below_size) { - this.inline_resource_storage_below_size = inline_resource_storage_below_size; - } + public void setInline_resource_storage_below_size(Integer inline_resource_storage_below_size) { + this.inline_resource_storage_below_size = inline_resource_storage_below_size; + } - public Boolean getBulk_export_enabled() { + public Boolean getBulk_export_enabled() { return bulk_export_enabled; } @@ -550,61 +565,61 @@ public void setLastn_enabled(Boolean lastn_enabled) { this.lastn_enabled = lastn_enabled; } - public boolean getStore_resource_in_lucene_index_enabled() { - return store_resource_in_lucene_index_enabled; - } + public boolean getStore_resource_in_lucene_index_enabled() { + return store_resource_in_lucene_index_enabled; + } - public void setStore_resource_in_lucene_index_enabled(Boolean store_resource_in_lucene_index_enabled) { - this.store_resource_in_lucene_index_enabled = store_resource_in_lucene_index_enabled; - } + public void setStore_resource_in_lucene_index_enabled(Boolean store_resource_in_lucene_index_enabled) { + this.store_resource_in_lucene_index_enabled = store_resource_in_lucene_index_enabled; + } - public NormalizedQuantitySearchLevel getNormalized_quantity_search_level() { - return this.normalized_quantity_search_level; + public NormalizedQuantitySearchLevel getNormalized_quantity_search_level() { + return this.normalized_quantity_search_level; } public void setNormalized_quantity_search_level(NormalizedQuantitySearchLevel normalized_quantity_search_level) { - this.normalized_quantity_search_level = normalized_quantity_search_level; + this.normalized_quantity_search_level = normalized_quantity_search_level; } - public boolean getInstall_transitive_ig_dependencies() { - return install_transitive_ig_dependencies; - } + public boolean getInstall_transitive_ig_dependencies() { + return install_transitive_ig_dependencies; + } - public void setInstall_transitive_ig_dependencies(boolean install_transitive_ig_dependencies) { - this.install_transitive_ig_dependencies = install_transitive_ig_dependencies; - } + public void setInstall_transitive_ig_dependencies(boolean install_transitive_ig_dependencies) { + this.install_transitive_ig_dependencies = install_transitive_ig_dependencies; + } - public boolean getReload_existing_implementationguides() { - return reload_existing_implementationguides; - } + public boolean getReload_existing_implementationguides() { + return reload_existing_implementationguides; + } - public void setReload_existing_implementationguides(boolean reload_existing_implementationguides) { - this.reload_existing_implementationguides = reload_existing_implementationguides; - } + public void setReload_existing_implementationguides(boolean reload_existing_implementationguides) { + this.reload_existing_implementationguides = reload_existing_implementationguides; + } - public Integer getBundle_batch_pool_size() { - return this.bundle_batch_pool_size; - } + public Integer getBundle_batch_pool_size() { + return this.bundle_batch_pool_size; + } - public void setBundle_batch_pool_size(Integer bundle_batch_pool_size) { - this.bundle_batch_pool_size = bundle_batch_pool_size; - } + public void setBundle_batch_pool_size(Integer bundle_batch_pool_size) { + this.bundle_batch_pool_size = bundle_batch_pool_size; + } - public Integer getBundle_batch_pool_max_size() { - return bundle_batch_pool_max_size; - } + public Integer getBundle_batch_pool_max_size() { + return bundle_batch_pool_max_size; + } - public void setBundle_batch_pool_max_size(Integer bundle_batch_pool_max_size) { - this.bundle_batch_pool_max_size = bundle_batch_pool_max_size; - } + public void setBundle_batch_pool_max_size(Integer bundle_batch_pool_max_size) { + this.bundle_batch_pool_max_size = bundle_batch_pool_max_size; + } - public List getLocal_base_urls() { - return local_base_urls; - } + public Set getLocal_base_urls() { + return local_base_urls; + } public static class Cors { private Boolean allow_Credentials = true; - private List allowed_origin = ImmutableList.of("*"); + private List allowed_origin = List.of("*"); public List getAllowed_origin() { return allowed_origin; @@ -856,36 +871,6 @@ public void setRefuse_to_fetch_third_party_urls(Boolean refuse_to_fetch_third_pa } } - public static class ImplementationGuide - { - private String url; - private String name; - private String version; - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - } public static class Validation { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ExtraStaticFilesConfigurer.java b/src/main/java/ca/uhn/fhir/jpa/starter/ExtraStaticFilesConfigurer.java index d875ec6a5d6..a3b528c0bda 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ExtraStaticFilesConfigurer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ExtraStaticFilesConfigurer.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.jpa.starter; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; @@ -14,25 +13,37 @@ @ConditionalOnProperty(prefix = "hapi.fhir", name = "staticLocation") public class ExtraStaticFilesConfigurer implements WebMvcConfigurer { - public static final String ROOT_CONTEXT_PATH = "/static"; - @Autowired - AppProperties appProperties; + private String staticLocation; + private String rootContextPath; - @Override + public ExtraStaticFilesConfigurer(AppProperties appProperties) { + + rootContextPath = appProperties.getStaticLocationPrefix(); + if(rootContextPath.endsWith("/")) + rootContextPath = rootContextPath.substring(0, rootContextPath.lastIndexOf('/')); + + staticLocation = appProperties.getStaticLocation(); + if(staticLocation.endsWith("/")) + staticLocation = staticLocation.substring(0, staticLocation.lastIndexOf('/')); + + } + + + @Override public void addResourceHandlers(ResourceHandlerRegistry theRegistry) { - theRegistry.addResourceHandler(ROOT_CONTEXT_PATH + "/**").addResourceLocations(appProperties.getStaticLocation()); + theRegistry.addResourceHandler(rootContextPath + "/**").addResourceLocations(staticLocation); } @Override public void addViewControllers(ViewControllerRegistry registry) { - String path = URI.create(appProperties.getStaticLocation()).getPath(); + String path = URI.create(staticLocation).getPath(); String lastSegment = path.substring(path.lastIndexOf('/') + 1); - registry.addViewController(ROOT_CONTEXT_PATH).setViewName("redirect:" + ROOT_CONTEXT_PATH + "/" + lastSegment + "/index.html"); + registry.addViewController(rootContextPath).setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html"); - registry.addViewController(ROOT_CONTEXT_PATH + "/*").setViewName("redirect:" + ROOT_CONTEXT_PATH + "/" + lastSegment + "/index.html"); + registry.addViewController(rootContextPath + "/*").setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html"); - registry.addViewController(ROOT_CONTEXT_PATH + "/" + lastSegment + "/").setViewName("redirect:" + ROOT_CONTEXT_PATH + "/" + lastSegment + "/index.html"); + registry.addViewController(rootContextPath + "/" + lastSegment + "/").setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html"); registry.setOrder(Ordered.HIGHEST_PRECEDENCE); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java index 38df9ec0c4b..648147cd20c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java @@ -9,7 +9,6 @@ import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.starter.AppProperties; import ca.uhn.fhir.jpa.starter.util.JpaHibernatePropertiesProvider; -import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory; import ca.uhn.fhir.jpa.subscription.match.deliver.email.EmailSenderImpl; import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender; import ca.uhn.fhir.rest.server.mail.IMailSvc; @@ -26,8 +25,6 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; -import java.util.HashSet; -import java.util.Optional; import java.util.stream.Collectors; /** @@ -49,6 +46,7 @@ public FhirServerConfigCommon(AppProperties appProperties) { ourLog.info("Server configured to " + (appProperties.getExpunge_enabled() ? "enable" : "disable") + " expunges"); ourLog.info("Server configured to " + (appProperties.getAllow_override_default_search_params() ? "allow" : "deny") + " overriding default search params"); ourLog.info("Server configured to " + (appProperties.getAuto_create_placeholder_reference_targets() ? "allow" : "disable") + " auto-creating placeholder references"); + ourLog.info("Server configured to auto-version references at paths {}", appProperties.getAuto_version_reference_at_paths()); if (appProperties.getSubscription().getEmail() != null) { AppProperties.Subscription.Email email = appProperties.getSubscription().getEmail(); @@ -86,8 +84,9 @@ public FhirServerConfigCommon(AppProperties appProperties) { public JpaStorageSettings jpaStorageSettings(AppProperties appProperties) { JpaStorageSettings jpaStorageSettings = new JpaStorageSettings(); - jpaStorageSettings.setIndexMissingFields(appProperties.getEnable_index_missing_fields() ? JpaStorageSettings.IndexEnabledEnum.ENABLED : JpaStorageSettings.IndexEnabledEnum.DISABLED); + jpaStorageSettings.setIndexMissingFields(appProperties.getEnable_index_missing_fields() ? StorageSettings.IndexEnabledEnum.ENABLED : StorageSettings.IndexEnabledEnum.DISABLED); jpaStorageSettings.setAutoCreatePlaceholderReferenceTargets(appProperties.getAuto_create_placeholder_reference_targets()); + jpaStorageSettings.setAutoVersionReferenceAtPaths(appProperties.getAuto_version_reference_at_paths()); jpaStorageSettings.setEnforceReferentialIntegrityOnWrite(appProperties.getEnforce_referential_integrity_on_write()); jpaStorageSettings.setEnforceReferentialIntegrityOnDelete(appProperties.getEnforce_referential_integrity_on_delete()); jpaStorageSettings.setAllowContainsSearches(appProperties.getAllow_contains_searches()); @@ -128,10 +127,10 @@ public JpaStorageSettings jpaStorageSettings(AppProperties appProperties) { } jpaStorageSettings.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); - jpaStorageSettings.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing()); - jpaStorageSettings.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls())); + jpaStorageSettings.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing()); + jpaStorageSettings.setTreatBaseUrlsAsLocal(appProperties.getLocal_base_urls()); - if (appProperties.getLastn_enabled()) { + if (appProperties.getLastn_enabled()) { jpaStorageSettings.setLastNEnabled(true); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java index cb28659d52c..c8c89251278 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.to.FhirTesterMvcConfig; import ca.uhn.fhir.to.TesterConfig; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -18,6 +19,7 @@ */ @Configuration @Import(FhirTesterMvcConfig.class) +@Conditional(FhirTesterConfigCondition.class) public class FhirTesterConfig { /** @@ -26,7 +28,7 @@ public class FhirTesterConfig { * server, as well as one public server. If you are creating a project to * deploy somewhere else, you might choose to only put your own server's * address here. - * + *

* Note the use of the ${serverBase} variable below. This will be replaced with * the base URL as reported by the server itself. Often for a simple Tomcat * (or other container) installation, this will end up being something diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfigCondition.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfigCondition.java new file mode 100644 index 00000000000..f5670d9c528 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfigCondition.java @@ -0,0 +1,16 @@ +package ca.uhn.fhir.jpa.starter.common; + +import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class FhirTesterConfigCondition implements Condition { + @Override + public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) { + + var properties = EnvironmentHelper.getPropertiesStartingWith((ConfigurableEnvironment) conditionContext.getEnvironment(), "hapi.fhir.tester"); + return !properties.isEmpty(); + } +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java index eaf5a9ae16b..ef83751ebd1 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; + import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.jpa.api.IDaoRegistry; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; @@ -17,13 +18,12 @@ import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider; -import ca.uhn.fhir.jpa.bulk.export.provider.BulkDataExportProvider; +import ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider; import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; import ca.uhn.fhir.jpa.config.util.ResourceCountCacheUtil; import ca.uhn.fhir.jpa.config.util.ValidationSupportConfigUtil; import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDaoJpaImpl; import ca.uhn.fhir.jpa.dao.search.HSearchSortHelperImpl; import ca.uhn.fhir.jpa.dao.search.IHSearchSortHelper; import ca.uhn.fhir.jpa.delete.ThreadSafeResourceDeleterSvc; @@ -47,12 +47,10 @@ import ca.uhn.fhir.jpa.starter.interceptor.CustomAuthorizationInterceptor; import ca.uhn.fhir.jpa.starter.interceptor.CustomConsentService; import ca.uhn.fhir.jpa.starter.interceptor.CustomSearchNarrowingInterceptor; -import ca.uhn.fhir.jpa.starter.ips.IpsConfigCondition; import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper; import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; import ca.uhn.fhir.jpa.util.ResourceCountCache; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; -import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.mdm.provider.MdmProviderLoader; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.narrative2.NullNarrativeGenerator; @@ -68,7 +66,6 @@ import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; @@ -84,7 +81,6 @@ import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; - import java.util.*; import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR; @@ -119,6 +115,8 @@ public CachingValidationSupport validationSupportChain(JpaValidationSupportChain @Autowired private ConfigurableEnvironment configurableEnvironment; + + /** * Customize the default/max page sizes for search results. You can set these however * you want, although very large page sizes will require a lot of RAM. @@ -200,13 +198,15 @@ public IPackageInstallerSvc packageInstaller(AppProperties appProperties, JobDef jobDefinitionRegistry.addJobDefinitionIfNotRegistered(reindexJobParametersJobDefinition); if (appProperties.getImplementationGuides() != null) { - Map guides = appProperties.getImplementationGuides(); - for (Map.Entry guide : guides.entrySet()) { - PackageInstallationSpec packageInstallationSpec = new PackageInstallationSpec().setPackageUrl(guide.getValue().getUrl()).setName(guide.getValue().getName()).setVersion(guide.getValue().getVersion()).setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); - packageInstallationSpec.setReloadExisting(appProperties.getReload_existing_implementationguides()); + Map guides = appProperties.getImplementationGuides(); + for (Map.Entry guidesEntry : guides.entrySet()) { + PackageInstallationSpec packageInstallationSpec = guidesEntry.getValue(); if (appProperties.getInstall_transitive_ig_dependencies()) { - packageInstallationSpec.setFetchDependencies(true); - packageInstallationSpec.setDependencyExcludes(ImmutableList.of("hl7.fhir.r2.core", "hl7.fhir.r3.core", "hl7.fhir.r4.core", "hl7.fhir.r5.core")); + + packageInstallationSpec.addDependencyExclude("hl7.fhir.r2.core") + .addDependencyExclude("hl7.fhir.r3.core") + .addDependencyExclude("hl7.fhir.r4.core") + .addDependencyExclude("hl7.fhir.r5.core"); } packageInstallerSvc.install(packageInstallationSpec); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java index f8874b31a50..edaade773a3 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java @@ -47,12 +47,12 @@ public RepositoryValidationInterceptorFactoryDstu3(RepositoryValidatingRuleBuild public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() { IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource"))); - Map> structureDefintions = results.getResources(0, results.size()) + Map> structureDefinitions = results.getResources(0, results.size()) .stream() .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); - structureDefintions.forEach((key, value) -> { + structureDefinitions.forEach((key, value) -> { String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new); repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); }); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrDstu3Config.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrDstu3Config.java index 9efc08645b9..06d95f4dd1e 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrDstu3Config.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrDstu3Config.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.starter.cr; -import ca.uhn.fhir.cr.config.CrDstu3Config; +import ca.uhn.fhir.cr.config.dstu3.CrDstu3Config; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrR4Config.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrR4Config.java index a97cea7835f..586a32499e5 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrR4Config.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrR4Config.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.starter.cr; -import ca.uhn.fhir.cr.config.CrR4Config; +import ca.uhn.fhir.cr.config.r4.CrR4Config; import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Import; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ips/StarterIpsConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/ips/StarterIpsConfig.java index e777879a580..308a3aafaf0 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ips/StarterIpsConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ips/StarterIpsConfig.java @@ -15,18 +15,18 @@ @Conditional(IpsConfigCondition.class) public class StarterIpsConfig { @Bean - IIpsGenerationStrategy IpsGenerationStrategy() + IIpsGenerationStrategy ipsGenerationStrategy() { return new DefaultIpsGenerationStrategy(); } @Bean - public IpsOperationProvider IpsOperationProvider(IIpsGeneratorSvc theIpsGeneratorSvc){ + public IpsOperationProvider ipsOperationProvider(IIpsGeneratorSvc theIpsGeneratorSvc){ return new IpsOperationProvider(theIpsGeneratorSvc); } @Bean - public IIpsGeneratorSvc IpsGeneratorSvcImpl(FhirContext theFhirContext, IIpsGenerationStrategy theGenerationStrategy, DaoRegistry theDaoRegistry) + public IIpsGeneratorSvc ipsGeneratorSvcImpl(FhirContext theFhirContext, IIpsGenerationStrategy theGenerationStrategy, DaoRegistry theDaoRegistry) { return new IpsGeneratorSvcImpl(theFhirContext, theGenerationStrategy, theDaoRegistry); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java index 50dfe9e0489..7f0ee4c314f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java @@ -6,7 +6,6 @@ import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator; import ca.uhn.fhir.mdm.rules.config.MdmSettings; -import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -17,6 +16,7 @@ import org.springframework.core.io.Resource; import java.io.IOException; +import java.nio.charset.StandardCharsets; @Configuration @Conditional(MdmConfigCondition.class) @@ -27,7 +27,7 @@ public class MdmConfig { IMdmSettings mdmSettings(@Autowired MdmRuleValidator theMdmRuleValidator, AppProperties appProperties) throws IOException { DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); Resource resource = resourceLoader.getResource("mdm-rules.json"); - String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8); + String json = IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8); return new MdmSettings(theMdmRuleValidator).setEnabled(appProperties.getMdm_enabled()).setScriptText(json); } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java index 41bb556c4c3..71d947ac410 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java @@ -17,7 +17,6 @@ import org.hibernate.search.mapper.orm.schema.management.SchemaManagementStrategyName; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy; -import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; diff --git a/src/main/resources/application-custom.yaml b/src/main/resources/application-custom.yaml index 1d44f9ec974..4a631e32af4 100644 --- a/src/main/resources/application-custom.yaml +++ b/src/main/resources/application-custom.yaml @@ -10,7 +10,6 @@ spring: allow-circular-references: true #allow-bean-definition-overriding: true banner-mode: "log" - batch.job.enabled: false flyway: enabled: false check-location: false @@ -29,6 +28,10 @@ spring: properties: hibernate.format_sql: false hibernate.show_sql: false + + #Hibernate dialect is automatically detected except Postgres and H2. + #If using H2, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect + #If using postgres, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect hibernate.dialect: ${hibernate.dialect} hibernate.hbm2ddl.auto: update hibernate.jdbc.batch_size: 20 @@ -56,12 +59,19 @@ hapi: openapi_enabled: true ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5 fhir_version: ${fhir_version} + ### This flag when enabled to true, will avail evaluate measure operations from CR Module. + ### Flag is false by default, can be passed as command line argument to override. + cr_enabled: "${CR_ENABLED: false}" ### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers ### to determine the FHIR server address # use_apache_address_strategy: false ### forces the use of the https:// protocol for the returned server address. ### alternatively, it may be set using the X-Forwarded-Proto header. # use_apache_address_strategy_https: false + ### enables the server to host content like HTML, css, etc. under the url pattern of eg. /static/** + # staticLocationPrefix: /static + ### the deepest folder level will be used. E.g. - if you put file:/foo/bar/bazz as value then the files are resolved under /static/bazz/** + #staticLocation: file:/foo/bar/bazz ### enable to set the Server URL # server_path: /baseR4 # server_address: http://hapi.fhir.org/baseR4 @@ -69,31 +79,39 @@ hapi: # install_transitive_ig_dependencies: true ### tells the server whether to attempt to load IG resources that are already present # reload_existing_implementationGuides : false - # implementationguides: + #implementationguides: ### example from registry (packages.fhir.org) - # swiss: - # name: swiss.mednet.fhir - # version: 0.8.0 + # swiss: + # name: swiss.mednet.fhir + # version: 0.8.0 + # reloadExisting : false # example not from registry # ips_1_0_0: - # url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz + # packageUrl: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz # name: hl7.fhir.uv.ips # version: 1.0.0 # supported_resource_types: # - Patient # - Observation + ################################################## + # Allowed Bundle Types for persistence (defaults are: COLLECTION,DOCUMENT,MESSAGE) + ################################################## + # allowed_bundle_types: COLLECTION,DOCUMENT,MESSAGE,TRANSACTION,TRANSACTIONRESPONSE,BATCH,BATCHRESPONSE,HISTORY,SEARCHSET allow_cascading_deletes: true allow_contains_searches: true allow_external_references: true allow_multiple_delete: true allow_override_default_search_params: true auto_create_placeholder_reference_targets: false - # cql_enabled: true + ### tells the server to automatically append the current version of the target resource to references at these paths + # auto_version_reference_at_paths: Device.patient, Device.location, Device.parent, DeviceMetric.parent, DeviceMetric.source, Observation.device, Observation.subject + # cr_enabled: true + # ips_enabled: false default_encoding: JSON # default_pretty_print: true default_page_size: 20 # delete_expunge_enabled: true - # enable_repository_validating_interceptor: false + # enable_repository_validating_interceptor: true # enable_index_missing_fields: false # enable_index_of_type: true # enable_index_contained_resource: false @@ -106,15 +124,14 @@ hapi: enforce_referential_integrity_on_write: false etag_support_enabled: true expunge_enabled: true - # daoconfig_client_id_strategy: null # client_id_strategy: ALPHANUMERIC # fhirpath_interceptor_enabled: false filter_search_enabled: true graphql_enabled: true # narrative_enabled: true # mdm_enabled: true -# local_base_urls: -# - https://hapi.fhir.org/baseR4 + # local_base_urls: + # - https://hapi.fhir.org/baseR4 mdm_enabled: false # partitioning: # allow_references_across_partitions: false @@ -130,9 +147,17 @@ hapi: search-coord-max-pool-size: 100 search-coord-queue-capacity: 200 + # comma-separated package names, will be @ComponentScan'ed by Spring to allow for creating custom Spring beans + #custom-bean-packages: + + # comma-separated list of fully qualified interceptor classes. + # classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages', + # or will be instantiated via reflection using an no-arg contructor; then registered with the server + #custom-interceptor-classes: + # Threadpool size for BATCH'ed GETs in a bundle. -# bundle_batch_pool_size: 10 -# bundle_batch_pool_max_size: 50 + # bundle_batch_pool_size: 10 + # bundle_batch_pool_max_size: 50 oauth: enabled: ${OAUTH_ENABLED:false} @@ -177,11 +202,12 @@ hapi: name: Global Tester server_address: "http://hapi.fhir.org/baseR4" refuse_to_fetch_third_party_urls: false - fhir_version: ${fhir_version} - # validation: - # requests_enabled: true - # responses_enabled: true + fhir_version: R4 + # validation: + # requests_enabled: true + # responses_enabled: true binary_storage_enabled: true + inline_resource_storage_below_size: 4000 bulk_export_enabled: true subscription: resthook_enabled: true diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index d8822766f2c..a77d91b18fb 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -58,13 +58,17 @@ hapi: openapi_enabled: true ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5 fhir_version: R4 + ### This flag when enabled to true, will avail evaluate measure operations from CR Module. + ### Flag is false by default, can be passed as command line argument to override. + cr_enabled: "${CR_ENABLED: false}" ### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers ### to determine the FHIR server address # use_apache_address_strategy: false ### forces the use of the https:// protocol for the returned server address. ### alternatively, it may be set using the X-Forwarded-Proto header. # use_apache_address_strategy_https: false - ### enables the server to host content like HTML, css, etc. under the url pattern of /static/** + ### enables the server to host content like HTML, css, etc. under the url pattern of eg. /static/** + # staticLocationPrefix: /static ### the deepest folder level will be used. E.g. - if you put file:/foo/bar/bazz as value then the files are resolved under /static/bazz/** #staticLocation: file:/foo/bar/bazz ### enable to set the Server URL @@ -74,14 +78,15 @@ hapi: # install_transitive_ig_dependencies: true ### tells the server whether to attempt to load IG resources that are already present # reload_existing_implementationGuides : false - # implementationguides: + #implementationguides: ### example from registry (packages.fhir.org) - # swiss: - # name: swiss.mednet.fhir - # version: 0.8.0 + # swiss: + # name: swiss.mednet.fhir + # version: 0.8.0 + # reloadExisting : false # example not from registry # ips_1_0_0: - # url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz + # packageUrl: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz # name: hl7.fhir.uv.ips # version: 1.0.0 # supported_resource_types: @@ -97,6 +102,8 @@ hapi: # allow_multiple_delete: true # allow_override_default_search_params: true # auto_create_placeholder_reference_targets: false + ### tells the server to automatically append the current version of the target resource to references at these paths + # auto_version_reference_at_paths: Device.patient, Device.location, Device.parent, DeviceMetric.parent, DeviceMetric.source, Observation.device, Observation.subject # cr_enabled: true # ips_enabled: false # default_encoding: JSON @@ -140,14 +147,14 @@ hapi: search-coord-core-pool-size: 20 search-coord-max-pool-size: 100 search-coord-queue-capacity: 200 - + # comma-separated package names, will be @ComponentScan'ed by Spring to allow for creating custom Spring beans #custom-bean-packages: - - # comma-separated list of fully qualified interceptor classes. - # classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages', - # or will be instantiated via reflection using an no-arg contructor; then registered with the server - #custom-interceptor-classes: + + # comma-separated list of fully qualified interceptor classes. + # classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages', + # or will be instantiated via reflection using an no-arg contructor; then registered with the server + #custom-interceptor-classes: # Threadpool size for BATCH'ed GETs in a bundle. # bundle_batch_pool_size: 10 diff --git a/src/main/webapp/WEB-INF/templates/tmpl-banner.html b/src/main/webapp/WEB-INF/templates/tmpl-banner.html index abbe718db63..cafc016f18f 100644 --- a/src/main/webapp/WEB-INF/templates/tmpl-banner.html +++ b/src/main/webapp/WEB-INF/templates/tmpl-banner.html @@ -3,18 +3,18 @@

- Sample Logo + Elimu Informatics
- - + +
Warning!

- +
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/CustomBeanTest.java b/src/test/java/ca/uhn/fhir/jpa/starter/CustomBeanTest.java index 07e7f581a30..fe3fba474dc 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/CustomBeanTest.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/CustomBeanTest.java @@ -11,7 +11,7 @@ // "hapi.fhir.enable_repository_validating_interceptor=true", "hapi.fhir.fhir_version=r4" }) -public class CustomBeanTest { +class CustomBeanTest { @Autowired some.custom.pkg1.CustomBean customBean1; diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/CustomInterceptorTest.java b/src/test/java/ca/uhn/fhir/jpa/starter/CustomInterceptorTest.java index 367857b7009..248bfe3600b 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/CustomInterceptorTest.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/CustomInterceptorTest.java @@ -21,7 +21,7 @@ "hapi.fhir.fhir_version=r4" }) -public class CustomInterceptorTest { +class CustomInterceptorTest { @LocalServerPort private int port; diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java index 5cb0a4e7072..6f358d49ed1 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java @@ -3,16 +3,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.search.lastn.ElasticsearchRestClientFactory; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; +import ca.uhn.fhir.jpa.test.config.TestElasticsearchContainerHelper; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import java.io.IOException; -import java.time.Duration; -import java.time.temporal.ChronoUnit; + import java.util.Date; import java.util.GregorianCalendar; +import java.util.List; import javax.annotation.PreDestroy; + +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.indices.PutIndexTemplateRequest; +import org.elasticsearch.common.settings.Settings; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.DateTimeType; @@ -34,8 +41,11 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; @ExtendWith(SpringExtension.class) +@Testcontainers @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class, JpaStarterWebsocketDispatcherConfig.class}, properties = { "spring.datasource.url=jdbc:h2:mem:dbr4", @@ -43,6 +53,7 @@ "hapi.fhir.lastn_enabled=true", "hapi.fhir.store_resource_in_lucene_index_enabled=true", "hapi.fhir.advanced_lucene_indexing=true", + "elasticsearch.enabled=true", // Because the port is set randomly, we will set the rest_url using the Initializer. // "elasticsearch.rest_url='http://localhost:9200'", @@ -61,19 +72,31 @@ public class ElasticsearchLastNR4IT { private IGenericClient ourClient; private FhirContext ourCtx; - private static final String ELASTIC_VERSION = "7.16.3"; - private static final String ELASTIC_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch:" + ELASTIC_VERSION; - private static ElasticsearchContainer embeddedElastic; + @Container + public static ElasticsearchContainer embeddedElastic = TestElasticsearchContainerHelper.getEmbeddedElasticSearch(); @Autowired private ElasticsearchSvcImpl myElasticsearchSvc; @BeforeAll - public static void beforeClass() { - embeddedElastic = new ElasticsearchContainer(ELASTIC_IMAGE).withStartupTimeout(Duration.of(300, ChronoUnit.SECONDS)); - embeddedElastic.start(); + public static void beforeClass() throws IOException { + //Given + RestHighLevelClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient( + "http", embeddedElastic.getHost() + ":" + embeddedElastic.getMappedPort(9200), "", ""); + + /* As of 2023-08-10, HAPI FHIR sets SubscriptionConstants.MAX_SUBSCRIPTION_RESULTS to 50000 + which is in excess of elastic's default max_result_window. If MAX_SUBSCRIPTION_RESULTS is changed + to a value <= 10000, the following will no longer be necessary. - dotasek + */ + PutIndexTemplateRequest putIndexTemplateRequest = new PutIndexTemplateRequest("hapi_fhir_template"); + putIndexTemplateRequest.patterns(List.of("*")); + Settings settings = Settings.builder().put("index.max_result_window", 50000).build(); + putIndexTemplateRequest.settings(settings); + elasticsearchHighLevelRestClient.indices().putTemplate(putIndexTemplateRequest, RequestOptions.DEFAULT); + + } - + @PreDestroy public void stop() { embeddedElastic.stop(); @@ -115,7 +138,7 @@ void testLastN() throws IOException, InterruptedException { } @BeforeEach - void beforeEach() { + void beforeEach() throws IOException { ourCtx = FhirContext.forR4(); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); @@ -123,6 +146,7 @@ void beforeEach() { String ourServerBase = "http://localhost:" + port + "/fhir/"; ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); + } static class Initializer diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java index 4369086733f..25776cc666a 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java @@ -21,7 +21,7 @@ "hapi.fhir.fhir_version=dstu2", "spring.datasource.url=jdbc:h2:mem:dbr2", }) -public class ExampleServerDstu2IT { +class ExampleServerDstu2IT { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class); private IGenericClient ourClient; diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java index 850bdb36c98..0d20bab4e62 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -49,7 +49,7 @@ }) -public class ExampleServerDstu3IT implements IServerSupport { +class ExampleServerDstu3IT implements IServerSupport { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class); private IGenericClient ourClient; @@ -71,8 +71,8 @@ void beforeEach() { ourClient.registerInterceptor(new LoggingInterceptor(true)); } - @Test - public void testCreateAndRead() { + @Test + void testCreateAndRead() { String methodName = "testCreateResourceConditional"; @@ -154,7 +154,7 @@ private Bundle loadBundle(String theLocation, FhirContext theCtx, IGenericClient } @Test - public void testWebsocketSubscription() throws Exception { + void testWebsocketSubscription() throws Exception { /* * Create subscription */ diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java index 9f19fcb74de..f6942c6848c 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java @@ -52,7 +52,7 @@ void testCreateAndRead() { @Test - public void testBatchPutWithIdenticalTags() { + void testBatchPutWithIdenticalTags() { String batchPuts = "{\n" + "\t\"resourceType\": \"Bundle\",\n" + "\t\"id\": \"patients\",\n" + diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java index 319ed1cd7a2..26f27f660b1 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.searchparam.config.NicknameServiceConfig; import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; @@ -30,7 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class, JpaStarterWebsocketDispatcherConfig.class}, properties = { +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class, JpaStarterWebsocketDispatcherConfig.class, NicknameServiceConfig.class}, properties = { "spring.datasource.url=jdbc:h2:mem:dbr4", "hapi.fhir.enable_repository_validating_interceptor=true", "hapi.fhir.fhir_version=r4", @@ -87,7 +88,7 @@ private Patient getGoldenResourcePatient() { } @Test - public void testBatchPutWithIdenticalTags() { + void testBatchPutWithIdenticalTags() { String batchPuts = "{\n" + "\t\"resourceType\": \"Bundle\",\n" + "\t\"id\": \"patients\",\n" + diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java index 8ecfb737462..349d883387b 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java @@ -45,15 +45,15 @@ public class ExampleServerR5IT { private IGenericClient ourClient; private FhirContext ourCtx; - public static final String SUBSCRIPTION_TOPIC_TEST_URL = "http://example.com/topic/test"; + public static final String SUBSCRIPTION_TOPIC_TEST_URL = "http://example.com/topic/test"; - @LocalServerPort + @LocalServerPort private int port; @Test - public void testCreateAndRead() { + void testCreateAndRead() { String methodName = "testCreateResourceConditional"; @@ -66,7 +66,7 @@ public void testCreateAndRead() { } @Test - public void testWebsocketSubscription() throws Exception { + void testWebsocketSubscription() throws Exception { String endpoint = "ws://localhost:" + port + "/websocket"; /* * Create topic diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java index 463a1c6c0ad..cf85478db9c 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java @@ -26,7 +26,7 @@ "hapi.fhir.partitioning.partitioning_include_in_search_hashes=false", }) -public class MultitenantServerR4IT { +class MultitenantServerR4IT { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class); @@ -40,7 +40,7 @@ public class MultitenantServerR4IT { @Test - public void testCreateAndReadInTenantA() { + void testCreateAndReadInTenantA() { // Create tenant A @@ -66,7 +66,7 @@ public void testCreateAndReadInTenantA() { } @Test - public void testCreateAndReadInTenantB() { + void testCreateAndReadInTenantB() { // Create tenant A diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java b/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java index 7734a23c6f2..4a536f0f2a1 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java @@ -18,7 +18,7 @@ public class SocketImplementation { private final String myCriteria; protected String myError; protected boolean myGotBound; - private final List myMessages = new ArrayList(); + private final List myMessages = new ArrayList<>(); protected int myPingCount; protected String mySubsId; private Session session; diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index 89229c9f0a1..9f473c574d8 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -64,9 +64,10 @@ hapi: # swiss: # name: swiss.mednet.fhir # version: 0.8.0 + # reloadExisting : false # example not from registry # ips_1_0_0: - # url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz + # packageUrl: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz # name: hl7.fhir.uv.ips # version: 1.0.0 # supported_resource_types: