Compare commits
15 Commits
9f3c209f78
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8d4ced193 | ||
|
|
2ce85f064f | ||
|
|
8069b7019a | ||
|
|
01f4bc2ce9 | ||
|
|
3f0170b55d | ||
|
|
f267209b0e | ||
|
|
3edd75bf72 | ||
|
|
8f3335e8bb | ||
|
|
c0a22e1e47 | ||
|
|
85266820db | ||
|
|
53837d6c94 | ||
|
|
5778f431bf | ||
|
|
68fc2a4c04 | ||
|
|
b80687959f | ||
|
|
ba077ab8fd |
2
.github/workflows/smoke-tests.yml
vendored
2
.github/workflows/smoke-tests.yml
vendored
@@ -30,6 +30,8 @@ jobs:
|
|||||||
distribution: zulu
|
distribution: zulu
|
||||||
|
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
|
env:
|
||||||
|
MAVEN_OPTS: -Xms256m -Xmx3g -XX:+UseG1GC
|
||||||
run: mvn -B package --file pom.xml -Dmaven.test.skip=true
|
run: mvn -B package --file pom.xml -Dmaven.test.skip=true
|
||||||
|
|
||||||
- name: Docker Pull HTTP client
|
- name: Docker Pull HTTP client
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ FROM build-hapi AS build-distroless
|
|||||||
RUN mvn package -DskipTests spring-boot:repackage -Pboot
|
RUN mvn package -DskipTests spring-boot:repackage -Pboot
|
||||||
RUN mkdir /app && cp /tmp/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
|
||||||
|
|
||||||
|
COPY src/main/java/HealthCheck.java /app/HealthCheck.java
|
||||||
|
RUN javac /app/HealthCheck.java
|
||||||
|
|
||||||
|
|
||||||
########### Use the official Tomcat image as base image for the Tomcat variant
|
########### Use the official Tomcat image as base image for the Tomcat variant
|
||||||
########### it can be built using eg. `docker build --target tomcat .`
|
########### it can be built using eg. `docker build --target tomcat .`
|
||||||
|
|||||||
50
README.md
50
README.md
@@ -49,6 +49,42 @@ docker run -p 8080:8080 -e hapi.fhir.default_encoding=xml hapiproject/hapi:lates
|
|||||||
|
|
||||||
HAPI looks in the environment variables for properties in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file for defaults.
|
HAPI looks in the environment variables for properties in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file for defaults.
|
||||||
|
|
||||||
|
### CORS configuration (including ETag/If-Match)
|
||||||
|
|
||||||
|
The starter CORS configuration now supports the following configurable keys:
|
||||||
|
|
||||||
|
- `hapi.fhir.cors.allowed_origin`
|
||||||
|
- `hapi.fhir.cors.allow_Credentials`
|
||||||
|
- `hapi.fhir.cors.allowed_headers`
|
||||||
|
- `hapi.fhir.cors.exposed_headers`
|
||||||
|
- `hapi.fhir.cors.allowed_methods`
|
||||||
|
|
||||||
|
Defaults include `If-Match` in allowed headers and `ETag` in exposed headers to support browser-based optimistic locking workflows.
|
||||||
|
The `allowed_headers`, `exposed_headers`, and `allowed_methods` keys are optional; if omitted, built-in defaults are applied.
|
||||||
|
The default for `allow_Credentials` is `false`. If you set `allow_Credentials=true`, do not use `"*"` for `allowed_origin`; configure explicit origins.
|
||||||
|
|
||||||
|
Example override file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
hapi:
|
||||||
|
fhir:
|
||||||
|
cors:
|
||||||
|
allowed_origin:
|
||||||
|
- "http://localhost:3000"
|
||||||
|
allowed_headers:
|
||||||
|
- Origin
|
||||||
|
- Accept
|
||||||
|
- Content-Type
|
||||||
|
- Authorization
|
||||||
|
- Cache-Control
|
||||||
|
- If-Match
|
||||||
|
- If-None-Match
|
||||||
|
exposed_headers:
|
||||||
|
- Location
|
||||||
|
- Content-Location
|
||||||
|
- ETag
|
||||||
|
```
|
||||||
|
|
||||||
### Binary storage configuration
|
### Binary storage configuration
|
||||||
|
|
||||||
To stream large `Binary` payloads to disk instead of the database, configure the starter with filesystem storage properties:
|
To stream large `Binary` payloads to disk instead of the database, configure the starter with filesystem storage properties:
|
||||||
@@ -475,6 +511,20 @@ jpa:
|
|||||||
# Then comment all hibernate.search.backend.*
|
# Then comment all hibernate.search.backend.*
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Docker Health Check
|
||||||
|
|
||||||
|
The distroless Docker image includes a built-in health check that verifies the FHIR server is operational by calling the `/fhir/metadata` endpoint and confirming a valid `CapabilityStatement` is returned. It uses a standalone Java class with no external dependencies, making it compatible with the distroless base image which has no shell or utilities like `curl`.
|
||||||
|
|
||||||
|
To run the health check inside a running container:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker exec hapi-fhir-jpaserver-start java -cp /app HealthCheck
|
||||||
|
```
|
||||||
|
|
||||||
|
An exit code of `0` indicates the server is healthy. An exit code of `1` indicates a failure, with diagnostic details written to stderr.
|
||||||
|
|
||||||
|
To enable periodic health checks, uncomment the `healthcheck` block in `docker-compose.yml`.
|
||||||
|
|
||||||
## Running hapi-fhir-jpaserver directly from IntelliJ as Spring Boot
|
## Running hapi-fhir-jpaserver directly from IntelliJ as Spring Boot
|
||||||
Make sure you run with the maven profile called ```boot``` and NOT also ```jetty```. Then you are ready to press debug the project directly without any extra Application Servers.
|
Make sure you run with the maven profile called ```boot``` and NOT also ```jetty```. Then you are ready to press debug the project directly without any extra Application Servers.
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,17 @@ services:
|
|||||||
SPRING_DATASOURCE_USERNAME: "admin"
|
SPRING_DATASOURCE_USERNAME: "admin"
|
||||||
SPRING_DATASOURCE_PASSWORD: "admin"
|
SPRING_DATASOURCE_PASSWORD: "admin"
|
||||||
SPRING_DATASOURCE_DRIVER_CLASS_NAME: "org.postgresql.Driver"
|
SPRING_DATASOURCE_DRIVER_CLASS_NAME: "org.postgresql.Driver"
|
||||||
SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect
|
HIBERNATE_DIALECT: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
|
# Uncomment to enable periodic health checks.
|
||||||
|
# Can also be run manually: docker exec hapi-fhir-jpaserver-start java -cp /app HealthCheck
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD", "java", "-cp", "/app", "HealthCheck"]
|
||||||
|
# interval: 30s
|
||||||
|
# timeout: 10s
|
||||||
|
# start_period: 60s
|
||||||
|
# retries: 3
|
||||||
depends_on:
|
depends_on:
|
||||||
hapi-fhir-postgres:
|
hapi-fhir-postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
4
pom.xml
4
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<hapi.fhir.jpa.server.starter.revision>1</hapi.fhir.jpa.server.starter.revision>
|
<hapi.fhir.jpa.server.starter.revision>1</hapi.fhir.jpa.server.starter.revision>
|
||||||
<clinical-reasoning.version>4.2.0</clinical-reasoning.version>
|
<clinical-reasoning.version>4.4.0</clinical-reasoning.version>
|
||||||
|
|
||||||
<!-- Plugins Versions -->
|
<!-- Plugins Versions -->
|
||||||
<maven.failsafe.version>3.5.4</maven.failsafe.version>
|
<maven.failsafe.version>3.5.4</maven.failsafe.version>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>8.7.13-SNAPSHOT</version>
|
<version>8.8.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-jpaserver-starter</artifactId>
|
<artifactId>hapi-fhir-jpaserver-starter</artifactId>
|
||||||
|
|||||||
45
src/main/java/HealthCheck.java
Normal file
45
src/main/java/HealthCheck.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class HealthCheck {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
var port = System.getenv().getOrDefault("SERVER_PORT", "8080");
|
||||||
|
var url = new URL("http://localhost:" + port + "/fhir/metadata");
|
||||||
|
var conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod("GET");
|
||||||
|
conn.setRequestProperty("Accept", "application/fhir+json");
|
||||||
|
conn.setConnectTimeout(10000);
|
||||||
|
conn.setReadTimeout(10000);
|
||||||
|
|
||||||
|
var status = conn.getResponseCode();
|
||||||
|
if (status != 200) {
|
||||||
|
System.err.println("Health check failed: HTTP " + status);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var body = new StringBuilder();
|
||||||
|
try (var reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
body.append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pattern = Pattern.compile("\"resourceType\"\\s*:\\s*\"CapabilityStatement\"");
|
||||||
|
if (pattern.matcher(body.toString()).find()) {
|
||||||
|
System.exit(0);
|
||||||
|
} else {
|
||||||
|
System.err.println("Health check failed: CapabilityStatement not found in response");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Health check failed: " + e.getMessage());
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -867,11 +867,37 @@ public class AppProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Cors {
|
public static class Cors {
|
||||||
private Boolean allow_Credentials = true;
|
private static final List<String> DEFAULT_ALLOWED_HEADERS = List.of(
|
||||||
|
"Origin",
|
||||||
|
"Accept",
|
||||||
|
"Content-Type",
|
||||||
|
"Authorization",
|
||||||
|
"Cache-Control",
|
||||||
|
"If-Match",
|
||||||
|
"If-None-Match",
|
||||||
|
"x-fhir-starter",
|
||||||
|
"X-Requested-With",
|
||||||
|
"Prefer");
|
||||||
|
private static final List<String> DEFAULT_EXPOSED_HEADERS = List.of(
|
||||||
|
"Location",
|
||||||
|
"Content-Location",
|
||||||
|
"ETag",
|
||||||
|
"Date",
|
||||||
|
"Retry-After",
|
||||||
|
"X-Correlation-Id",
|
||||||
|
"X-Progress",
|
||||||
|
"X-Request-Id");
|
||||||
|
private static final List<String> DEFAULT_ALLOWED_METHODS =
|
||||||
|
List.of("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD");
|
||||||
|
|
||||||
|
private Boolean allow_Credentials = false;
|
||||||
private List<String> allowed_origin = List.of("*");
|
private List<String> allowed_origin = List.of("*");
|
||||||
|
private List<String> allowed_headers = DEFAULT_ALLOWED_HEADERS;
|
||||||
|
private List<String> exposed_headers = DEFAULT_EXPOSED_HEADERS;
|
||||||
|
private List<String> allowed_methods = DEFAULT_ALLOWED_METHODS;
|
||||||
|
|
||||||
public List<String> getAllowed_origin() {
|
public List<String> getAllowed_origin() {
|
||||||
return allowed_origin;
|
return defaultIfNull(allowed_origin, List.of("*"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAllowed_origin(List<String> allowed_origin) {
|
public void setAllowed_origin(List<String> allowed_origin) {
|
||||||
@@ -885,6 +911,30 @@ public class AppProperties {
|
|||||||
public void setAllow_Credentials(Boolean allow_Credentials) {
|
public void setAllow_Credentials(Boolean allow_Credentials) {
|
||||||
this.allow_Credentials = allow_Credentials;
|
this.allow_Credentials = allow_Credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getAllowed_headers() {
|
||||||
|
return defaultIfNull(allowed_headers, DEFAULT_ALLOWED_HEADERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowed_headers(List<String> allowed_headers) {
|
||||||
|
this.allowed_headers = allowed_headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getExposed_headers() {
|
||||||
|
return defaultIfNull(exposed_headers, DEFAULT_EXPOSED_HEADERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExposed_headers(List<String> exposed_headers) {
|
||||||
|
this.exposed_headers = exposed_headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getAllowed_methods() {
|
||||||
|
return defaultIfNull(allowed_methods, DEFAULT_ALLOWED_METHODS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowed_methods(List<String> allowed_methods) {
|
||||||
|
this.allowed_methods = allowed_methods;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Logger {
|
public static class Logger {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class ErrorHandling {
|
public class ErrorHandling {
|
||||||
|
|
||||||
@@ -68,30 +67,14 @@ public class ErrorHandling {
|
|||||||
|
|
||||||
public static void setAccessControlHeaders(HttpServletResponse resp, AppProperties myAppProperties) {
|
public static void setAccessControlHeaders(HttpServletResponse resp, AppProperties myAppProperties) {
|
||||||
if (myAppProperties.getCors() != null) {
|
if (myAppProperties.getCors() != null) {
|
||||||
if (myAppProperties.getCors().getAllow_Credentials()) {
|
AppProperties.Cors cors = myAppProperties.getCors();
|
||||||
resp.setHeader(
|
if (cors.getAllow_Credentials()) {
|
||||||
"Access-Control-Allow-Origin",
|
String allowOrigin =
|
||||||
myAppProperties.getCors().getAllowed_origin().stream()
|
cors.getAllowed_origin().stream().findFirst().orElse("*");
|
||||||
.findFirst()
|
resp.setHeader("Access-Control-Allow-Origin", allowOrigin);
|
||||||
.get());
|
resp.setHeader("Access-Control-Allow-Methods", String.join(", ", cors.getAllowed_methods()));
|
||||||
resp.setHeader(
|
resp.setHeader("Access-Control-Allow-Headers", String.join(", ", cors.getAllowed_headers()));
|
||||||
"Access-Control-Allow-Methods",
|
resp.setHeader("Access-Control-Expose-Headers", String.join(", ", cors.getExposed_headers()));
|
||||||
String.join(", ", Arrays.asList("GET", "HEAD", "POST", "OPTIONS")));
|
|
||||||
resp.setHeader(
|
|
||||||
"Access-Control-Allow-Headers",
|
|
||||||
String.join(
|
|
||||||
", ",
|
|
||||||
Arrays.asList(
|
|
||||||
"x-fhir-starter",
|
|
||||||
"Origin",
|
|
||||||
"Accept",
|
|
||||||
"X-Requested-With",
|
|
||||||
"Content-Type",
|
|
||||||
"Authorization",
|
|
||||||
"Cache-Control")));
|
|
||||||
resp.setHeader(
|
|
||||||
"Access-Control-Expose-Headers",
|
|
||||||
String.join(", ", Arrays.asList("Location", "Content-Location")));
|
|
||||||
resp.setHeader("Access-Control-Max-Age", "86400");
|
resp.setHeader("Access-Control-Max-Age", "86400");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ import org.springframework.context.annotation.Conditional;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
@@ -280,24 +279,17 @@ public class StarterJpaConfig {
|
|||||||
// showing a typical setup. You should customize this
|
// showing a typical setup. You should customize this
|
||||||
// to your specific needs
|
// to your specific needs
|
||||||
ourLog.info("CORS is enabled on this server");
|
ourLog.info("CORS is enabled on this server");
|
||||||
|
AppProperties.Cors corsProperties = appProperties.getCors();
|
||||||
CorsConfiguration config = new CorsConfiguration();
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
config.addAllowedHeader(HttpHeaders.ORIGIN);
|
corsProperties.getAllowed_headers().forEach(config::addAllowedHeader);
|
||||||
config.addAllowedHeader(HttpHeaders.ACCEPT);
|
|
||||||
config.addAllowedHeader(HttpHeaders.CONTENT_TYPE);
|
|
||||||
config.addAllowedHeader(HttpHeaders.AUTHORIZATION);
|
|
||||||
config.addAllowedHeader(HttpHeaders.CACHE_CONTROL);
|
|
||||||
config.addAllowedHeader("x-fhir-starter");
|
|
||||||
config.addAllowedHeader("X-Requested-With");
|
|
||||||
config.addAllowedHeader("Prefer");
|
|
||||||
|
|
||||||
List<String> allAllowedCORSOrigins = appProperties.getCors().getAllowed_origin();
|
List<String> allAllowedCORSOrigins = corsProperties.getAllowed_origin();
|
||||||
allAllowedCORSOrigins.forEach(config::addAllowedOriginPattern);
|
allAllowedCORSOrigins.forEach(config::addAllowedOriginPattern);
|
||||||
ourLog.info("CORS allows the following origins: {}", String.join(", ", allAllowedCORSOrigins));
|
ourLog.info("CORS allows the following origins: {}", String.join(", ", allAllowedCORSOrigins));
|
||||||
|
|
||||||
config.addExposedHeader("Location");
|
corsProperties.getExposed_headers().forEach(config::addExposedHeader);
|
||||||
config.addExposedHeader("Content-Location");
|
config.setAllowedMethods(corsProperties.getAllowed_methods());
|
||||||
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD"));
|
config.setAllowCredentials(corsProperties.getAllow_Credentials());
|
||||||
config.setAllowCredentials(appProperties.getCors().getAllow_Credentials());
|
|
||||||
|
|
||||||
// Create the interceptor and register it
|
// Create the interceptor and register it
|
||||||
return new CorsInterceptor(config);
|
return new CorsInterceptor(config);
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ hapi:
|
|||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
bulk_export_enabled: false
|
bulk_export_enabled: false
|
||||||
bulk_import_enabled: false
|
bulk_import_enabled: false
|
||||||
bulk_export_file_retention_period_hours: 2
|
bulk_export_file_retention_period_hours: 2
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
# F. Write / Delete / Integrity
|
# F. Write / Delete / Integrity
|
||||||
@@ -338,9 +338,31 @@ hapi:
|
|||||||
# K. CORS
|
# K. CORS
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
cors:
|
cors:
|
||||||
allow_Credentials: true
|
# allow_Credentials: false
|
||||||
allowed_origin:
|
allowed_origin:
|
||||||
- "*"
|
- "*"
|
||||||
|
# If you enable allow_Credentials=true, use explicit origins instead of "*".
|
||||||
|
# Optional overrides. If omitted, built-in defaults are used.
|
||||||
|
# allowed_headers:
|
||||||
|
# - Origin
|
||||||
|
# - Accept
|
||||||
|
# - Content-Type
|
||||||
|
# - Authorization
|
||||||
|
# - Cache-Control
|
||||||
|
# - If-Match
|
||||||
|
# - If-None-Match
|
||||||
|
# exposed_headers:
|
||||||
|
# - Location
|
||||||
|
# - Content-Location
|
||||||
|
# - ETag
|
||||||
|
# allowed_methods:
|
||||||
|
# - GET
|
||||||
|
# - POST
|
||||||
|
# - PUT
|
||||||
|
# - DELETE
|
||||||
|
# - OPTIONS
|
||||||
|
# - PATCH
|
||||||
|
# - HEAD
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
# L. Search Orchestration
|
# L. Search Orchestration
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ spring:
|
|||||||
# Hibernate dialect is auto-detected except for H2/Postgres.
|
# Hibernate dialect is auto-detected except for H2/Postgres.
|
||||||
# If using H2: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect
|
# If using H2: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect
|
||||||
# If using Postgres: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect
|
# If using Postgres: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect
|
||||||
dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect
|
dialect: ${HIBERNATE_DIALECT:ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect}
|
||||||
|
|
||||||
# --- Optional Hibernate DDL & tuning (commented out from source) ---
|
# --- Optional Hibernate DDL & tuning (commented out from source) ---
|
||||||
hbm2ddl:
|
hbm2ddl:
|
||||||
@@ -368,9 +368,31 @@ hapi:
|
|||||||
# K. CORS
|
# K. CORS
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
cors:
|
cors:
|
||||||
allow_Credentials: true
|
# allow_Credentials: false
|
||||||
allowed_origin:
|
allowed_origin:
|
||||||
- "*"
|
- "*"
|
||||||
|
# If you enable allow_Credentials=true, use explicit origins instead of "*".
|
||||||
|
# Optional overrides. If omitted, built-in defaults are used.
|
||||||
|
# allowed_headers:
|
||||||
|
# - Origin
|
||||||
|
# - Accept
|
||||||
|
# - Content-Type
|
||||||
|
# - Authorization
|
||||||
|
# - Cache-Control
|
||||||
|
# - If-Match
|
||||||
|
# - If-None-Match
|
||||||
|
# exposed_headers:
|
||||||
|
# - Location
|
||||||
|
# - Content-Location
|
||||||
|
# - ETag
|
||||||
|
# allowed_methods:
|
||||||
|
# - GET
|
||||||
|
# - POST
|
||||||
|
# - PUT
|
||||||
|
# - DELETE
|
||||||
|
# - OPTIONS
|
||||||
|
# - PATCH
|
||||||
|
# - HEAD
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
# L. Search Orchestration
|
# L. Search Orchestration
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class AppPropertiesCorsDefaultsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultCorsHeadersIncludeFhirOptimisticLockingHeaders() {
|
||||||
|
AppProperties.Cors cors = new AppProperties.Cors();
|
||||||
|
|
||||||
|
assertFalse(cors.getAllow_Credentials());
|
||||||
|
assertTrue(cors.getAllowed_headers().contains("If-Match"));
|
||||||
|
assertTrue(cors.getExposed_headers().contains("ETag"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nullCorsListsFallBackToDefaults() {
|
||||||
|
AppProperties.Cors cors = new AppProperties.Cors();
|
||||||
|
cors.setAllowed_headers(null);
|
||||||
|
cors.setExposed_headers(null);
|
||||||
|
cors.setAllowed_methods(null);
|
||||||
|
|
||||||
|
assertTrue(cors.getAllowed_headers().contains("If-Match"));
|
||||||
|
assertTrue(cors.getExposed_headers().contains("ETag"));
|
||||||
|
assertTrue(cors.getAllowed_methods().contains("PATCH"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user