Skip to content

Commit 36963ed

Browse files
committed
#663 - Standardized API endpoint to serve configuration for the frontend
- Add SettingsController and /.settings endpoint - add some passthrough config params to application.yml
1 parent 3762174 commit 36963ed

File tree

6 files changed

+170
-2
lines changed

6 files changed

+170
-2
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,17 @@ To configure the location of the external service, use the following parameters.
153153
| ELASTIC_SEARCH_HOST | Host and port of the elastic search endpoint | `192.168.1.1:9200` | `localhost:9200` |
154154
| ELASTIC_SEARCH_FILTER | Which parameters can be used to filter results | `foo,bar,baz` | `context,terminology,kds_module` |
155155

156+
### Passthrough Variables
157+
158+
There are a few variables that are not used in the backend itself, but are forwarded to the frontend if requested.
159+
160+
161+
| EnvVar | Description | Example | Default |
162+
|-----------------------------|------------------------------------------------------------------|-------------------|-----------------------------------------------------------------------------------------------------------------|
163+
| PT_CCDL_VERSION | The used version of the Clinical Cohort Definition Language | `` | `unknown` |
164+
| PT_PORTAL_LINK | URL to the portal page | `https://foo.bar` | `https://antrag.forschen-fuer-gesundheit.de` |
165+
| PT_DSE_PATIENT_PROFILE_URL | URL of the patient profile used in data selection and extraction | `foo,bar,baz` | `https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/PatientPseudonymisiert` |
166+
156167
## Support for self-signed certificates
157168

158169
The dataportal backend supports the use of self-signed certificates from your own CAs.

src/main/java/de/numcodex/feasibility_gui_backend/config/WebSecurityConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class WebSecurityConfig {
5151
public static final String PATH_CODEABLE_CONCEPT = "/codeable-concept";
5252
public static final String PATH_SWAGGER_UI = "/swagger-ui/**";
5353
public static final String PATH_SWAGGER_CONFIG = "/v3/api-docs/**";
54+
public static final String PATH_SETTINGS = "/.settings";
5455
@Value("${app.keycloakAllowedRole}")
5556
private String keycloakAllowedRole;
5657

@@ -97,6 +98,7 @@ public SecurityFilterChain apiFilterChain(
9798
Converter<Jwt, ? extends AbstractAuthenticationToken> authenticationConverter) throws Exception {
9899

99100
http.authorizeHttpRequests(authorize -> authorize
101+
.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher(PATH_SETTINGS)).permitAll()
100102
.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher(PATH_SWAGGER_CONFIG)).permitAll()
101103
.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher(PATH_API + PATH_ACTUATOR_HEALTH)).permitAll()
102104
.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher(PATH_API + PATH_ACTUATOR_INFO)).permitAll()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package de.numcodex.feasibility_gui_backend.settings;
2+
3+
import java.util.Map;
4+
5+
import de.numcodex.feasibility_gui_backend.config.WebSecurityConfig;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.web.bind.annotation.CrossOrigin;
8+
import org.springframework.web.bind.annotation.GetMapping;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
@RestController
12+
@CrossOrigin(origins = "${cors.allowedOrigins}", exposedHeaders = "Location")
13+
public class SettingsController {
14+
15+
private static final String KEY_DSE_URL = "dsePatientProfileUrl";
16+
private static final String KEY_LOCATION_RESULT_LOWERBOUNDARY = "lowerboundarylocationresult";
17+
private static final String KEY_PATIENT_RESULT_LOWERBOUNDARY = "lowerboundarypatientresult";
18+
private static final String KEY_POLLING_INTERVAL = "pollingintervall";
19+
private static final String KEY_POLLING_TIME = "pollingtime";
20+
private static final String KEY_PORTAL_LINK = "proposalPortalLink";
21+
private static final String KEY_CCDL_VERSION = "ccdlVersion";
22+
private static final String KEY_REST_API_PATH = "uiBackendApiUrl";
23+
24+
25+
@Value("${passthrough.dsePatientProfileUrl}")
26+
private String dseUrl;
27+
28+
@Value("${app.privacy.threshold.sites}")
29+
private int lowerboundaryLocationResult;
30+
31+
@Value("${app.privacy.threshold.results}")
32+
private int lowerboundardyPatientResult;
33+
34+
@Value("${app.privacy.quota.read.resultDetailedObfuscated.interval}")
35+
private String pollingInterval;
36+
37+
@Value("${app.privacy.quota.read.resultSummary.pollingInterval}")
38+
private String pollingTime = "PT20S";
39+
40+
@Value("${passthrough.portalLink}")
41+
private String portalLink;
42+
43+
@Value("${passthrough.ccdlVersion}")
44+
private String ccdlVersion = "version";
45+
46+
47+
@GetMapping("/.settings")
48+
public Map<String, Object> getSettings() {
49+
return Map.of(
50+
KEY_DSE_URL, dseUrl,
51+
KEY_LOCATION_RESULT_LOWERBOUNDARY, lowerboundaryLocationResult,
52+
KEY_PATIENT_RESULT_LOWERBOUNDARY, lowerboundardyPatientResult,
53+
KEY_POLLING_INTERVAL, pollingInterval,
54+
KEY_POLLING_TIME, pollingTime,
55+
KEY_PORTAL_LINK, portalLink,
56+
KEY_CCDL_VERSION, ccdlVersion,
57+
KEY_REST_API_PATH, WebSecurityConfig.PATH_API
58+
);
59+
}
60+
}

src/main/resources/application.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,10 @@ app:
149149
pollingInterval: ${PRIVACY_QUOTA_READ_DETAILED_OBFUSCATED_POLLINGINTERVAL:PT10S}
150150
amount: ${PRIVACY_QUOTA_READ_DETAILED_OBFUSCATED_AMOUNT:3}
151151
interval: ${PRIVACY_QUOTA_READ_DETAILED_OBFUSCATED_INTERVAL:PT2H}
152-
152+
passthrough:
153+
ccdlVersion: ${PT_CCDL_VERSION:unknown}
154+
portalLink: ${PT_PORTAL_LINK:https://antrag.forschen-fuer-gesundheit.de}
155+
dsePatientProfileUrl: ${PT_DSE_PATIENT_PROFILE_URL:https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/PatientPseudonymisiert}
153156
logging:
154157
level:
155158
org.hibernate: ${LOG_LEVEL_SQL:warn}

src/main/resources/static/v3/api-docs/swagger.yaml

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,13 +869,27 @@ paths:
869869
operationId: ''
870870
responses:
871871
200:
872-
description: Successful health information.
872+
description: Successful build information.
873873
content:
874874
application/vnd.spring-boot.actuator.v3+json:
875875
schema:
876876
$ref: "#/components/schemas/SystemInfo"
877877
tags:
878878
- intrinsics
879+
/.settings:
880+
get:
881+
summary: Offers config settings needed by the frontend.
882+
description: ''
883+
operationId: ''
884+
responses:
885+
200:
886+
description: Successful health information.
887+
content:
888+
application/json:
889+
schema:
890+
$ref: "#/components/schemas/SettingsInfo"
891+
tags:
892+
- intrinsics
879893
components:
880894
schemas:
881895
Dataquery:
@@ -1668,6 +1682,43 @@ components:
16681682
$ref: "#/components/schemas/BuildInfo"
16691683
terminology:
16701684
$ref: "#/components/schemas/Terminology"
1685+
SettingsInfo:
1686+
type: object
1687+
properties:
1688+
ccdlVersion:
1689+
type: string
1690+
examples:
1691+
- v1
1692+
pollingintervall:
1693+
type: string
1694+
format: duration
1695+
examples:
1696+
- PT10S
1697+
dsePatientProfileUrl:
1698+
type: string
1699+
examples:
1700+
- https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/PatientPseudonymisiert
1701+
uiBackendApiUrl:
1702+
type: string
1703+
examples:
1704+
- /api/v5
1705+
lowerboundarypatientresult:
1706+
type: number
1707+
examples:
1708+
- 20
1709+
proposalPortalLink:
1710+
type: string
1711+
examples:
1712+
- https://antrag.forschen-fuer-gesundheit.de
1713+
lowerboundarylocationresult:
1714+
type: number
1715+
examples:
1716+
- 3
1717+
pollingtime:
1718+
type: string
1719+
format: duration
1720+
examples:
1721+
- PT10S
16711722
securitySchemes:
16721723
dataportal_auth:
16731724
type: oauth2
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package de.numcodex.feasibility_gui_backend.settings;
2+
3+
import de.numcodex.feasibility_gui_backend.query.ratelimiting.RateLimitingInterceptor;
4+
import de.numcodex.feasibility_gui_backend.query.ratelimiting.RateLimitingServiceSpringConfig;
5+
import org.junit.jupiter.api.Tag;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.extension.ExtendWith;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
10+
import org.springframework.context.annotation.Import;
11+
import org.springframework.security.test.context.support.WithMockUser;
12+
import org.springframework.test.context.bean.override.mockito.MockitoBean;
13+
import org.springframework.test.context.junit.jupiter.SpringExtension;
14+
import org.springframework.test.web.servlet.MockMvc;
15+
16+
import java.net.URI;
17+
18+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
19+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
20+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
21+
22+
@Tag("info")
23+
@ExtendWith(SpringExtension.class)
24+
@Import(RateLimitingServiceSpringConfig.class)
25+
@WebMvcTest(
26+
controllers = SettingsController.class
27+
)
28+
class SettingsControllerIT {
29+
@Autowired
30+
private MockMvc mockMvc;
31+
32+
@MockitoBean
33+
private RateLimitingInterceptor rateLimitingInterceptor;
34+
35+
@Test
36+
@WithMockUser(roles = "DATAPORTAL_TEST_USER")
37+
void getSettings_succeeds() throws Exception {
38+
mockMvc.perform(get(URI.create("/.settings")).with(csrf()))
39+
.andExpect(status().isOk());
40+
}
41+
}

0 commit comments

Comments
 (0)