From bb8f620d91f366849f8d348a9b45eb961bad5a18 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Wed, 14 Feb 2024 08:31:53 +0100 Subject: [PATCH 1/5] fix: reintroduced configurable condition on tester (#644) --- .../java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 7e2b679..6dc8ba1 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.jpa.starter.AppProperties; 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,7 +19,7 @@ import org.springframework.context.annotation.Import; */ @Configuration @Import(FhirTesterMvcConfig.class) -// @Conditional(FhirTesterConfigCondition.class) +@Conditional(FhirTesterConfigCondition.class) public class FhirTesterConfig { /** From 7e82e63fc2094849e23c2d9916d6b9a1dbad2038 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Wed, 14 Feb 2024 08:32:27 +0100 Subject: [PATCH 2/5] added missing uninstall (#643) --- .../ig/IImplementationGuideOperationProvider.java | 4 ++++ .../ig/ImplementationGuideR4OperationProvider.java | 9 +++++++++ .../ig/ImplementationGuideR5OperationProvider.java | 11 ++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ig/IImplementationGuideOperationProvider.java b/src/main/java/ca/uhn/fhir/jpa/starter/ig/IImplementationGuideOperationProvider.java index b35b1f6..13a659e 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ig/IImplementationGuideOperationProvider.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ig/IImplementationGuideOperationProvider.java @@ -20,5 +20,9 @@ public interface IImplementationGuideOperationProvider { // The following declaration is the one that counts but cannot be used across different versions as stating // Base64BinaryType would bind to a separate version // @Operation(name = "$install", typeName = "ImplementationGuide") + // Parameters install(@OperationParam(name = "npmContent",min = 1, max = 1) Base64BinaryType implementationGuide); + + // Parameters uninstall(@OperationParam(name = "name", min = 1, max = 1) String name, @OperationParam(name = "version", min = 1, max = 1) String version) ; + } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR4OperationProvider.java b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR4OperationProvider.java index 145924d..0c9405f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR4OperationProvider.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR4OperationProvider.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.starter.ig; import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; +import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -33,4 +34,12 @@ public class ImplementationGuideR4OperationProvider implements IImplementationGu } return new Parameters(); } + + @Operation(name = "$uninstall", typeName = "ImplementationGuide") + public Parameters uninstall( + @OperationParam(name = "name", min = 1, max = 1) String name, @OperationParam(name = "version", min = 1, max = 1) String version) { + + packageInstallerSvc.uninstall(new PackageInstallationSpec().setName(name).setVersion(version)); + return new Parameters(); + } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR5OperationProvider.java b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR5OperationProvider.java index 794bf72..50ba6b6 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR5OperationProvider.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR5OperationProvider.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.starter.ig; import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; +import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -13,7 +14,7 @@ import java.io.IOException; @Conditional({OnR5Condition.class, IgConfigCondition.class}) @Service -public class ImplementationGuideR5OperationProvider { +public class ImplementationGuideR5OperationProvider implements IImplementationGuideOperationProvider { IPackageInstallerSvc packageInstallerSvc; @@ -33,4 +34,12 @@ public class ImplementationGuideR5OperationProvider { } return new Parameters(); } + + @Operation(name = "$uninstall", typeName = "ImplementationGuide") + public org.hl7.fhir.r4.model.Parameters uninstall( + @OperationParam(name = "name", min = 1, max = 1) String name, @OperationParam(name = "version", min = 1, max = 1) String version) { + + packageInstallerSvc.uninstall(new PackageInstallationSpec().setName(name).setVersion(version)); + return new org.hl7.fhir.r4.model.Parameters(); + } } From 372ef4b6c9465027a2a0d193bcb88a2278178eea Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Wed, 14 Feb 2024 09:28:11 +0100 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20added=20`setLoadSynchronousUpTo`=20t?= =?UTF-8?q?o=20validation=20searches=20on=20Structu=E2=80=A6=20(#645)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: added `setLoadSynchronousUpTo` to validation searches on StructureDefinition * chore: adjusted to comments --- .../validation/RepositoryValidationInterceptorFactoryDstu3.java | 2 +- .../validation/RepositoryValidationInterceptorFactoryR4.java | 2 +- .../validation/RepositoryValidationInterceptorFactoryR4B.java | 2 +- .../validation/RepositoryValidationInterceptorFactoryR5.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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 f276b86..621616c 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,7 +47,7 @@ public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryV public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() { IBundleProvider results = structureDefinitionResourceProvider.search( - new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource"))); + new SearchParameterMap().setLoadSynchronous(true).add(StructureDefinition.SP_KIND, new TokenParam("resource"))); Map> structureDefinitions = results.getResources(0, results.size()).stream() .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java index b7df8b9..0487ae9 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java @@ -48,7 +48,7 @@ public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryVali public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() { IBundleProvider results = structureDefinitionResourceProvider.search( - new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource"))); + new SearchParameterMap().setLoadSynchronous(true).add(StructureDefinition.SP_KIND, new TokenParam("resource"))); Map> structureDefintions = results.getResources(0, results.size()).stream() .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4B.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4B.java index 6f6847e..f36bba6 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4B.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4B.java @@ -48,7 +48,7 @@ public class RepositoryValidationInterceptorFactoryR4B implements IRepositoryVal public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() { IBundleProvider results = structureDefinitionResourceProvider.search( - new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource"))); + new SearchParameterMap().setLoadSynchronous(true).add(StructureDefinition.SP_KIND, new TokenParam("resource"))); Map> structureDefintions = results.getResources(0, results.size()).stream() .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java index 7f3b38b..eada48b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java @@ -47,7 +47,7 @@ public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryVali public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() { IBundleProvider results = structureDefinitionResourceProvider.search( - new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource"))); + new SearchParameterMap().setLoadSynchronous(true).add(StructureDefinition.SP_KIND, new TokenParam("resource"))); Map> structureDefintions = results.getResources(0, results.size()).stream() .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); From f6671f97c5bd250396cf314d3ff10183d5b8737c Mon Sep 17 00:00:00 2001 From: chgl Date: Wed, 14 Feb 2024 11:34:24 +0100 Subject: [PATCH 4/5] Updated all Helm chart dependencies to latest (#629) * Updated all Helm chart dependencies to latest * added k8s versions 1.28 and 1.29 to test matrix * updated actions * updated curl to 8.6.0 --- .github/workflows/chart-test.yaml | 12 ++++----- charts/hapi-fhir-jpaserver/Chart.lock | 6 ++--- charts/hapi-fhir-jpaserver/Chart.yaml | 26 +++++++++++-------- charts/hapi-fhir-jpaserver/README.md | 10 +++---- .../templates/deployment.yaml | 2 +- charts/hapi-fhir-jpaserver/values.yaml | 13 ++-------- 6 files changed, 30 insertions(+), 39 deletions(-) diff --git a/.github/workflows/chart-test.yaml b/.github/workflows/chart-test.yaml index 629789f..ebe3a64 100644 --- a/.github/workflows/chart-test.yaml +++ b/.github/workflows/chart-test.yaml @@ -10,7 +10,7 @@ on: jobs: lint: runs-on: ubuntu-22.04 - container: quay.io/helmpack/chart-testing:v3.8.0@sha256:f058c660a28d99a9394ae081d98921efe068079531f247c86b8054e3c9d407aa + container: quay.io/helmpack/chart-testing:v3.10.1@sha256:7d8a7f99fc5840142249cc33ed6d9752fc66b92f9e1bf792d987ee85227d84da steps: - name: Install helm-docs working-directory: /tmp @@ -27,7 +27,7 @@ jobs: git config --global --add safe.directory /__w/hapi-fhir-jpaserver-starter/hapi-fhir-jpaserver-starter - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 @@ -41,17 +41,17 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - k8s-version: [1.25.9, 1.26.4, 1.27.2] + k8s-version: [1.25.11, 1.26.6, 1.27.3, 1.28.0, 1.29.0] needs: - lint steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 - name: Set up chart-testing - uses: helm/chart-testing-action@e8788873172cb653a90ca2e819d79d65a66d4e76 # v2.4.0 + uses: helm/chart-testing-action@e6669bcd63d7cb57cb4380c33043eebe5d111992 # v2.6.1 - name: Run chart-testing (list-changed) id: list-changed @@ -62,7 +62,7 @@ jobs: fi - name: Create k8s Kind Cluster - uses: helm/kind-action@fa81e57adff234b2908110485695db0f181f3c67 # v1.7.0 + uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0 if: ${{ steps.list-changed.outputs.changed == 'true' }} with: cluster_name: kind-cluster-k8s-${{ matrix.k8s-version }} diff --git a/charts/hapi-fhir-jpaserver/Chart.lock b/charts/hapi-fhir-jpaserver/Chart.lock index 98ba848..fa3830b 100644 --- a/charts/hapi-fhir-jpaserver/Chart.lock +++ b/charts/hapi-fhir-jpaserver/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: postgresql repository: oci://registry-1.docker.io/bitnamicharts - version: 12.5.6 -digest: sha256:4d21dbc02bbdb55b957b0093e37376853727de82396abfadfaf1d738bd51b8e6 -generated: "2023-06-03T20:58:45.922102213+02:00" + version: 13.2.27 +digest: sha256:6374f6f32d32adbe6763c48e2d817d85ec20a1784b2aea1fb0312c658f8e58e9 +generated: "2024-01-10T17:56:36.521957926+01:00" diff --git a/charts/hapi-fhir-jpaserver/Chart.yaml b/charts/hapi-fhir-jpaserver/Chart.yaml index ed55661..0950131 100644 --- a/charts/hapi-fhir-jpaserver/Chart.yaml +++ b/charts/hapi-fhir-jpaserver/Chart.yaml @@ -7,21 +7,25 @@ sources: - https://github.com/hapifhir/hapi-fhir-jpaserver-starter dependencies: - name: postgresql - version: 12.5.6 + version: 13.2.27 repository: oci://registry-1.docker.io/bitnamicharts condition: postgresql.enabled -appVersion: 6.8.3 -version: 0.14.0 +appVersion: 6.10.1 +version: 0.15.0 annotations: artifacthub.io/license: Apache-2.0 + artifacthub.io/containsSecurityUpdates: "false" + artifacthub.io/operator: "false" + artifacthub.io/prerelease: "false" + artifacthub.io/recommendations: | + - url: https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack + - url: https://artifacthub.io/packages/helm/bitnami/postgresql 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: updated starter image to 6.8.3 - - kind: fixed - description: incorrect handling of existing secret database config - - kind: added - description: support for using a non-admin user for the postgres database - - kind: added - description: ability to create a dedicated ServiceAccount + - kind: changed + description: updated starter image to 6.10.1 + - kind: changed + description: updated curlimages/curl to 8.5.0 + - kind: changed + description: "updated postgresql sub-chart to 13.2.27. ⚠️: this updates the used PostgreSQL image from v15 to v16." diff --git a/charts/hapi-fhir-jpaserver/README.md b/charts/hapi-fhir-jpaserver/README.md index af318f1..87b9bb1 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.14.0](https://img.shields.io/badge/Version-0.14.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.8.3](https://img.shields.io/badge/AppVersion-6.8.3-informational?style=flat-square) +![Version: 0.15.0](https://img.shields.io/badge/Version-0.15.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.10.1](https://img.shields.io/badge/AppVersion-6.10.1-informational?style=flat-square) This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. @@ -15,7 +15,7 @@ helm install hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver | Repository | Name | Version | |------------|------|---------| -| oci://registry-1.docker.io/bitnamicharts | postgresql | 12.5.6 | +| oci://registry-1.docker.io/bitnamicharts | postgresql | 13.2.27 | ## Values @@ -36,7 +36,7 @@ helm install hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver | 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.8.3@sha256:6195f1116ebabfb0a608addde043b3e524c456c4d4f35b3d25025afd7dcd2e27"` | 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.10.1@sha256:4eac1b3481180b028616d1fab7e657e368538063d75f7ed3be2032e34c657dd4"` | 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 | @@ -57,10 +57,6 @@ helm install hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver | postgresql.auth.database | string | `"fhir"` | name for a custom database to create | | postgresql.auth.existingSecret | string | `""` | Name of existing secret to use for PostgreSQL credentials `auth.postgresPassword`, `auth.password`, and `auth.replicationPassword` will be ignored and picked up from this secret The secret must contain the keys `postgres-password` (which is the password for "postgres" admin user), `password` (which is the password for the custom user to create when `auth.username` is set), and `replication-password` (which is the password for replication user). The secret might also contains the key `ldap-password` if LDAP is enabled. `ldap.bind_password` will be ignored and picked from this secret in this case. The value is evaluated as a template. | | postgresql.enabled | bool | `true` | enable an included PostgreSQL DB. see for details if set to `false`, the values under `externalDatabase` are used | -| postgresql.primary.containerSecurityContext.allowPrivilegeEscalation | bool | `false` | | -| postgresql.primary.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | -| postgresql.primary.containerSecurityContext.runAsNonRoot | bool | `true` | | -| postgresql.primary.containerSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | | | replicaCount | int | `1` | number of replicas to deploy | | resources | object | `{}` | configure the FHIR server's resource requests and limits | | securityContext.allowPrivilegeEscalation | bool | `false` | | diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index febfded..fc73ae2 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -31,7 +31,7 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} initContainers: - name: wait-for-db-to-be-ready - image: docker.io/bitnami/postgresql:15.3.0-debian-11-r7@sha256:cc301eef743685f4f69d1d719853988e8a9650c90fd9521f4742ce400b3fdf6a + image: docker.io/bitnami/postgresql:16.1.0-debian-11-r18@sha256:06f1f2297f6241a02bd8e8c025b31625254ca66784ac75a4a62e945fa611d045 imagePullPolicy: IfNotPresent {{- with .Values.restrictedContainerSecurityContext }} securityContext: diff --git a/charts/hapi-fhir-jpaserver/values.yaml b/charts/hapi-fhir-jpaserver/values.yaml index 56332bc..00326b8 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.8.3@sha256:6195f1116ebabfb0a608addde043b3e524c456c4d4f35b3d25025afd7dcd2e27" + tag: "v6.10.1@sha256:4eac1b3481180b028616d1fab7e657e368538063d75f7ed3be2032e34c657dd4" # -- image pullPolicy to use pullPolicy: IfNotPresent @@ -121,15 +121,6 @@ postgresql: # picked from this secret in this case. # The value is evaluated as a template. existingSecret: "" - primary: - containerSecurityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault # -- readiness probe # @ignored @@ -240,7 +231,7 @@ curl: image: registry: docker.io repository: curlimages/curl - tag: 8.4.0@sha256:4a3396ae573c44932d06ba33f8696db4429c419da87cbdc82965ee96a37dd0af + tag: 8.6.0@sha256:c3b8bee303c6c6beed656cfc921218c529d65aa61114eb9e27c62047a1271b9b tests: # -- configure the test pods resource requests and limits From 9e21d8062e84499a2e233a0463990452da3c2aba Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Thu, 15 Feb 2024 20:02:46 +0100 Subject: [PATCH 5/5] Feature/dynamic content (#605) * Added dynamic content options * Added defaults * Added sane defaults * Added IG operation providers for run time installation of IG's * Refactored conditions for enabling the provider * Refactoring * Disable it by default in config as well * document package install feature * Added hosting options * Provided better custom defaults * Removed double default files --------- Co-authored-by: Jose Costa Teixeira --- configs/app/index.html | 3 + custom/about.html | 14 ++++ custom/logo.jpg | Bin 0 -> 48378 bytes custom/welcome.html | 14 ++++ .../uhn/fhir/jpa/starter/AppProperties.java | 41 +++++------ .../starter/ExtraStaticFilesConfigurer.java | 50 ------------- .../jpa/starter/common/StarterJpaConfig.java | 4 +- ...IImplementationGuideOperationProvider.java | 14 +--- ...mplementationGuideR4OperationProvider.java | 10 ++- ...mplementationGuideR5OperationProvider.java | 10 ++- .../web/CustomContentFilesConfigurer.java | 40 +++++++++++ .../starter/web/WebAppFilesConfigurer.java | 56 +++++++++++++++ src/main/resources/application.yaml | 14 ++-- src/main/webapp/WEB-INF/templates/about.html | 65 +++++++++++++---- .../webapp/WEB-INF/templates/tmpl-banner.html | 51 +++++++++---- .../WEB-INF/templates/tmpl-home-welcome.html | 68 ++++++++++++++---- 16 files changed, 310 insertions(+), 144 deletions(-) create mode 100644 configs/app/index.html create mode 100644 custom/about.html create mode 100644 custom/logo.jpg create mode 100644 custom/welcome.html delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/ExtraStaticFilesConfigurer.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/web/CustomContentFilesConfigurer.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/web/WebAppFilesConfigurer.java diff --git a/configs/app/index.html b/configs/app/index.html new file mode 100644 index 0000000..a5ecf1b --- /dev/null +++ b/configs/app/index.html @@ -0,0 +1,3 @@ +

+ Greetings from the custom web app page! +

\ No newline at end of file diff --git a/custom/about.html b/custom/about.html new file mode 100644 index 0000000..f1064a9 --- /dev/null +++ b/custom/about.html @@ -0,0 +1,14 @@ +

+ This is a custom about page! It means you have configured 'custom_content_path: ./custom' in the application.yaml +

+

+ This server provides a complete implementation of the FHIR Specification + using a 100% open source software stack. +

+

+ This server is built + from a number of modules of the + HAPI FHIR + project, which is a 100% open-source (Apache 2.0 Licensed) Java based + implementation of the FHIR specification. +

\ No newline at end of file diff --git a/custom/logo.jpg b/custom/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3a5aad0b6d2ce87c6a585c4a3057c92d1e6881d7 GIT binary patch literal 48378 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 M(>eXex%@rxZ}K4ha{vGU literal 0 HcmV?d00001 diff --git a/custom/welcome.html b/custom/welcome.html new file mode 100644 index 0000000..40aa399 --- /dev/null +++ b/custom/welcome.html @@ -0,0 +1,14 @@ +

+ This is a custom welcome page! It means you have configured 'custom_content_path: ./custom' in the application.yaml +

+

+ This server provides a complete implementation of the FHIR Specification + using a 100% open source software stack. +

+

+ This server is built + from a number of modules of the + HAPI FHIR + project, which is a 100% open-source (Apache 2.0 Licensed) Java based + implementation of the FHIR specification. +

\ No newline at end of file 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 451c19d..16e8964 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -79,9 +79,8 @@ public class AppProperties { private Boolean install_transitive_ig_dependencies = true; private Map implementationGuides = null; - private String staticLocation = null; - - private String staticLocationPrefix = "/static"; + private String custom_content_path = null; + private String app_content_path = null; private Boolean lastn_enabled = false; private boolean store_resource_in_lucene_index_enabled = false; @@ -94,16 +93,9 @@ public class AppProperties { private Integer bundle_batch_pool_max_size = 100; private final Set local_base_urls = new HashSet<>(); private final Set logical_urls = new HashSet<>(); - + private final List custom_interceptor_classes = new ArrayList<>(); - public String getStaticLocationPrefix() { - return staticLocationPrefix; - } - - public void setStaticLocationPrefix(String staticLocationPrefix) { - this.staticLocationPrefix = staticLocationPrefix; - } public List getCustomInterceptorClasses() { @@ -111,14 +103,6 @@ public class AppProperties { } - public String getStaticLocation() { - return staticLocation; - } - - public void setStaticLocation(String staticLocation) { - this.staticLocation = staticLocation; - } - public Boolean getOpenapi_enabled() { return openapi_enabled; @@ -575,7 +559,7 @@ public Cors getCors() { public void setInstall_transitive_ig_dependencies(boolean install_transitive_ig_dependencies) { this.install_transitive_ig_dependencies = install_transitive_ig_dependencies; } - + public Integer getBundle_batch_pool_size() { return this.bundle_batch_pool_size; } @@ -600,6 +584,7 @@ public Cors getCors() { return logical_urls; } + public Boolean getIg_runtime_upload_enabled() { return ig_runtime_upload_enabled; } @@ -608,6 +593,22 @@ public Cors getCors() { this.ig_runtime_upload_enabled = ig_runtime_upload_enabled; } + public String getCustom_content_path() { + return custom_content_path; + } + + public void setCustom_content_path(String custom_content_path) { + this.custom_content_path = custom_content_path; + } + + public String getApp_content_path() { + return app_content_path; + } + + public void setApp_content_path(String app_content_path) { + this.app_content_path = app_content_path; + } + public static class Cors { private Boolean allow_Credentials = true; private List allowed_origin = List.of("*"); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ExtraStaticFilesConfigurer.java b/src/main/java/ca/uhn/fhir/jpa/starter/ExtraStaticFilesConfigurer.java deleted file mode 100644 index 2edb3aa..0000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ExtraStaticFilesConfigurer.java +++ /dev/null @@ -1,50 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import java.net.URI; - -@Configuration -@ConditionalOnProperty(prefix = "hapi.fhir", name = "staticLocation") -public class ExtraStaticFilesConfigurer implements WebMvcConfigurer { - - private String staticLocation; - private String rootContextPath; - - 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(rootContextPath + "/**").addResourceLocations(staticLocation); - } - - @Override - public void addViewControllers(ViewControllerRegistry registry) { - String path = URI.create(staticLocation).getPath(); - String lastSegment = path.substring(path.lastIndexOf('/') + 1); - - registry.addViewController(rootContextPath) - .setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html"); - - registry.addViewController(rootContextPath + "/*") - .setViewName("redirect:" + rootContextPath + "/" + 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/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java index cc8eb18..2a2de60 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 @@ -44,6 +44,7 @@ import ca.uhn.fhir.jpa.starter.annotations.OnImplementationGuidesPresent; import ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory; import ca.uhn.fhir.jpa.starter.ig.IImplementationGuideOperationProvider; import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper; +import ca.uhn.fhir.jpa.starter.ig.IImplementationGuideOperationProvider; import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; import ca.uhn.fhir.jpa.util.ResourceCountCache; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; @@ -272,8 +273,7 @@ public class StarterJpaConfig { IPackageInstallerSvc packageInstallerSvc, ThreadSafeResourceDeleterSvc theThreadSafeResourceDeleterSvc, ApplicationContext appContext, - Optional theIpsOperationProvider, - Optional implementationGuideOperationProvider) { + Optional theIpsOperationProvider, Optional implementationGuideOperationProvider) { RestfulServer fhirServer = new RestfulServer(fhirSystemDao.getContext()); List supportedResourceTypes = appProperties.getSupported_resource_types(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ig/IImplementationGuideOperationProvider.java b/src/main/java/ca/uhn/fhir/jpa/starter/ig/IImplementationGuideOperationProvider.java index 13a659e..b6ed5fa 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ig/IImplementationGuideOperationProvider.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ig/IImplementationGuideOperationProvider.java @@ -9,20 +9,10 @@ import java.io.IOException; public interface IImplementationGuideOperationProvider { static PackageInstallationSpec toPackageInstallationSpec(byte[] npmPackageAsByteArray) throws IOException { NpmPackage npmPackage = NpmPackage.fromPackage(new ByteArrayInputStream(npmPackageAsByteArray)); - return new PackageInstallationSpec() - .setName(npmPackage.name()) - .setPackageContents(npmPackageAsByteArray) - .setVersion(npmPackage.version()) - .setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL) - .setFetchDependencies(false); + return new PackageInstallationSpec().setName(npmPackage.name()).setPackageContents(npmPackageAsByteArray).setVersion(npmPackage.version()).setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL).setFetchDependencies(false); } - // The following declaration is the one that counts but cannot be used across different versions as stating - // Base64BinaryType would bind to a separate version - // @Operation(name = "$install", typeName = "ImplementationGuide") - + // The following declaration is the one that counts but cannot be used across different versions as stating Base64BinaryType would bind to a separate version // Parameters install(@OperationParam(name = "npmContent",min = 1, max = 1) Base64BinaryType implementationGuide); - // Parameters uninstall(@OperationParam(name = "name", min = 1, max = 1) String name, @OperationParam(name = "version", min = 1, max = 1) String version) ; - } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR4OperationProvider.java b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR4OperationProvider.java index 0c9405f..da2b6d2 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR4OperationProvider.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR4OperationProvider.java @@ -23,12 +23,10 @@ public class ImplementationGuideR4OperationProvider implements IImplementationGu } @Operation(name = "$install", typeName = "ImplementationGuide") - public Parameters install( - @OperationParam(name = "npmContent", min = 1, max = 1) Base64BinaryType implementationGuide) { + public Parameters install(@OperationParam(name = "npmContent", min = 1, max = 1) Base64BinaryType implementationGuide) { try { - packageInstallerSvc.install( - IImplementationGuideOperationProvider.toPackageInstallationSpec(implementationGuide.getValue())); + packageInstallerSvc.install(IImplementationGuideOperationProvider.toPackageInstallationSpec(implementationGuide.getValue())); } catch (IOException e) { throw new RuntimeException(e); } @@ -36,10 +34,10 @@ public class ImplementationGuideR4OperationProvider implements IImplementationGu } @Operation(name = "$uninstall", typeName = "ImplementationGuide") - public Parameters uninstall( - @OperationParam(name = "name", min = 1, max = 1) String name, @OperationParam(name = "version", min = 1, max = 1) String version) { + public Parameters uninstall(@OperationParam(name = "name", min = 1, max = 1) String name, @OperationParam(name = "version", min = 1, max = 1) String version) { packageInstallerSvc.uninstall(new PackageInstallationSpec().setName(name).setVersion(version)); return new Parameters(); } + } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR5OperationProvider.java b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR5OperationProvider.java index 50ba6b6..962820d 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR5OperationProvider.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ImplementationGuideR5OperationProvider.java @@ -23,21 +23,19 @@ public class ImplementationGuideR5OperationProvider implements IImplementationGu } @Operation(name = "$install", typeName = "ImplementationGuide") - public Parameters install( - @OperationParam(name = "npmContent", min = 1, max = 1) Base64BinaryType implementationGuide) { + public Parameters install(@OperationParam(name = "npmContent", min = 1, max = 1) Base64BinaryType implementationGuide) { try { - packageInstallerSvc.install( - IImplementationGuideOperationProvider.toPackageInstallationSpec(implementationGuide.getValue())); + packageInstallerSvc.install(IImplementationGuideOperationProvider.toPackageInstallationSpec(implementationGuide.getValue())); } catch (IOException e) { throw new RuntimeException(e); } return new Parameters(); } + @Operation(name = "$uninstall", typeName = "ImplementationGuide") - public org.hl7.fhir.r4.model.Parameters uninstall( - @OperationParam(name = "name", min = 1, max = 1) String name, @OperationParam(name = "version", min = 1, max = 1) String version) { + public org.hl7.fhir.r4.model.Parameters uninstall(@OperationParam(name = "name", min = 1, max = 1) String name, @OperationParam(name = "version", min = 1, max = 1) String version) { packageInstallerSvc.uninstall(new PackageInstallationSpec().setName(name).setVersion(version)); return new org.hl7.fhir.r4.model.Parameters(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/web/CustomContentFilesConfigurer.java b/src/main/java/ca/uhn/fhir/jpa/starter/web/CustomContentFilesConfigurer.java new file mode 100644 index 0000000..de9714e --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/web/CustomContentFilesConfigurer.java @@ -0,0 +1,40 @@ +package ca.uhn.fhir.jpa.starter.web; + +import ca.uhn.fhir.jpa.starter.AppProperties; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.FileUrlResource; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.net.MalformedURLException; + +@Configuration +@ConditionalOnProperty(prefix = "hapi.fhir", name = "custom_content_path") +public class CustomContentFilesConfigurer implements WebMvcConfigurer { + + public static final String CUSTOM_CONTENT = "/content"; + private String customContentPath; + + + public CustomContentFilesConfigurer(AppProperties appProperties) { + customContentPath = appProperties.getCustom_content_path(); + if (customContentPath.endsWith("/")) + customContentPath = customContentPath.substring(0, customContentPath.lastIndexOf('/')); + + } + + + @Override + public void addResourceHandlers(@NotNull ResourceHandlerRegistry theRegistry) { + if (!theRegistry.hasMappingForPattern(CUSTOM_CONTENT + "/**")) { + + try { + theRegistry.addResourceHandler(CUSTOM_CONTENT + "/**").addResourceLocations(new FileUrlResource(customContentPath)); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/web/WebAppFilesConfigurer.java b/src/main/java/ca/uhn/fhir/jpa/starter/web/WebAppFilesConfigurer.java new file mode 100644 index 0000000..fb20901 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/web/WebAppFilesConfigurer.java @@ -0,0 +1,56 @@ +package ca.uhn.fhir.jpa.starter.web; + +import ca.uhn.fhir.jpa.starter.AppProperties; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.io.FileUrlResource; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.net.MalformedURLException; +import java.net.URI; + +@Configuration +@ConditionalOnProperty(prefix = "hapi.fhir", name = "app_content_path") +public class WebAppFilesConfigurer implements WebMvcConfigurer { + + public static final String WEB_CONTENT = "web"; + private String appContentPath; + + + public WebAppFilesConfigurer(AppProperties appProperties) { + appContentPath = appProperties.getApp_content_path(); + if (appContentPath.endsWith("/")) + appContentPath = appContentPath.substring(0, appContentPath.lastIndexOf('/')); + + } + + + @Override + public void addResourceHandlers(@NotNull ResourceHandlerRegistry theRegistry) { + if (!theRegistry.hasMappingForPattern(WEB_CONTENT + "/**")) { + { + try { + theRegistry.addResourceHandler(WEB_CONTENT + "/**").addResourceLocations(new FileUrlResource(appContentPath)); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + } + } + + @Override + public void addViewControllers(@NotNull ViewControllerRegistry registry) { + String path = URI.create(appContentPath).getPath(); + String lastSegment = path.substring(path.lastIndexOf('/') + 1); + + registry.addViewController(WEB_CONTENT + "/" + lastSegment).setViewName("redirect:" + lastSegment + "/index.html"); + registry.addViewController(WEB_CONTENT + "/" + lastSegment + "/").setViewName("redirect:index.html"); + + registry.setOrder(Ordered.HIGHEST_PRECEDENCE); + + } +} \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 95ebfec..df14c31 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -14,8 +14,8 @@ spring: check-location: false baselineOnMigrate: true datasource: - url: 'jdbc:h2:file:./target/database/h2' - #url: jdbc:h2:mem:test_mem + #url: 'jdbc:h2:file:./target/database/h2' + url: jdbc:h2:mem:test_mem username: sa password: null driverClassName: org.h2.Driver @@ -76,10 +76,12 @@ hapi: ### 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 + ### enables the server to overwrite defaults on HTML, css, etc. under the url pattern of eg. /content/custom ** + ### Folder with custom content MUST be named custom. If omitted then default content applies + #custom_content_path: ./custom + ### enables the server host custom content. If e.g. the value ./configs/app is supplied then the content + ### will be served under /web/app + #app_content_path: ./configs/app ### enable to set the Server URL # server_address: http://hapi.fhir.org/baseR4 # defer_indexing_for_codesystems_of_size: 101 diff --git a/src/main/webapp/WEB-INF/templates/about.html b/src/main/webapp/WEB-INF/templates/about.html index 0811965..4859418 100644 --- a/src/main/webapp/WEB-INF/templates/about.html +++ b/src/main/webapp/WEB-INF/templates/about.html @@ -21,21 +21,23 @@

About This Server

-

- This server provides a complete implementation of the FHIR Specification - using a 100% open source software stack. -

-

- This server is built - from a number of modules of the - HAPI FHIR - project, which is a 100% open-source (Apache 2.0 Licensed) Java based - implementation of the FHIR specification. -

-

+

+

+ This server provides a complete implementation of the FHIR Specification + using a 100% open source software stack. +

+

+ This server is built + from a number of modules of the + HAPI FHIR + project, which is a 100% open-source (Apache 2.0 Licensed) Java based + implementation of the FHIR specification. +

-

+
+ +
@@ -55,6 +57,41 @@
- + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/templates/tmpl-banner.html b/src/main/webapp/WEB-INF/templates/tmpl-banner.html index abbe718..bfcc01b 100644 --- a/src/main/webapp/WEB-INF/templates/tmpl-banner.html +++ b/src/main/webapp/WEB-INF/templates/tmpl-banner.html @@ -1,20 +1,41 @@
-
-
- Sample Logo -
-
- -
-
- - -
- Warning! -

-
- +
+
+ +
+
+ +
+
+ + + +
+ Warning! +

+
+
diff --git a/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html b/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html index 97747f4..f9bfcd9 100644 --- a/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html +++ b/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html @@ -1,16 +1,58 @@ -
-

- This server provides a complete implementation of the FHIR Specification - using a 100% open source software stack. -

-

- This server is built - from a number of modules of the - HAPI FHIR - project, which is a 100% open-source (Apache 2.0 Licensed) Java based - implementation of the FHIR specification. -

+ +
+ +
+
+

+ This server provides a complete implementation of the FHIR Specification + using a 100% open source software stack. +

+

+ This server is built + from a number of modules of the + HAPI FHIR + project, which is a 100% open-source (Apache 2.0 Licensed) Java based + implementation of the FHIR specification. +

+
+
- + + \ No newline at end of file