Skip to content

Commit a12c8ad

Browse files
Add /health endpoint and standardize /version response (#331)
* Add /health endpoint and standardize /version response * Add license headers and Javadocs to health and version controllers * Enhance /health endpoint to check Database and Redis connectivity * Improve /health endpoint HTTP status handling and logging * Enhance database health check with validation query * Refactor health controller to constructor injection and constants * Refactor: Extract business logic to HealthService to keep controller lean * Refactor: Extract business logic to HealthService to keep controller lean * Fix: Use ObjectProvider for optional health dependencies
1 parent 3c1ef4b commit a12c8ad

4 files changed

Lines changed: 273 additions & 51 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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.
17+
* See the 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+
23+
package com.iemr.common.controller.health;
24+
25+
import com.iemr.common.service.health.HealthService;
26+
import java.util.Map;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
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+
/**
35+
* Health check controller for Common-API.
36+
* Verifies application liveness and dependency health (DB, Redis).
37+
*
38+
* @author vaishnavbhosale
39+
*/
40+
@RestController
41+
public class HealthController {
42+
43+
private static final Logger logger = LoggerFactory.getLogger(HealthController.class);
44+
45+
private final HealthService healthService;
46+
47+
public HealthController(HealthService healthService) {
48+
this.healthService = healthService;
49+
}
50+
51+
@GetMapping("/health")
52+
public ResponseEntity<Map<String, Object>> health() {
53+
logger.info("Health check endpoint called");
54+
55+
56+
Map<String, Object> healthStatus = healthService.checkHealth();
57+
58+
// Standard HTTP Status logic
59+
String status = (String) healthStatus.get("status");
60+
HttpStatus httpStatus = "UP".equals(status) ? HttpStatus.OK : HttpStatus.SERVICE_UNAVAILABLE;
61+
62+
logger.info("Health check completed with status: {}", status);
63+
64+
return ResponseEntity.status(httpStatus).body(healthStatus);
65+
}
66+
}
Lines changed: 47 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,73 @@
11
/*
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-
*/
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.
17+
* See the 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+
/**
23+
* REST controller exposing application version and build metadata.
24+
* <p>
25+
* Provides the <code>/version</code> endpoint which returns the
26+
* Git commit hash and build timestamp in a standardized JSON format.
27+
* </p>
28+
*
29+
* @author Vaishnav Bhosale
30+
*/
2231
package com.iemr.common.controller.version;
2332

24-
import java.io.BufferedReader;
25-
import java.io.IOException;
2633
import java.io.InputStream;
27-
import java.io.InputStreamReader;
34+
import java.util.Properties;
2835

2936
import org.slf4j.Logger;
3037
import org.slf4j.LoggerFactory;
3138
import org.springframework.web.bind.annotation.RequestMapping;
3239
import org.springframework.web.bind.annotation.RequestMethod;
3340
import org.springframework.web.bind.annotation.RestController;
3441

35-
import com.iemr.common.utils.response.OutputResponse;
36-
3742
import io.swagger.v3.oas.annotations.Operation;
3843

39-
4044
@RestController
4145
public class VersionController {
4246

43-
private Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());
47+
private final Logger logger =
48+
LoggerFactory.getLogger(this.getClass().getSimpleName());
4449

4550
@Operation(summary = "Get version")
4651
@RequestMapping(value = "/version", method = { RequestMethod.GET })
47-
public String versionInformation() {
48-
OutputResponse output = new OutputResponse();
49-
try {
50-
logger.info("version Controller Start");
51-
output.setResponse(readGitProperties());
52-
} catch (Exception e) {
53-
output.setError(e);
54-
}
55-
56-
logger.info("version Controller End");
57-
return output.toString();
58-
}
52+
public VersionInfo versionInformation() {
5953

60-
private String readGitProperties() throws Exception {
61-
ClassLoader classLoader = getClass().getClassLoader();
62-
InputStream inputStream = classLoader.getResourceAsStream("git.properties");
54+
Properties properties = new Properties();
6355

64-
return readFromInputStream(inputStream);
65-
}
56+
try (InputStream is = getClass()
57+
.getClassLoader()
58+
.getResourceAsStream("git.properties")) {
6659

67-
private String readFromInputStream(InputStream inputStream) throws IOException {
68-
StringBuilder resultStringBuilder = new StringBuilder();
69-
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
70-
String line;
71-
while ((line = br.readLine()) != null) {
72-
resultStringBuilder.append(line).append("\n");
60+
if (is != null) {
61+
properties.load(is);
7362
}
63+
64+
} catch (Exception e) {
65+
logger.error("Error reading git.properties", e);
7466
}
75-
return resultStringBuilder.toString();
67+
68+
return new VersionInfo(
69+
properties.getProperty("git.commit.id.abbrev", "unknown"),
70+
properties.getProperty("git.build.time", "unknown")
71+
);
7672
}
7773
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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.
17+
* See the 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+
/**
23+
* DTO for exposing build and version metadata.
24+
*
25+
* @author vaishnavbhosale
26+
*/
27+
package com.iemr.common.controller.version;
28+
29+
public class VersionInfo {
30+
31+
private String commitHash;
32+
private String buildTime;
33+
34+
public VersionInfo(String commitHash, String buildTime) {
35+
this.commitHash = commitHash;
36+
this.buildTime = buildTime;
37+
}
38+
39+
public String getCommitHash() {
40+
return commitHash;
41+
}
42+
43+
public String getBuildTime() {
44+
return buildTime;
45+
}
46+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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.
17+
* See the 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.common.service.health;
23+
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
import org.springframework.beans.factory.ObjectProvider; // <--- VITAL IMPORT
27+
import org.springframework.data.redis.connection.RedisConnection;
28+
import org.springframework.data.redis.connection.RedisConnectionFactory;
29+
import org.springframework.stereotype.Service;
30+
31+
import javax.sql.DataSource;
32+
import java.sql.Connection;
33+
import java.util.HashMap;
34+
import java.util.Map;
35+
36+
/**
37+
* Health check service for Common-API.
38+
* Verifies application liveness and dependency health (DB, Redis).
39+
*
40+
* @author vaishnavbhosale
41+
*/
42+
@Service
43+
public class HealthService {
44+
45+
private static final Logger logger = LoggerFactory.getLogger(HealthService.class);
46+
47+
private static final String COMPONENT_DATABASE = "database";
48+
private static final String COMPONENT_REDIS = "redis";
49+
50+
private final DataSource dataSource;
51+
private final RedisConnectionFactory redisConnectionFactory;
52+
53+
// --- CORRECT CONSTRUCTOR START ---
54+
public HealthService(ObjectProvider<DataSource> dataSourceProvider,
55+
ObjectProvider<RedisConnectionFactory> redisProvider) {
56+
// This allows them to be null without crashing the app
57+
this.dataSource = dataSourceProvider.getIfAvailable();
58+
this.redisConnectionFactory = redisProvider.getIfAvailable();
59+
}
60+
// --- CORRECT CONSTRUCTOR END ---
61+
62+
public Map<String, Object> checkHealth() {
63+
Map<String, Object> response = new HashMap<>();
64+
Map<String, String> components = new HashMap<>();
65+
66+
boolean dbUp = checkDatabase(components);
67+
boolean redisUp = checkRedis(components);
68+
69+
boolean overallUp = dbUp && redisUp;
70+
71+
response.put("status", overallUp ? "UP" : "DOWN");
72+
response.put("components", components);
73+
74+
return response;
75+
}
76+
77+
private boolean checkDatabase(Map<String, String> components) {
78+
if (dataSource == null) {
79+
components.put(COMPONENT_DATABASE, "NOT_CONFIGURED");
80+
return true;
81+
}
82+
83+
try (Connection connection = dataSource.getConnection();
84+
var statement = connection.createStatement()) {
85+
86+
statement.execute("SELECT 1");
87+
components.put(COMPONENT_DATABASE, "UP");
88+
return true;
89+
90+
} catch (Exception e) {
91+
logger.error("Database health check failed", e);
92+
components.put(COMPONENT_DATABASE, "DOWN");
93+
return false;
94+
}
95+
}
96+
97+
private boolean checkRedis(Map<String, String> components) {
98+
if (redisConnectionFactory == null) {
99+
components.put(COMPONENT_REDIS, "NOT_CONFIGURED");
100+
return true;
101+
}
102+
103+
try (RedisConnection connection = redisConnectionFactory.getConnection()) {
104+
connection.ping();
105+
components.put(COMPONENT_REDIS, "UP");
106+
return true;
107+
108+
} catch (Exception e) {
109+
logger.error("Redis health check failed", e);
110+
components.put(COMPONENT_REDIS, "DOWN");
111+
return false;
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)