Skip to content

fix: align API status codes and swagger#132

Open
harrshita123 wants to merge 1 commit intoPSMRI:mainfrom
harrshita123:fix/issue-113
Open

fix: align API status codes and swagger#132
harrshita123 wants to merge 1 commit intoPSMRI:mainfrom
harrshita123:fix/issue-113

Conversation

@harrshita123
Copy link
Copy Markdown

@harrshita123 harrshita123 commented May 5, 2026

📋 Description

JIRA ID: PSMRI/AMRIT#113

Previously, the API was returning an HTTP 200 OK status for almost all responses, even when an error or exception occurred. This made it difficult for client applications to properly handle errors and broke standard REST semantics.

This PR resolves this by introducing a centralized standard for error handling:

  • Added a standard ErrorResponse DTO.
  • Refactored EcdGlobalExceptionHandler to catch exceptions and return the actual proper HTTP status codes (400 for bad requests, 401 for unauthorized, 500 for internal errors, etc.).
  • Updated the constants in CustomExceptionResponse to use standard HTTP values instead of arbitrary internal integers (5008, 5002). This automatically fixes our Swagger documentation so that the @ApiResponses finally reflect the true status codes.

✅ Type of Change

  • 🐞 Bug fix (non-breaking change which resolves an issue)
  • New feature (non-breaking change which adds functionality)
  • 🔥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 🛠 Refactor (change that is neither a fix nor a new feature)
  • ⚙️ Config change (configuration file or build script updates)
  • 📚 Documentation (updates to docs or readme)
  • 🧪 Tests (adding new or updating existing tests)
  • 🎨 UI/UX (changes that affect the user interface)
  • 🚀 Performance (improves performance)
  • 🧹 Chore (miscellaneous changes that don't modify src or test files)

ℹ️ Additional Information

Testing & Context:

  • Compiled locally using mvn clean compile to ensure no checkstyle violations.
  • Verified that database exceptions (like SQLException) are safely caught by the global handler and masked behind a generic "Database error occurred" message with a 500 status code, ensuring we don't leak internal DB structures to the client.
  • Swagger UI was checked to confirm the response schemas are generating correctly.

Summary by CodeRabbit

Release Notes

  • Documentation

    • Enhanced API documentation with structured error response schemas for all endpoints.
  • Bug Fixes

    • Standardized API error responses with consistent structure and proper HTTP status codes.
    • Improved exception handling to provide clearer error messages and appropriate status codes across all error scenarios.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

📝 Walkthrough

Walkthrough

This PR standardizes error handling across the application by introducing a new ErrorResponse DTO, refactoring HTTP status code constants from internal codes to standard HTTP codes, rewriting the global exception handler to return structured ResponseEntity<ErrorResponse> objects, and updating controller OpenAPI documentation to reference the new error response schema.

Changes

Error Handling Standardization

Layer / File(s) Summary
Data Shape
src/main/java/com/iemr/ecd/dto/ErrorResponse.java
New ErrorResponse DTO with Lombok @Data and @Builder, containing timestamp (ISO format), status (int), error (String), message (String), and path (String) fields.
Exception Constants
src/main/java/com/iemr/ecd/utils/advice/exception_handler/CustomExceptionResponse.java
HTTP-standard status constants added (BAD_REQUEST = 400, UNAUTHORIZED = 401, FORBIDDEN = 403, NOT_FOUND = 404, CONFLICT = 409, INTERNAL_SERVER_ERROR = 500, DB_EXCEPTION = 500). Legacy internal numeric codes (GENERIC_FAILURE, OBJECT_FAILURE, etc.) removed. Status string and numeric string constants (*_SC, *_SC_V) updated to align with HTTP codes. Default statusCode and errorMessage initialized to INTERNAL_SERVER_ERROR and "Internal Server Error".
Exception Handler Logic
src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java
EcdGlobalExceptionHandler rewritten to return ResponseEntity<ErrorResponse> with explicit HTTP status codes. Handlers added for InvalidRequestException (400), ECDException (500), MethodArgumentNotValidException (400 with aggregated field errors), and generic Exception (500). Logger changed to static final. No longer extends ResponseEntityExceptionHandler.
OpenAPI Documentation
src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java
@ApiResponses blocks for /registration and /updateBeneficiaryDetails endpoints updated to declare structured ErrorResponse JSON schema via @Schema(implementation = ErrorResponse.class) for error responses. ErrorResponse import added.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Poem

🐰 From codes cryptic (5008!) we hop away,
To HTTP standards that shine bright as day,
Error responses now structured, clean, and true—
RESTful and righteous, the whole system through! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: aligning API status codes with proper HTTP semantics and updating Swagger documentation to reflect these changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

sonarqubecloud Bot commented May 5, 2026

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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java`:
- Around line 59-66: The OpenAPI annotations in
BeneficiaryRegistrationController declare two separate ApiResponse entries for
the same HTTP status (CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC_V and
CustomExceptionResponse.DB_EXCEPTION_SC_V), which conflicts with OpenAPI keys;
update the annotation block to merge these into a single ApiResponse for the 500
status (use either INTERNAL_SERVER_ERROR_SC_V or DB_EXCEPTION_SC_V constant) and
combine their descriptions into one unified message (or reference a shared
schema/ErrorResponse) so only one ApiResponse exists for 500; apply the same
consolidation pattern wherever you see duplicate 500 ApiResponse constants.

In
`@src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java`:
- Around line 87-98: In EcdGlobalExceptionHandler, add specific
`@ExceptionHandler` methods for HttpMessageNotReadableException,
MethodArgumentTypeMismatchException, MissingServletRequestParameterException,
and ConstraintViolationException that construct and return an ErrorResponse
(same shape as used in handleGeneralException) with HttpStatus.BAD_REQUEST and
the request path, and place these handlers before the existing
handleGeneralException(Exception ex, HttpServletRequest request) catch-all so
client input errors return 400 instead of falling through to 500; reuse the
ErrorResponse.builder() pattern and logger to log the exception details in each
new handler.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 138bd272-49fb-4405-b2f3-5bd7b14b049c

📥 Commits

Reviewing files that changed from the base of the PR and between a647b70 and 66de6ac.

📒 Files selected for processing (4)
  • src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java
  • src/main/java/com/iemr/ecd/dto/ErrorResponse.java
  • src/main/java/com/iemr/ecd/utils/advice/exception_handler/CustomExceptionResponse.java
  • src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java

Comment on lines +59 to +66
@ApiResponse(responseCode = CustomExceptionResponse.NOT_FOUND_SC_V, description = CustomExceptionResponse.NOT_FOUND_SC, content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }),
@ApiResponse(responseCode = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC_V, description = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC, content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }),
@ApiResponse(responseCode = CustomExceptionResponse.DB_EXCEPTION_SC_V, description = CustomExceptionResponse.DB_EXCEPTION_SC, content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }),
@ApiResponse(responseCode = CustomExceptionResponse.BAD_REQUEST_SC_V, description = CustomExceptionResponse.BAD_REQUEST_SC, content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }) })
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 | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In OpenAPI 3.x, if an operation defines two responses with the same HTTP status code (e.g., two "500" entries), how is that represented in the final schema and UI?

💡 Result:

In OpenAPI 3.x, the operation’s responses are a map keyed by HTTP status code (plus ranges/default). That means you cannot represent “two separate 500 response objects” under the same status code key; only one responses['500'] entry can exist in the resulting parsed schema/JSON/YAML model because each status code key is unique in the map [1][2]. So if you try to write two responses: { '500': ... , '500': ... } in YAML/JSON, the spec structure can only keep one of them (the later one overwrites the earlier one in typical parsers), and there is no native way in OAS3 to merge them as two distinct response definitions under the same HTTP status code. How to model “multiple shapes/examples for the same status code” instead 1) Put the variability inside the single responses['500'] object: - Use a single response with one content[<media-type>].schema that can represent alternatives, e.g. oneOf (or anyOf / discriminators) [3][4]. - Optionally provide multiple examples within that one response/media type (supported by Swagger UI for “multiple examples” in the cited discussions) [5]. 2) In the UI (e.g., Swagger UI): - The UI will show one response block for status 500, because there is only one response entry keyed by 500 in the parsed spec [1][2]. - If you use oneOf/anyOf at the response schema level, many UIs will typically render/select one alternative schema (often the first item) rather than two distinct 500 responses; Swagger UI’s behavior is discussed in its issue/PR history for oneOf/anyOf handling [6]. - If you instead supply multiple examples for that same 500 response, some Swagger UI versions show them as selectable alternatives (again per the cited guidance) [5]. Practical takeaway - You should not define two 500 entries at the top level under responses. - Define a single 500 response, and then model multiple possible body shapes and/or examples within it using oneOf (and/or discriminator mapping) and examples [3][5].

Citations:


🏁 Script executed:

fd -t f CustomExceptionResponse

Repository: PSMRI/ECD-API

Length of output: 144


🏁 Script executed:

find . -name "CustomExceptionResponse*" -type f

Repository: PSMRI/ECD-API

Length of output: 146


🏁 Script executed:

rg -t java "INTERNAL_SERVER_ERROR_SC_V|DB_EXCEPTION_SC_V" -A 1 -B 1

Repository: PSMRI/ECD-API

Length of output: 50369


🏁 Script executed:

wc -l src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java

Repository: PSMRI/ECD-API

Length of output: 147


🏁 Script executed:

sed -n '50,90p' src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java

Repository: PSMRI/ECD-API

Length of output: 3661


Merge duplicate 500 responses into a single entry.

Both INTERNAL_SERVER_ERROR_SC_V and DB_EXCEPTION_SC_V map to 500. In OpenAPI 3.x, response objects are keyed by HTTP status code—having two entries with the same code violates the spec structure, with the latter overwriting the former in generated documentation, resulting in inconsistent Swagger output.

Consolidate the two 500 responses into one with a unified description indicating both error scenarios, or use a single schema that can represent both error types.

This pattern appears across multiple endpoints in the codebase.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java`
around lines 59 - 66, The OpenAPI annotations in
BeneficiaryRegistrationController declare two separate ApiResponse entries for
the same HTTP status (CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC_V and
CustomExceptionResponse.DB_EXCEPTION_SC_V), which conflicts with OpenAPI keys;
update the annotation block to merge these into a single ApiResponse for the 500
status (use either INTERNAL_SERVER_ERROR_SC_V or DB_EXCEPTION_SC_V constant) and
combine their descriptions into one unified message (or reference a shared
schema/ErrorResponse) so only one ApiResponse exists for 500; apply the same
consolidation pattern wherever you see duplicate 500 ApiResponse constants.

Comment on lines +87 to +98
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneralException(Exception ex, HttpServletRequest request) {
logger.error("Unexpected error: ", ex);
ErrorResponse error = ErrorResponse.builder()
.timestamp(LocalDateTime.now())
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
.error(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase())
.message("An unexpected error occurred. Please contact administrator.")
.path(request.getRequestURI())
.build();
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether explicit handlers exist for common client-input exceptions
# Expected: no matches currently, indicating they fall through to generic Exception->500.

rg -nC2 --type=java '(`@ExceptionHandler`\()|(HttpMessageNotReadableException|MethodArgumentTypeMismatchException|MissingServletRequestParameterException|ConstraintViolationException)' \
  src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java

Repository: PSMRI/ECD-API

Length of output: 1103


🏁 Script executed:

cat -n src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java

Repository: PSMRI/ECD-API

Length of output: 4781


Add explicit handlers for client-input exceptions before generic 500 fallback.

Malformed JSON, type mismatches, missing parameters, and constraint violations currently fall through the generic Exception handler and return 500. These should return 400 to preserve REST error semantics.

Add handlers for HttpMessageNotReadableException, MethodArgumentTypeMismatchException, MissingServletRequestParameterException, and ConstraintViolationException with HttpStatus.BAD_REQUEST before the catch-all @ExceptionHandler(Exception.class).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java`
around lines 87 - 98, In EcdGlobalExceptionHandler, add specific
`@ExceptionHandler` methods for HttpMessageNotReadableException,
MethodArgumentTypeMismatchException, MissingServletRequestParameterException,
and ConstraintViolationException that construct and return an ErrorResponse
(same shape as used in handleGeneralException) with HttpStatus.BAD_REQUEST and
the request path, and place these handlers before the existing
handleGeneralException(Exception ex, HttpServletRequest request) catch-all so
client input errors return 400 instead of falling through to 500; reuse the
ErrorResponse.builder() pattern and logger to log the exception details in each
new handler.

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