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 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 0000000..3a5aad0 Binary files /dev/null and b/custom/logo.jpg differ 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/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 { /** 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 1613e3f..01ff15b 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/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)); 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..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,16 +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 145924d..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 @@ -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; @@ -22,15 +23,21 @@ 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); } 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..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 @@ -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; @@ -22,15 +23,21 @@ public class ImplementationGuideR5OperationProvider { } @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) { + + 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