Skip to content

Commit c56ce2d

Browse files
authored
Merge pull request #95 from Suraj-kumar00/contributor/suraj
Implement /health and /version endpoints for Admin API
2 parents c4b57e0 + e1f2346 commit c56ce2d

7 files changed

Lines changed: 427 additions & 61 deletions

File tree

pom.xml

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -295,26 +295,25 @@
295295
<plugin>
296296
<groupId>pl.project13.maven</groupId>
297297
<artifactId>git-commit-id-plugin</artifactId>
298-
<version>2.2.4</version>
298+
<version>4.9.10</version>
299299
<executions>
300300
<execution>
301301
<id>get-the-git-infos</id>
302302
<goals>
303303
<goal>revision</goal>
304304
</goals>
305+
<phase>initialize</phase>
305306
</execution>
306307
</executions>
307308
<configuration>
308-
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
309-
<prefix>git</prefix>
310-
<verbose>false</verbose>
311309
<generateGitPropertiesFile>true</generateGitPropertiesFile>
312-
<generateGitPropertiesFilename>
313-
${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
314-
<format>json</format>
315-
<gitDescribe>
316-
<skip>true</skip>
317-
</gitDescribe>
310+
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
311+
<includeOnlyProperties>
312+
<includeOnlyProperty>git.commit.id</includeOnlyProperty>
313+
<includeOnlyProperty>git.build.time</includeOnlyProperty>
314+
</includeOnlyProperties>
315+
<commitIdGenerationMode>full</commitIdGenerationMode>
316+
<format>properties</format>
318317
</configuration>
319318
</plugin>
320319
<plugin>
@@ -472,6 +471,12 @@
472471
<goal>repackage</goal>
473472
</goals>
474473
</execution>
474+
<execution>
475+
<id>build-info</id>
476+
<goals>
477+
<goal>build-info</goal>
478+
</goals>
479+
</execution>
475480
</executions>
476481
</plugin>
477482
</plugins>
@@ -485,4 +490,4 @@
485490
</plugin>
486491
</plugins>
487492
</reporting>
488-
</project>
493+
</project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.iemr.admin.config;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
9+
import com.iemr.admin.utils.JwtAuthenticationUtil;
10+
import com.iemr.admin.utils.JwtUserIdValidationFilter;
11+
12+
@Configuration
13+
public class SecurityFilterConfig {
14+
15+
@Autowired
16+
private JwtAuthenticationUtil jwtAuthenticationUtil;
17+
18+
@Value("${cors.allowed-origins}")
19+
private String allowedOrigins;
20+
21+
@Bean
22+
public FilterRegistrationBean<JwtUserIdValidationFilter> jwtFilterRegistration() {
23+
FilterRegistrationBean<JwtUserIdValidationFilter> registration = new FilterRegistrationBean<>();
24+
registration.setFilter(new JwtUserIdValidationFilter(jwtAuthenticationUtil, allowedOrigins));
25+
registration.addUrlPatterns("/*");
26+
registration.setOrder(1);
27+
28+
// Set name for easier debugging
29+
registration.setName("JwtUserIdValidationFilter");
30+
31+
return registration;
32+
}
33+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* AMRIT – Accessible Medical Records via Integrated Technology
3+
* Integrated EHR (Electronic Health Records) Solution
4+
*
5+
* Copyright (C) "Piramal Swasthya Management and Research Institute"
6+
*
7+
* This file is part of AMRIT.
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU General Public License
20+
* along with this program. If not, see https://www.gnu.org/licenses/.
21+
*/
22+
package com.iemr.admin.controller.health;
23+
24+
import java.util.Map;
25+
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.http.HttpStatus;
30+
import org.springframework.http.ResponseEntity;
31+
import org.springframework.web.bind.annotation.GetMapping;
32+
import org.springframework.web.bind.annotation.RestController;
33+
34+
import com.iemr.admin.service.health.HealthService;
35+
36+
import io.swagger.v3.oas.annotations.Operation;
37+
38+
@RestController
39+
public class HealthController {
40+
41+
private static final Logger logger = LoggerFactory.getLogger(HealthController.class);
42+
43+
@Autowired
44+
private HealthService healthService;
45+
46+
@Operation(summary = "Health check endpoint")
47+
@GetMapping("/health")
48+
public ResponseEntity<Map<String, Object>> health() {
49+
logger.info("Health check endpoint called");
50+
51+
Map<String, Object> healthStatus = healthService.checkHealth();
52+
53+
// Return 503 if any service is down, 200 if all are up
54+
String status = (String) healthStatus.get("status");
55+
HttpStatus httpStatus = "UP".equals(status) ? HttpStatus.OK : HttpStatus.SERVICE_UNAVAILABLE;
56+
57+
logger.info("Health check completed with status: {}", status);
58+
return ResponseEntity.status(httpStatus).body(healthStatus);
59+
}
60+
}

src/main/java/com/iemr/admin/controller/version/VersionController.java

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,58 +21,33 @@
2121
*/
2222
package com.iemr.admin.controller.version;
2323

24-
import java.io.BufferedReader;
25-
import java.io.IOException;
26-
import java.io.InputStream;
27-
import java.io.InputStreamReader;
24+
import java.util.Map;
2825

2926
import org.slf4j.Logger;
3027
import org.slf4j.LoggerFactory;
31-
32-
import org.springframework.web.bind.annotation.RequestMapping;
33-
import org.springframework.web.bind.annotation.RequestMethod;
28+
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.http.MediaType;
30+
import org.springframework.web.bind.annotation.GetMapping;
3431
import org.springframework.web.bind.annotation.RestController;
3532

36-
import com.iemr.admin.utils.response.OutputResponse;
33+
import com.iemr.admin.service.version.VersionService;
3734

3835
import io.swagger.v3.oas.annotations.Operation;
3936

40-
4137
@RestController
4238
public class VersionController {
4339

44-
private Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());
45-
46-
@Operation(summary = "Version information")
47-
@RequestMapping(value = "/version", method = { RequestMethod.GET })
48-
public String versionInformation() {
49-
OutputResponse output = new OutputResponse();
50-
try {
51-
logger.info("version Controller Start");
52-
output.setResponse(readGitProperties());
53-
} catch (Exception e) {
54-
output.setError(e);
55-
}
56-
57-
logger.info("version Controller End");
58-
return output.toString();
59-
}
60-
61-
private String readGitProperties() throws Exception {
62-
ClassLoader classLoader = getClass().getClassLoader();
63-
InputStream inputStream = classLoader.getResourceAsStream("git.properties");
40+
private static final Logger logger = LoggerFactory.getLogger(VersionController.class);
6441

65-
return readFromInputStream(inputStream);
66-
}
42+
@Autowired
43+
private VersionService versionService;
6744

68-
private String readFromInputStream(InputStream inputStream) throws IOException {
69-
StringBuilder resultStringBuilder = new StringBuilder();
70-
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
71-
String line;
72-
while ((line = br.readLine()) != null) {
73-
resultStringBuilder.append(line).append("\n");
74-
}
75-
}
76-
return resultStringBuilder.toString();
77-
}
45+
@Operation(summary = "Version information")
46+
@GetMapping(value = "/version", produces = MediaType.APPLICATION_JSON_VALUE)
47+
public Map<String, String> versionInformation() {
48+
logger.info("version Controller Start");
49+
Map<String, String> versionInfo = versionService.getVersionInfo();
50+
logger.info("version Controller End");
51+
return versionInfo;
52+
}
7853
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* AMRIT – Accessible Medical Records via Integrated Technology
3+
* Integrated EHR (Electronic Health Records) Solution
4+
*
5+
* Copyright (C) "Piramal Swasthya Management and Research Institute"
6+
*
7+
* This file is part of AMRIT.
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU General Public License
20+
* along with this program. If not, see https://www.gnu.org/licenses/.
21+
*/
22+
package com.iemr.admin.service.health;
23+
24+
import java.sql.Connection;
25+
import java.sql.PreparedStatement;
26+
import java.sql.ResultSet;
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
30+
import javax.sql.DataSource;
31+
32+
import org.slf4j.Logger;
33+
import org.slf4j.LoggerFactory;
34+
import org.springframework.beans.factory.annotation.Autowired;
35+
import org.springframework.data.redis.core.RedisCallback;
36+
import org.springframework.data.redis.core.RedisTemplate;
37+
import org.springframework.stereotype.Service;
38+
39+
@Service
40+
public class HealthService {
41+
42+
private static final Logger logger = LoggerFactory.getLogger(HealthService.class);
43+
private static final String DB_HEALTH_CHECK_QUERY = "SELECT 1 as health_check";
44+
45+
@Autowired
46+
private DataSource dataSource;
47+
48+
@Autowired(required = false)
49+
private RedisTemplate<String, Object> redisTemplate;
50+
51+
public Map<String, Object> checkHealth() {
52+
Map<String, Object> healthStatus = new HashMap<>();
53+
boolean overallHealth = true;
54+
55+
// Check database connectivity (details logged internally, not exposed)
56+
boolean dbHealthy = checkDatabaseHealthInternal();
57+
if (!dbHealthy) {
58+
overallHealth = false;
59+
}
60+
61+
// Check Redis connectivity if configured (details logged internally)
62+
if (redisTemplate != null) {
63+
boolean redisHealthy = checkRedisHealthInternal();
64+
if (!redisHealthy) {
65+
overallHealth = false;
66+
}
67+
}
68+
69+
healthStatus.put("status", overallHealth ? "UP" : "DOWN");
70+
71+
logger.info("Health check completed - Overall status: {}", overallHealth ? "UP" : "DOWN");
72+
return healthStatus;
73+
}
74+
75+
private boolean checkDatabaseHealthInternal() {
76+
long startTime = System.currentTimeMillis();
77+
78+
try (Connection connection = dataSource.getConnection()) {
79+
boolean isConnectionValid = connection.isValid(2); // 2 second timeout per best practices
80+
81+
if (isConnectionValid) {
82+
try (PreparedStatement stmt = connection.prepareStatement(DB_HEALTH_CHECK_QUERY)) {
83+
stmt.setQueryTimeout(3); // 3 second query timeout
84+
try (ResultSet rs = stmt.executeQuery()) {
85+
if (rs.next() && rs.getInt(1) == 1) {
86+
long responseTime = System.currentTimeMillis() - startTime;
87+
logger.debug("Database health check: UP ({}ms)", responseTime);
88+
return true;
89+
}
90+
}
91+
}
92+
}
93+
logger.warn("Database health check: Connection not valid");
94+
return false;
95+
} catch (Exception e) {
96+
logger.error("Database health check failed: {}", e.getMessage());
97+
return false;
98+
}
99+
}
100+
101+
private boolean checkRedisHealthInternal() {
102+
long startTime = System.currentTimeMillis();
103+
104+
try {
105+
String pong = redisTemplate.execute((RedisCallback<String>) connection -> connection.ping());
106+
107+
if ("PONG".equals(pong)) {
108+
long responseTime = System.currentTimeMillis() - startTime;
109+
logger.debug("Redis health check: UP ({}ms)", responseTime);
110+
return true;
111+
}
112+
logger.warn("Redis health check: Ping returned unexpected response");
113+
return false;
114+
} catch (Exception e) {
115+
logger.error("Redis health check failed: {}", e.getMessage());
116+
return false;
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)