Skip to content

Add /health endpoint and update /version endpoint#188

Closed
DurgaPrasad-54 wants to merge 2 commits intoPSMRI:mainfrom
DurgaPrasad-54:feat/health-version-clean
Closed

Add /health endpoint and update /version endpoint#188
DurgaPrasad-54 wants to merge 2 commits intoPSMRI:mainfrom
DurgaPrasad-54:feat/health-version-clean

Conversation

@DurgaPrasad-54
Copy link
Copy Markdown
Contributor

@DurgaPrasad-54 DurgaPrasad-54 commented Feb 16, 2026

📋 Description

JIRA ID:

This PR implements /health and /version endpoints in the HWC-API as part of Issue PSMRI/Common-API#102.

What’s changed

  • Added /health endpoint
  • Updated /version endpoint

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a health check endpoint that monitors system status, including database and cache service availability
    • Enhanced version endpoint to provide detailed build metadata in JSON format (commit hash, build timestamp, branch)
    • Both endpoints are now publicly accessible without authentication
  • Chores

    • Configured build process to capture git metadata during compilation

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 16, 2026

📝 Walkthrough

Walkthrough

The changes introduce a health check system with new /health and /version endpoints that return structured JSON responses. A Maven plugin is added to generate Git metadata at build time. Both endpoints are configured to bypass JWT authentication for public access.

Changes

Cohort / File(s) Summary
Maven Build Configuration
pom.xml
Adds git-commit-id-maven-plugin to generate Git properties file (commit hash, branch, build timestamp) during the build process.
Health Check Feature
src/main/java/com/iemr/hwc/controller/health/HealthController.java, src/main/java/com/iemr/hwc/service/health/HealthService.java
Introduces new /health endpoint that checks MySQL and Redis backend health, returning component-level status, version, response time, and error details in a structured JSON map.
Version Endpoint Refactor
src/main/java/com/iemr/hwc/controller/version/VersionController.java
Refactors version endpoint from returning plain string to JSON map with commitHash, buildTimestamp, version, and branch sourced from git.properties file. Updates method signature to return ResponseEntity<Map<String, String>>. Replaces inline Git properties reading with dedicated loader method.
Security Configuration
src/main/java/com/iemr/hwc/utils/JwtUserIdValidationFilter.java, src/main/java/com/iemr/hwc/utils/http/HTTPRequestInterceptor.java
Adds /health and /version endpoints to JWT validation bypass list and HTTP request interceptor to allow unauthenticated public access.
Minor Formatting
src/main/resources/application.properties
Removes trailing newline at end of file; comment text preserved.

Sequence Diagram

sequenceDiagram
    participant Client
    participant HealthController
    participant HealthService
    participant MySQL
    participant Redis

    Client->>HealthController: GET /health
    HealthController->>HealthService: checkHealth()
    
    HealthService->>MySQL: Test connection & query version
    MySQL-->>HealthService: Version info or error
    
    HealthService->>Redis: Test connection & query info
    Redis-->>HealthService: Info response or error
    
    HealthService->>HealthService: Aggregate status<br/>(UP/DOWN per component)<br/>with response times
    
    HealthService-->>HealthController: Map with overall status<br/>& component details
    HealthController-->>Client: 200 OK with health JSON
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Git properties in our build, a health check heart so strong,
Two new endpoints bloom for all, no auth to slow us down,
MySQL, Redis, version bright—we'll check them all day long! 🏥✨

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add /health endpoint and update /version endpoint' directly and accurately reflects the main changes in the pull request.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@src/main/java/com/iemr/hwc/controller/health/HealthController.java`:
- Around line 23-28: The health() endpoint in HealthController always returns
200; change it to return 503 when the service reports DOWN by inspecting the map
returned from healthService.checkHealth() (e.g., check
healthStatus.get("status") or similar), and if the status equals "DOWN"
(case-insensitive) return
ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(healthStatus),
otherwise return ResponseEntity.ok(healthStatus); keep the existing logging and
ensure you import/use HttpStatus and do null-safety when reading the "status"
key.

In `@src/main/java/com/iemr/hwc/service/health/HealthService.java`:
- Around line 44-71: The public checkHealth() currently returns detailed
component data (host, port, database, version, and error messages) that exposes
infra too broadly; change it so checkHealth() only returns minimal data (e.g.,
{"status":"UP"|"DOWN","timestamp":...}) and do not include the "components" map
for unauthenticated callers, and either (a) move the detailed components payload
into a new authenticated method/endpoint (e.g., getHealthDetails() or
checkHealthDetails()) that calls checkMySQLHealth() and checkRedisHealth(), or
(b) sanitize the components map before returning by removing sensitive keys
("host","port","database","version") and stripping raw exception/error messages;
update calls to checkMySQLHealth() and checkRedisHealth() so they still perform
checks but only surface non-sensitive boolean/status flags to the public
checkHealth().
- Around line 134-145: The details map is missing responseTimeMs when
result.isHealthy is false; in HealthService (the block that checks
result.isHealthy) ensure you always put responseTimeMs into details regardless
of success or failure—add details.put("responseTimeMs", responseTimeMs) inside
the else branch that handles result.isHealthy == false (near the code
referencing result.error and status.put("status", "DOWN")) so the response shape
matches the success and exception paths.

In `@src/main/resources/application.properties`:
- Line 47: The trailing newline after the comment "Keep a line at the end so
that environment.ts file can be appended properly" was removed; restore the
final newline in src/main/resources/application.properties so that future
appends to environment.ts begin on a new line, or if the requirement is
obsolete, remove that comment line entirely—update the file to either re-add the
trailing newline after the existing comment text or delete the comment.
🧹 Nitpick comments (6)
src/main/java/com/iemr/hwc/controller/version/VersionController.java (3)

42-42: Prefer LoggerFactory.getLogger(VersionController.class) over getSimpleName().

Using getSimpleName() strips the package prefix from the logger category, which breaks package-level log configuration (e.g., logging.level.com.iemr.hwc.controller=DEBUG won't apply to this logger).

♻️ Suggested fix
-	private final Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());
+	private final Logger logger = LoggerFactory.getLogger(VersionController.class);

46-63: The endpoint logic is sound overall. A couple of optional improvements:

  1. The HashMap produces non-deterministic key ordering in JSON. Consider LinkedHashMap if consistent output is desired.
  2. The four "unknown" string literals are duplicated between the default parameters (lines 51-54) and the catch block (lines 57-60). A constant (e.g., private static final String UNKNOWN = "unknown") would reduce duplication.

Both are cosmetic — the logic itself is correct.


66-75: Log a warning when git.properties is not found on the classpath.

If getResourceAsStream returns null, the method silently returns empty Properties. This makes it harder to diagnose missing build-time plugin configuration. A warning log would help operators during deployment.

♻️ Suggested fix
 	private Properties loadGitProperties() throws IOException {
 		Properties properties = new Properties();
 		try (InputStream input = getClass().getClassLoader()
 				.getResourceAsStream("git.properties")) {
 			if (input != null) {
 				properties.load(input);
+			} else {
+				logger.warn("git.properties not found on classpath");
 			}
 		}
 		return properties;
 	}
src/main/java/com/iemr/hwc/service/health/HealthService.java (2)

69-69: Health check logs at INFO on every invocation — consider DEBUG level.

If this endpoint is polled by a load balancer or monitoring system every few seconds, INFO-level logging will be very noisy. Use logger.debug(...) here, consistent with Line 135.

Proposed fix
-        logger.info("Health check completed - Overall status: {}", overallHealth ? "UP" : "DOWN");
+        logger.debug("Health check completed - Overall status: {}", overallHealth ? "UP" : "DOWN");

82-100: MySQL health check acquires a connection from the pool on every call.

Each health check call gets a JDBC connection, validates it, runs SELECT 1, and runs SELECT VERSION(). Under frequent polling, this adds load to both the connection pool and the database.

Consider caching the health result for a short TTL (e.g., 5–10 seconds) to avoid hammering the database and pool on rapid successive calls.

src/main/java/com/iemr/hwc/controller/health/HealthController.java (1)

3-3: Unused import HashMap.

java.util.HashMap is imported but never used in this file.

Proposed fix
-import java.util.HashMap;
 import java.util.Map;

Comment on lines +23 to +28
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> health() {
logger.debug("Health check endpoint called");
Map<String, Object> healthStatus = healthService.checkHealth();
return ResponseEntity.ok(healthStatus);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return HTTP 503 when health status is DOWN.

The endpoint always returns 200 OK, even when the service reports "status": "DOWN". Load balancers, orchestrators (Kubernetes), and monitoring tools rely on the HTTP status code to determine service health. Returning 200 for a degraded service means it will continue receiving traffic.

Proposed fix
     `@GetMapping`("/health")
     public ResponseEntity<Map<String, Object>> health() {
         logger.debug("Health check endpoint called");
         Map<String, Object> healthStatus = healthService.checkHealth();
-        return ResponseEntity.ok(healthStatus);
+        boolean isUp = "UP".equals(healthStatus.get("status"));
+        return ResponseEntity.status(isUp ? 200 : 503).body(healthStatus);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> health() {
logger.debug("Health check endpoint called");
Map<String, Object> healthStatus = healthService.checkHealth();
return ResponseEntity.ok(healthStatus);
}
`@GetMapping`("/health")
public ResponseEntity<Map<String, Object>> health() {
logger.debug("Health check endpoint called");
Map<String, Object> healthStatus = healthService.checkHealth();
boolean isUp = "UP".equals(healthStatus.get("status"));
return ResponseEntity.status(isUp ? 200 : 503).body(healthStatus);
}
🤖 Prompt for AI Agents
In `@src/main/java/com/iemr/hwc/controller/health/HealthController.java` around
lines 23 - 28, The health() endpoint in HealthController always returns 200;
change it to return 503 when the service reports DOWN by inspecting the map
returned from healthService.checkHealth() (e.g., check
healthStatus.get("status") or similar), and if the status equals "DOWN"
(case-insensitive) return
ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(healthStatus),
otherwise return ResponseEntity.ok(healthStatus); keep the existing logging and
ensure you import/use HttpStatus and do null-safety when reading the "status"
key.

Comment on lines +44 to +71
public Map<String, Object> checkHealth() {
Map<String, Object> healthStatus = new LinkedHashMap<>();
Map<String, Object> components = new LinkedHashMap<>();
boolean overallHealth = true;

// Check MySQL connectivity
Map<String, Object> mysqlStatus = checkMySQLHealth();
components.put("mysql", mysqlStatus);
if (!isHealthy(mysqlStatus)) {
overallHealth = false;
}

// Check Redis connectivity if configured
if (redisTemplate != null) {
Map<String, Object> redisStatus = checkRedisHealth();
components.put("redis", redisStatus);
if (!isHealthy(redisStatus)) {
overallHealth = false;
}
}

healthStatus.put("status", overallHealth ? "UP" : "DOWN");
healthStatus.put("timestamp", Instant.now().toString());
healthStatus.put("components", components);

logger.info("Health check completed - Overall status: {}", overallHealth ? "UP" : "DOWN");
return healthStatus;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Unauthenticated endpoint exposes infrastructure details — information disclosure risk.

The /health endpoint is bypassed from JWT auth and returns host, port, database name, component versions, and error messages to any caller. This leaks infrastructure topology to unauthenticated users, which can aid reconnaissance attacks.

Consider:

  • Returning only {"status": "UP"} or {"status": "DOWN"} for the unauthenticated endpoint.
  • Gating the detailed component information behind authentication, or using a separate authenticated /health/details endpoint.
  • At minimum, strip host, port, database, and version from the public response.
🤖 Prompt for AI Agents
In `@src/main/java/com/iemr/hwc/service/health/HealthService.java` around lines 44
- 71, The public checkHealth() currently returns detailed component data (host,
port, database, version, and error messages) that exposes infra too broadly;
change it so checkHealth() only returns minimal data (e.g.,
{"status":"UP"|"DOWN","timestamp":...}) and do not include the "components" map
for unauthenticated callers, and either (a) move the detailed components payload
into a new authenticated method/endpoint (e.g., getHealthDetails() or
checkHealthDetails()) that calls checkMySQLHealth() and checkRedisHealth(), or
(b) sanitize the components map before returning by removing sensitive keys
("host","port","database","version") and stripping raw exception/error messages;
update calls to checkMySQLHealth() and checkRedisHealth() so they still perform
checks but only surface non-sensitive boolean/status flags to the public
checkHealth().

Comment on lines +134 to +145
if (result.isHealthy) {
logger.debug("{} health check: UP ({}ms)", componentName, responseTime);
status.put("status", "UP");
details.put("responseTimeMs", responseTime);
if (result.version != null) {
details.put("version", result.version);
}
} else {
logger.warn("{} health check: {}", componentName, result.error);
status.put("status", "DOWN");
details.put("error", result.error);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

responseTimeMs is only added to details on success, not on the unhealthy (non-exception) path.

When result.isHealthy is false (Line 141–144), responseTimeMs is not added to the details map, though it is added on success (Line 137) and on exception (Line 154). This creates inconsistent response shapes.

Proposed fix
             } else {
                 logger.warn("{} health check: {}", componentName, result.error);
                 status.put("status", "DOWN");
                 details.put("error", result.error);
+                details.put("responseTimeMs", responseTime);
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (result.isHealthy) {
logger.debug("{} health check: UP ({}ms)", componentName, responseTime);
status.put("status", "UP");
details.put("responseTimeMs", responseTime);
if (result.version != null) {
details.put("version", result.version);
}
} else {
logger.warn("{} health check: {}", componentName, result.error);
status.put("status", "DOWN");
details.put("error", result.error);
}
if (result.isHealthy) {
logger.debug("{} health check: UP ({}ms)", componentName, responseTime);
status.put("status", "UP");
details.put("responseTimeMs", responseTime);
if (result.version != null) {
details.put("version", result.version);
}
} else {
logger.warn("{} health check: {}", componentName, result.error);
status.put("status", "DOWN");
details.put("error", result.error);
details.put("responseTimeMs", responseTime);
}
🤖 Prompt for AI Agents
In `@src/main/java/com/iemr/hwc/service/health/HealthService.java` around lines
134 - 145, The details map is missing responseTimeMs when result.isHealthy is
false; in HealthService (the block that checks result.isHealthy) ensure you
always put responseTimeMs into details regardless of success or failure—add
details.put("responseTimeMs", responseTimeMs) inside the else branch that
handles result.isHealthy == false (near the code referencing result.error and
status.put("status", "DOWN")) so the response shape matches the success and
exception paths.



# Keep a line at the end so that environment.ts file can be appended properly
# Keep a line at the end so that environment.ts file can be appended properly No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Removing the trailing newline contradicts the comment's own instruction.

The comment on Line 47 says to keep a line at the end so that the environment file can be appended properly. If the trailing newline was indeed removed, subsequent appends may concatenate onto this comment line instead of starting on a new line. Either restore the trailing newline or remove the comment if it's no longer applicable.

🤖 Prompt for AI Agents
In `@src/main/resources/application.properties` at line 47, The trailing newline
after the comment "Keep a line at the end so that environment.ts file can be
appended properly" was removed; restore the final newline in
src/main/resources/application.properties so that future appends to
environment.ts begin on a new line, or if the requirement is obsolete, remove
that comment line entirely—update the file to either re-add the trailing newline
after the existing comment text or delete the comment.

@DurgaPrasad-54 DurgaPrasad-54 deleted the feat/health-version-clean branch February 16, 2026 03:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant