Skip to content

Conversation

srishtigrp78
Copy link
Contributor

@srishtigrp78 srishtigrp78 commented Jan 9, 2025

📋 Description

JIRA ID: AMM-1139
Scheduler for fetching L3 to L5 grievance details from GR portal and saving it into the DB alongwith transactions for each grievances.

Please provide a summary of the change and the motivation behind it. Include relevant context and details.


✅ 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

Please describe how the changes were tested, and include any relevant screenshots, logs, or other information that provides additional context.

Summary by CodeRabbit

  • New Features

    • Added grievance data synchronization functionality.
    • Introduced new data models for grievance details and transactions.
    • Implemented scheduled job for grievance data sync.
  • Repositories

    • Created new repositories for managing grievance-related data.
    • Added methods for fetching and checking grievance information.
  • Services

    • Developed service for retrieving and processing grievance data.
    • Implemented authentication and data synchronization mechanisms.
    • Enhanced error handling and transaction management in the data synchronization process.

Copy link
Contributor

coderabbitai bot commented Jan 9, 2025

Warning

Rate limit exceeded

@srishtigrp78 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 53 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 5cb6886 and 9e8e1f2.

📒 Files selected for processing (1)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1 hunks)

Walkthrough

The pull request introduces a grievance data synchronization feature, adding new classes and interfaces to manage grievance details, transactions, and synchronization processes. It includes enhancements to Quartz scheduling configuration, new data repositories, and a service for processing grievance data from an external system. These changes enable automated periodic synchronization of grievance information while ensuring robust error handling and transaction management.

Changes

File Change Summary
src/main/java/com/iemr/common/config/quartz/QuartzConfig.java Added methods for creating job and trigger for grievance data synchronization. Updated existing trigger method for clarity.
src/main/java/com/iemr/common/config/quartz/ScheduleForGrievanceDataSync.java New Quartz job class for executing grievance data sync.
src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java New entity class representing grievance details with JPA annotations, including fields and relationships.
src/main/java/com/iemr/common/data/grievance/GrievanceTransaction.java New entity class for grievance transaction details with JPA annotations and relationships.
src/main/java/com/iemr/common/repository/grievance/GrievanceDataRepo.java New repository interface for grievance details with custom query method to check existence by complaint ID.
src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java New repository interface for fetching beneficiary details related to grievances with a custom query method.
src/main/java/com/iemr/common/repository/grievance/GrievanceTransactionRepo.java New repository interface for grievance transactions.
src/main/java/com/iemr/common/repository/location/LocationStateRepository.java Added method for finding state by ID for grievances.
src/main/java/com/iemr/common/service/grievance/GrievanceDataSync.java New interface for grievance data synchronization with method declaration.
src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java Implementation of grievance data synchronization service, including data fetching, error handling, and token management.

Sequence Diagram

sequenceDiagram
    participant Scheduler
    participant GrievanceJob
    participant GrievanceDataSyncService
    participant ExternalAPI
    participant Database

    Scheduler->>GrievanceJob: Trigger Job
    GrievanceJob->>GrievanceDataSyncService: Initiate Data Sync
    GrievanceDataSyncService->>GrievanceDataSyncService: Generate Auth Token
    GrievanceDataSyncService->>ExternalAPI: Fetch Grievance Data
    ExternalAPI-->>GrievanceDataSyncService: Return Grievance Details
    GrievanceDataSyncService->>Database: Save Grievance Details
    GrievanceDataSyncService->>Database: Save Grievance Transactions
Loading

Poem

🐰 Hop, hop, grievance sync is here,
Data dancing, crystal clear!
Quartz ticks, transactions flow,
Rabbits sync what systems know 🌈
Synchronization magic, oh so neat! 📊


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 11

🧹 Nitpick comments (6)
src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java (1)

1-20: Verify repository's role in grievance data synchronization.

Given that this PR implements a scheduler for fetching L3-L5 grievance details:

  1. Consider adding methods specific to L3-L5 grievance filtering
  2. Add transaction support for batch operations
  3. Consider implementing pagination for large data sets

Let's verify the integration with the scheduler:

#!/bin/bash
# Search for scheduler-related configurations and usage
rg -A 5 "GrievanceFetchBenDetailsRepo|@Scheduled.*grievance" --type java

# Search for transaction management in related files
rg "@Transactional.*grievance" --type java
src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (2)

45-45: Declare the logger as private static final for proper usage.

For consistency and adherence to best practices, the logger should be declared as private static final.

Apply this diff to update the logger declaration:

-    Logger logger = LoggerFactory.getLogger(this.getClass().getName());
+    private static final Logger logger = LoggerFactory.getLogger(GrievanceDataSyncImpl.class);

141-179: Remove commented-out code to improve maintainability.

The block of code between lines 141 and 179 is commented out. Removing unused code enhances readability and maintainability.

src/main/java/com/iemr/common/config/quartz/ScheduleForGrievanceDataSync.java (1)

23-29: Add exception handling in the execute method to handle potential errors.

To ensure that any exceptions during job execution are properly logged and do not cause the scheduler to fail, wrap the code in a try-catch block.

Apply this diff to include exception handling:

 @Override
 public void execute(JobExecutionContext arg0) throws JobExecutionException
 {
     logger.info("Started job for grievance data sync " + arg0.getClass().getName());
+    try {
         grievanceDataSync.dataSyncToGrievance();
+    } catch (Exception e) {
+        logger.error("Error during grievance data sync job execution", e);
+        throw new JobExecutionException(e);
+    }
     logger.info("Completed job for grievance data sync " + arg0.getClass().getName());
 }
src/main/java/com/iemr/common/data/grievance/GrievanceTransaction.java (1)

34-36: Maintain consistent field naming conventions.

Some fields don't follow Java naming conventions:

  • Processed starts with uppercase
  • Inconsistent use of camelCase vs PascalCase

Apply consistent naming:

-private Character Processed = 'N';
+private Character processed = 'N';

Also applies to: 57-59, 98-99

src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (1)

116-127: Maintain consistent field naming conventions.

Several fields don't follow Java naming conventions:

  • VanID, VanSerialNo, ParkingPlaceID use inconsistent casing

Apply consistent camelCase naming:

-private Integer VanID;
-private Integer VanSerialNo;
-private Integer ParkingPlaceID;
+private Integer vanId;
+private Integer vanSerialNo;
+private Integer parkingPlaceId;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc77f01 and 21be52e.

📒 Files selected for processing (10)
  • src/main/java/com/iemr/common/config/quartz/QuartzConfig.java (2 hunks)
  • src/main/java/com/iemr/common/config/quartz/ScheduleForGrievanceDataSync.java (1 hunks)
  • src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (1 hunks)
  • src/main/java/com/iemr/common/data/grievance/GrievanceTransaction.java (1 hunks)
  • src/main/java/com/iemr/common/repository/grievance/GrievanceDataRepo.java (1 hunks)
  • src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java (1 hunks)
  • src/main/java/com/iemr/common/repository/grievance/GrievanceTransactionRepo.java (1 hunks)
  • src/main/java/com/iemr/common/repository/location/LocationStateRepository.java (1 hunks)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSync.java (1 hunks)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/main/java/com/iemr/common/repository/grievance/GrievanceTransactionRepo.java
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSync.java
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Analyze (java)
  • GitHub Check: Build
  • GitHub Check: Package-test
🔇 Additional comments (3)
src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java (2)

1-11: LGTM! Package structure and imports are well-organized.


13-14: Verify repository naming and entity relationship.

The repository name suggests it handles grievance beneficiary details, but it extends CrudRepository<FeedbackDetails, Long>. This might indicate:

  1. A potential naming inconsistency between grievance and feedback domains
  2. A design decision to store grievance data in the feedback table

Please clarify if this is intentional or if we should consider:

  • Renaming to FeedbackDetailsRepository for clarity
  • Creating a separate entity for grievance data

Let's verify the relationship between feedback and grievance domains:

✅ Verification successful

Repository name and entity relationship are architecturally sound

The codebase shows that grievances are intentionally modeled as part of the feedback domain, with FeedbackDetails serving as the underlying entity. The repository name GrievanceFetchBenDetailsRepo appropriately describes its specialized purpose of fetching beneficiary-related grievance data from the feedback store.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of FeedbackDetails to understand the domain relationship
rg -l "FeedbackDetails" --type java

# Search for any existing grievance-specific entities
rg -l "Grievance.*\.java$" --type java

Length of output: 2731

src/main/java/com/iemr/common/config/quartz/QuartzConfig.java (1)

232-253: LGTM! Well-structured scheduler configuration.

The implementation follows the established pattern and includes proper configuration for both job and trigger beans.

if (response != null && response.hasBody()) {
JSONObject obj = new JSONObject(response.getBody());
if (obj != null && obj.has("data") && obj.has("statusCode") && obj.getInt("statusCode") == 200) {
logger.info("Grievance data details response: " + response.getBody());
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid logging sensitive data in production logs.

Logging the entire response body may expose sensitive information. Consider removing or sanitizing the response before logging.

Apply this diff to adjust the logging:

-    logger.info("Grievance data details response: " + response.getBody());
+    logger.info("Grievance data details response received successfully.");
📝 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
logger.info("Grievance data details response: " + response.getBody());
logger.info("Grievance data details response received successfully.");


@Service
@PropertySource("classpath:application.properties")
public class GrievanceDataSyncImpl {
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 9, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement the GrievanceDataSync interface in the class declaration.

To ensure that GrievanceDataSyncImpl correctly implements the GrievanceDataSync interface and follows the defined contract, the class declaration should explicitly implement the interface.

Apply this diff to fix the issue:

-public class GrievanceDataSyncImpl {
+public class GrievanceDataSyncImpl implements GrievanceDataSync {

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Thank you for implementing the interface! This will help ensure type safety and contract adherence.


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 79 to 81
private static String GRIEVANCE_AUTH_TOKEN;
private static Long GRIEVANCE_TOKEN_EXP;

Copy link
Contributor

@coderabbitai coderabbitai bot Jan 9, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using static variables for instance-specific state to ensure thread safety.

Using static variables GRIEVANCE_AUTH_TOKEN and GRIEVANCE_TOKEN_EXP in a service class can lead to thread safety issues in a multi-threaded environment. Consider changing them to instance variables or using thread-safe mechanisms.

Apply this diff to fix the issue:

-private static String GRIEVANCE_AUTH_TOKEN;
-private static Long GRIEVANCE_TOKEN_EXP;
+private String grievanceAuthToken;
+private Long grievanceTokenExp;

Also, update references to these variables throughout the class accordingly.

📝 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
private static String GRIEVANCE_AUTH_TOKEN;
private static Long GRIEVANCE_TOKEN_EXP;
private String grievanceAuthToken;
private Long grievanceTokenExp;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Thank you for addressing the thread safety concern by making the variables instance-scoped! 🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!


HttpEntity<Object> request = new HttpEntity<Object>(headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(updateGrievanceTransactionDetails, HttpMethod.POST, request, String.class);
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 9, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Include complaintId in the API call to fetch transaction details.

The complaintId parameter is not being used in the API request. Ensure that the complaintId is included in the request to fetch the correct grievance transactions.

Apply this diff to include complaintId in the request:

 ResponseEntity<String> response = restTemplate.exchange(
-    updateGrievanceTransactionDetails, HttpMethod.POST, request, String.class);
+    updateGrievanceTransactionDetails + "?complaintId=" + URLEncoder.encode(complaintId, "UTF-8"),
+    HttpMethod.POST, request, String.class);

Ensure to handle UnsupportedEncodingException and properly encode the parameter.

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Great! Thank you for implementing the fix. 🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 54 to 55
@Query("select state from States state where state.stateID = :id")
public String findByStateIDForGrievance(@Param("id") int id);
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 9, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider consolidating duplicate query methods.

The new method findByStateIDForGrievance uses the same query as findByStateID but returns String instead of States. This creates unnecessary duplication and could lead to confusion.

Consider one of these approaches:

  1. If you need a specific field, modify the query to select only that field:
-@Query("select state from States state where state.stateID = :id")
+@Query("select state.stateName from States state where state.stateID = :id")
  1. Or use the existing findByStateID method and extract the required field in the service layer.
📝 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
@Query("select state from States state where state.stateID = :id")
public String findByStateIDForGrievance(@Param("id") int id);
@Query("select state.stateName from States state where state.stateID = :id")
public String findByStateIDForGrievance(@Param("id") int id);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Thank you for addressing the duplicate query methods! 🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 15 to 88
public class GrievanceTransaction {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "gtid")
private Long gtid;

@Column(name = "gwid", nullable = false)
@Expose
private Long gwid;

@Column(name = "FileName", nullable = false, length = 500)
@Expose
private String fileName;

@Column(name = "FileType", nullable = false, length = 300)
@Expose
private String fileType;

@Column(name = "Redressed", nullable = false)
@Expose
private String redressed;

@Column(name = "createdAt", nullable = false)
@Expose
private Date createdAt;

@Column(name = "updatedAt", nullable = false)
@Expose
private Date updatedAt;

@Column(name = "Comments", length = 500)
private String comments;

@Column(name = "ProviderServiceMapID")
@Expose
private Integer providerServiceMapID;

@Column(name = "Deleted", insertable = false, updatable = true)
@Expose
private Boolean deleted;

@Column(name = "Processed", nullable = false)
@Expose
private String processed;


@Column(name = "ActionTakenBy")
@Expose
private String actionTakenBy;

@Column(name = "Status")
@Expose
private String status;

@Column(name = "Comment")
@Expose
private String comment;

@Column(name = "CreatedBy", nullable = false)
@Expose
private String createdBy;

@Column(name = "CreatedDate", nullable = false)
private Date createdDate;

@Column(name = "ModifiedBy")
@Expose
private String modifiedBy;

@Column(name = "LastModDate")
@Expose
private Date lastModDate;

Copy link
Contributor

@coderabbitai coderabbitai bot Jan 9, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add JPA relationship mapping with GrievanceDetails.

The entity lacks a proper JPA relationship mapping with its parent GrievanceDetails entity.

Add the following relationship mapping:

+@ManyToOne(fetch = FetchType.LAZY)
+@JoinColumn(name = "gwid", insertable = false, updatable = false)
+private GrievanceDetails grievanceDetails;
📝 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
public class GrievanceTransaction {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "gtid")
private Long gtid;
@Column(name = "gwid", nullable = false)
@Expose
private Long gwid;
@Column(name = "FileName", nullable = false, length = 500)
@Expose
private String fileName;
@Column(name = "FileType", nullable = false, length = 300)
@Expose
private String fileType;
@Column(name = "Redressed", nullable = false)
@Expose
private String redressed;
@Column(name = "createdAt", nullable = false)
@Expose
private Date createdAt;
@Column(name = "updatedAt", nullable = false)
@Expose
private Date updatedAt;
@Column(name = "Comments", length = 500)
private String comments;
@Column(name = "ProviderServiceMapID")
@Expose
private Integer providerServiceMapID;
@Column(name = "Deleted", insertable = false, updatable = true)
@Expose
private Boolean deleted;
@Column(name = "Processed", nullable = false)
@Expose
private String processed;
@Column(name = "ActionTakenBy")
@Expose
private String actionTakenBy;
@Column(name = "Status")
@Expose
private String status;
@Column(name = "Comment")
@Expose
private String comment;
@Column(name = "CreatedBy", nullable = false)
@Expose
private String createdBy;
@Column(name = "CreatedDate", nullable = false)
private Date createdDate;
@Column(name = "ModifiedBy")
@Expose
private String modifiedBy;
@Column(name = "LastModDate")
@Expose
private Date lastModDate;
public class GrievanceTransaction {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "gtid")
private Long gtid;
@Column(name = "gwid", nullable = false)
@Expose
private Long gwid;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "gwid", insertable = false, updatable = false)
private GrievanceDetails grievanceDetails;
@Column(name = "FileName", nullable = false, length = 500)
@Expose
private String fileName;
@Column(name = "FileType", nullable = false, length = 300)
@Expose
private String fileType;
@Column(name = "Redressed", nullable = false)
@Expose
private String redressed;
@Column(name = "createdAt", nullable = false)
@Expose
private Date createdAt;
@Column(name = "updatedAt", nullable = false)
@Expose
private Date updatedAt;
@Column(name = "Comments", length = 500)
private String comments;
@Column(name = "ProviderServiceMapID")
@Expose
private Integer providerServiceMapID;
@Column(name = "Deleted", insertable = false, updatable = true)
@Expose
private Boolean deleted;
@Column(name = "Processed", nullable = false)
@Expose
private String processed;
@Column(name = "ActionTakenBy")
@Expose
private String actionTakenBy;
@Column(name = "Status")
@Expose
private String status;
@Column(name = "Comment")
@Expose
private String comment;
@Column(name = "CreatedBy", nullable = false)
@Expose
private String createdBy;
@Column(name = "CreatedDate", nullable = false)
private Date createdDate;
@Column(name = "ModifiedBy")
@Expose
private String modifiedBy;
@Column(name = "LastModDate")
@Expose
private Date lastModDate;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Thank you for implementing the JPA relationship mapping! 🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Great! Thank you for implementing the JPA relationship mapping. 🎉

(`∇´ฅ)

@Column(name = "isCompleted")
private Boolean isCompleted = false;

private List<GrievanceTransaction> grievanceTransactionDetails;
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 9, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add JPA relationship mapping for grievanceTransactionDetails.

The field lacks proper JPA relationship mapping with GrievanceTransaction entity.

Add the following annotations:

+@OneToMany(mappedBy = "grievanceDetails", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
+@JsonManagedReference
private List<GrievanceTransaction> grievanceTransactionDetails;
📝 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
private List<GrievanceTransaction> grievanceTransactionDetails;
@OneToMany(mappedBy = "grievanceDetails", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JsonManagedReference
private List<GrievanceTransaction> grievanceTransactionDetails;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Thank you for confirming the fix! The JPA relationship mapping is now properly configured.


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Copy link
Contributor

@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: 6

🧹 Nitpick comments (1)
src/main/java/com/iemr/common/service/grievance/GrievanceDataSync.java (1)

6-9: Consider using more descriptive parameter names and a custom return type.

The interface could benefit from:

  1. More descriptive parameter names to indicate their purpose
  2. A custom return type instead of List<Map<String, Object>> for better type safety

Apply this diff to improve the interface:

-public List<Map<String, Object>> dataSyncToGrievance(String grievanceAuthorization, String registeringUser,
-            String Authorization);
+public List<GrievanceResponse> dataSyncToGrievance(String grievanceToken, String registeredUsername,
+            String authorizationKey);

+public class GrievanceResponse {
+    private String complaintID;
+    private String subjectOfComplaint;
+    private String complaint;
+    private Long beneficiaryRegID;
+    private Integer providerServiceMapId;
+    private List<GrievanceTransactionResponse> transactions;
+    // Add other fields, getters, and setters
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21be52e and 0a90728.

📒 Files selected for processing (6)
  • src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (1 hunks)
  • src/main/java/com/iemr/common/data/grievance/GrievanceTransaction.java (1 hunks)
  • src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java (1 hunks)
  • src/main/java/com/iemr/common/repository/location/LocationStateRepository.java (1 hunks)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSync.java (1 hunks)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/iemr/common/repository/location/LocationStateRepository.java
  • src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: style-check / checkstyle
  • GitHub Check: Analyze (java)
  • GitHub Check: Build
  • GitHub Check: Package-test
🔇 Additional comments (2)
src/main/java/com/iemr/common/data/grievance/GrievanceTransaction.java (1)

8-14: Remove redundant code generation.

The class uses both Lombok's @Data annotation and manual getters/setters, creating redundancy.

src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1)

79-80: ⚠️ Potential issue

Use instance variables for token management to ensure thread safety.

Static variables for token management can cause issues in a multi-threaded environment.

Apply this diff to fix thread safety:

-private String GRIEVANCE_AUTH_TOKEN;
-private Long GRIEVANCE_TOKEN_EXP;
+private volatile String grievanceAuthToken;
+private volatile Long grievanceTokenExp;

Likely invalid or redundant comment.

Comment on lines 231 to 237
public String comment( ) {
return comment;
}

public void setComment(String comment ) {
this.comment = comment;
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 16, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix method name inconsistency.

The method name comment() doesn't follow the getter naming convention.

Apply this diff to fix the method name:

-public String comment( ) {
+public String getComment() {
📝 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
public String comment( ) {
return comment;
}
public void setComment(String comment ) {
this.comment = comment;
}
public String getComment() {
return comment;
}
public void setComment(String comment ) {
this.comment = comment;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Thank you for fixing the method name! 🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 26 to 36
@Column(name = "FileName", nullable = false, length = 500)
@Expose
private String fileName;

@Column(name = "FileType", nullable = false, length = 300)
@Expose
private String fileType;

@Column(name = "Redressed", nullable = false)
@Expose
private String redressed;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation constraints for file-related fields.

The file-related fields should have proper validation constraints to ensure data integrity.

Apply this diff to add validation:

+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;

 @Column(name = "FileName", nullable = false, length = 500)
 @Expose
+@NotBlank(message = "File name is required")
+@Size(max = 500, message = "File name cannot exceed 500 characters")
 private String fileName;

 @Column(name = "FileType", nullable = false, length = 300)
 @Expose
+@NotBlank(message = "File type is required")
+@Size(max = 300, message = "File type cannot exceed 300 characters")
 private String fileType;
📝 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
@Column(name = "FileName", nullable = false, length = 500)
@Expose
private String fileName;
@Column(name = "FileType", nullable = false, length = 300)
@Expose
private String fileType;
@Column(name = "Redressed", nullable = false)
@Expose
private String redressed;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
@Column(name = "FileName", nullable = false, length = 500)
@Expose
@NotBlank(message = "File name is required")
@Size(max = 500, message = "File name cannot exceed 500 characters")
private String fileName;
@Column(name = "FileType", nullable = false, length = 300)
@Expose
@NotBlank(message = "File type is required")
@Size(max = 300, message = "File type cannot exceed 300 characters")
private String fileType;
@Column(name = "Redressed", nullable = false)
@Expose
private String redressed;

Comment on lines 63 to 69
@Expose
@Column(name = "Severety")
private String severety;

@Expose
@Column(name = "Level")
private String level;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in column name and add validation.

The column name "Severety" is misspelled and lacks validation.

Apply this diff to fix the issue:

 @Expose
-@Column(name = "Severety")
-private String severety;
+@Column(name = "Severity")
+@NotBlank(message = "Severity is required")
+private String severity;
📝 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
@Expose
@Column(name = "Severety")
private String severety;
@Expose
@Column(name = "Level")
private String level;
@Expose
@Column(name = "Severity")
@NotBlank(message = "Severity is required")
private String severity;
@Expose
@Column(name = "Level")
private String level;

Comment on lines 119 to 132
@Expose
@Column(name = "VanSerialNo")
private Integer VanSerialNo;
@Expose
@Column(name = "VanID")
private Integer VanID;

@Expose
@Column(name = "VehicalNo")
private String VehicalNo;
@Expose
@Column(name = "ParkingPlaceID")
private Integer ParkingPlaceID;
@Expose
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Follow Java naming conventions for field names.

Field names should follow camelCase convention in Java.

Apply this diff to fix field naming:

-private Integer VanSerialNo;
-private Integer VanID;
-private String VehicalNo;
-private Integer ParkingPlaceID;
+private Integer vanSerialNo;
+private Integer vanId;
+private String vehicleNo;  // Also fixed typo in 'Vehical'
+private Integer parkingPlaceId;
📝 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
@Expose
@Column(name = "VanSerialNo")
private Integer VanSerialNo;
@Expose
@Column(name = "VanID")
private Integer VanID;
@Expose
@Column(name = "VehicalNo")
private String VehicalNo;
@Expose
@Column(name = "ParkingPlaceID")
private Integer ParkingPlaceID;
@Expose
@Expose
@Column(name = "VanSerialNo")
private Integer vanSerialNo;
@Expose
@Column(name = "VanID")
private Integer vanId;
@Expose
@Column(name = "VehicalNo")
private String vehicleNo;
@Expose
@Column(name = "ParkingPlaceID")
private Integer parkingPlaceId;
@Expose

Copy link
Contributor

@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

🧹 Nitpick comments (2)
src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (2)

21-24: Replace @DaTa with more specific Lombok annotations.

Using @Data with JPA entities can lead to performance issues due to lazy loading. Consider using a combination of @Getter, @Setter, and @EqualsAndHashCode(exclude = "grievanceTransactionDetails") instead.

-@Data
+@Getter
+@Setter
+@EqualsAndHashCode(exclude = "grievanceTransactionDetails")

24-24: Add JPA auditing for better tracking of changes.

Since this entity handles sensitive grievance data, consider adding JPA auditing annotations for better tracking:

+@EntityListeners(AuditingEntityListener.class)
 public class GrievanceDetails {

Also add the following imports:

import org.springframework.data.jpa.domain.support.AuditingEntityListener;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea9ff9e and 8825473.

📒 Files selected for processing (2)
  • src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (1 hunks)
  • src/main/java/com/iemr/common/data/grievance/GrievanceTransaction.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/iemr/common/data/grievance/GrievanceTransaction.java
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: style-check / checkstyle
  • GitHub Check: Package-test
  • GitHub Check: Analyze (java)
  • GitHub Check: Build
🔇 Additional comments (2)
src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (2)

65-67: Fix typos and inconsistent field naming.

The field names contain typos and don't follow Java naming conventions:

  • "Severety" should be "Severity"
  • "VehicalNo" should be "vehicleNo"

Also applies to: 129-130


146-148: LGTM! Well-structured relationship mapping.

The one-to-many relationship with GrievanceTransaction is properly configured with appropriate cascade type and fetch strategy.

Comment on lines 151 to 189
public GrievanceDetails(Long gwid, Long grievanceId, Long beneficiaryRegID, Long benCallID,
Integer providerServiceMapID, String complaintID, String subjectOfComplaint, String complaint,
String primaryNumber, String severety, String state, String agentID, String userid, Boolean isAllocated,
Boolean retryNeeded, Boolean isRegistered, Integer callCounter, Boolean deleted, Character processed,
String createdBy, Timestamp createdDate, String modifiedBy, Timestamp lastModDate, Integer vanSerialNo,
Integer vanID, String vehicalNo, Integer parkingPlaceID, String syncedBy, Timestamp syncedDate,
Boolean isCompleted) {
super();
this.gwid = gwid;
this.grievanceId = grievanceId;
this.beneficiaryRegID = beneficiaryRegID;
this.benCallID = benCallID;
this.providerServiceMapID = providerServiceMapID;
this.complaintID = complaintID;
this.subjectOfComplaint = subjectOfComplaint;
this.complaint = complaint;
this.primaryNumber = primaryNumber;
this.severety = severety;
this.state = state;
this.agentid = agentID;
this.userid = userid;
this.isAllocated = isAllocated;
this.retryNeeded = retryNeeded;
this.isRegistered = isRegistered;
this.callCounter = callCounter;
this.deleted = deleted;
this.Processed = processed;
this.createdBy = createdBy;
this.createdDate = createdDate;
this.modifiedBy = modifiedBy;
this.lastModDate = lastModDate;
this.vanSerialNo = vanSerialNo;
this.vanID = vanID;
this.vehicalNo = vehicalNo;
this.parkingPlaceID = parkingPlaceID;
this.syncedBy = syncedBy;
this.syncedDate = syncedDate;
this.isCompleted = isCompleted;
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 16, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix inconsistencies in constructor parameters and setter methods.

There are naming inconsistencies between fields, constructor parameters, and setter methods:

  1. Constructor parameter agentID doesn't match field name agentid
  2. Method setSeverity doesn't match field name severety
-public void setSeverity(String severety) {
+public void setSeverety(String severety) {
     this.severety = severety;
 }

Also applies to: 268-270

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Thank you for addressing the naming inconsistencies! 🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Copy link
Contributor

@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: 1

♻️ Duplicate comments (7)
src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (4)

65-67: ⚠️ Potential issue

Fix typo in column name and add validation.

The column name "Severety" is misspelled and should be "Severity".


57-62: ⚠️ Potential issue

Consider encrypting sensitive PII data.

Fields containing PII should be encrypted:

  • primaryNumber: Contains contact information

122-133: ⚠️ Potential issue

Follow Java naming conventions for field names.

Field names should follow camelCase convention and be consistent with their column names.


268-270: ⚠️ Potential issue

Fix inconsistencies in setter method name.

The setter method name doesn't match the field name:

  • Method: setSeverity
  • Field: severety
src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (3)

84-85: ⚠️ Potential issue

Use instance variables instead of static variables.

Using static variables for token storage can lead to thread safety issues in a multi-threaded environment.


284-286: ⚠️ Potential issue

Improve exception handling.

The catch block swallows exceptions after logging them. Consider rethrowing a custom exception.


158-162: 🛠️ Refactor suggestion

Enhance error handling for duplicate complaints.

Consider using a custom exception for duplicate complaints instead of RuntimeException.

🧹 Nitpick comments (1)
src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (1)

21-24: Add class-level documentation.

Consider adding Javadoc to describe the purpose and functionality of this entity class.

+/**
+ * Entity class representing grievance details in the system.
+ * Maps to the t_grievanceworklist table in the database.
+ */
 @Entity
 @Table(name = "t_grievanceworklist")
 @Data
 public class GrievanceDetails {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8825473 and 2d639c4.

📒 Files selected for processing (6)
  • src/main/java/com/iemr/common/config/quartz/QuartzConfig.java (3 hunks)
  • src/main/java/com/iemr/common/config/quartz/ScheduleForGrievanceDataSync.java (1 hunks)
  • src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (1 hunks)
  • src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java (1 hunks)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSync.java (1 hunks)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSync.java
  • src/main/java/com/iemr/common/config/quartz/ScheduleForGrievanceDataSync.java
  • src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java
🔇 Additional comments (4)
src/main/java/com/iemr/common/data/grievance/GrievanceDetails.java (1)

146-148: LGTM! Well-structured relationship mapping.

The one-to-many relationship with GrievanceTransaction is properly configured with appropriate JPA annotations.

src/main/java/com/iemr/common/config/quartz/QuartzConfig.java (2)

100-101: LGTM! Proper integration of new triggers.

The grievance data sync trigger is correctly integrated into the scheduler configuration.


173-173: Improved null check using Boolean.TRUE.equals().

Good practice using Boolean.TRUE.equals(startJob) for null-safe comparison.

src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1)

353-353: ⚠️ Potential issue

Avoid logging sensitive data.

The auth token should not be logged in production environments.

-logger.info("Auth key generated at : " + System.currentTimeMillis() + ", Key : " + GRIEVANCE_AUTH_TOKEN);
+logger.info("Auth key generated at : " + System.currentTimeMillis());

Likely invalid or redundant comment.

Comment on lines 365 to 371
while (count > 0) {
List<Map<String, Object>> savedGrievanceData = dataSyncToGrievance();
if (savedGrievanceData != null)
break;
else
count--;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add timeout mechanism for retry logic.

The retry loop should include a delay between attempts and a timeout mechanism.

 while (count > 0) {
+    try {
         List<Map<String, Object>> savedGrievanceData = dataSyncToGrievance();
         if (savedGrievanceData != null)
             break;
-        else
-            count--;
+        else {
+            count--;
+            if (count > 0) {
+                Thread.sleep(5000); // 5 seconds delay between retries
+            }
+        }
+    } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+        break;
+    }
 }
📝 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
while (count > 0) {
List<Map<String, Object>> savedGrievanceData = dataSyncToGrievance();
if (savedGrievanceData != null)
break;
else
count--;
}
while (count > 0) {
try {
List<Map<String, Object>> savedGrievanceData = dataSyncToGrievance();
if (savedGrievanceData != null)
break;
else {
count--;
if (count > 0) {
Thread.sleep(5000); // 5 seconds delay between retries
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}

Copy link
Contributor

@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: 3

🧹 Nitpick comments (2)
src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (2)

359-365: Improve retry mechanism with exponential backoff.

The current retry mechanism doesn't include any delay between attempts. Implement exponential backoff to avoid overwhelming the system.

 int count = 3;
 while (count > 0) {
+    try {
         List<Map<String, Object>> savedGrievanceData = dataSyncToGrievance();
         if (savedGrievanceData != null)
             break;
-        else
-            count--;
+        else {
+            count--;
+            if (count > 0) {
+                Thread.sleep((3 - count) * 1000); // Exponential backoff
+            }
+        }
+    } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+        break;
+    }
 }

349-356: Simplify token expiration calculation using Java 8 Time API.

The current date manipulation code is verbose and uses the legacy Calendar API. Consider using the Java 8 Time API for better readability and maintainability.

-    Date date = new Date();
-    java.sql.Date sqlDate = new java.sql.Date(date.getTime());
-    Calendar grievanceCalendar = Calendar.getInstance();
-    grievanceCalendar.setTime(sqlDate);
-    grievanceCalendar.add(Calendar.DATE, 29);
-    Date grievanceTokenEndDate = grievanceCalendar.getTime();
-    GRIEVANCE_TOKEN_EXP = grievanceTokenEndDate.getTime();
+    GRIEVANCE_TOKEN_EXP = LocalDateTime.now()
+        .plusDays(29)
+        .atZone(ZoneId.systemDefault())
+        .toInstant()
+        .toEpochMilli();
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d639c4 and b0e039b.

📒 Files selected for processing (3)
  • src/main/java/com/iemr/common/config/quartz/ScheduleForGrievanceDataSync.java (1 hunks)
  • src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java (1 hunks)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/iemr/common/repository/grievance/GrievanceFetchBenDetailsRepo.java
  • src/main/java/com/iemr/common/config/quartz/ScheduleForGrievanceDataSync.java
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Analyze (java)
  • GitHub Check: Build
  • GitHub Check: Package-test
🔇 Additional comments (1)
src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1)

88-89: 🛠️ Refactor suggestion

Consider using instance variables for token management.

Using instance variables for GRIEVANCE_AUTH_TOKEN and GRIEVANCE_TOKEN_EXP would be more appropriate in a multi-instance environment. Also, follow Java naming conventions for instance variables.

-    private String GRIEVANCE_AUTH_TOKEN;
-    private Long GRIEVANCE_TOKEN_EXP;
+    private String grievanceAuthToken;
+    private Long grievanceTokenExp;

Likely invalid or redundant comment.

Comment on lines 171 to 184
JsonObject transactionDetailsJson = grievanceJsonData.getAsJsonObject("transactionDetails"); // or another relevant path

// Adding properties for each transaction detail
// transactionDetails.setComplaintId(formattedComplaintId);
// Assuming these fields are coming from your API response
transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
transactionDetails.setStatus(transactionDetailsJson.get("status").getAsString());
transactionDetails.setFileName(transactionDetailsJson.has("fileName") ? transactionDetailsJson.get("fileName").getAsString() : null);
transactionDetails.setFileType(transactionDetailsJson.has("fileType") ? transactionDetailsJson.get("fileType").getAsString() : null);
transactionDetails.setRedressed(transactionDetailsJson.get("redressed").getAsString());
transactionDetails.setCreatedAt(Timestamp.valueOf(transactionDetailsJson.get("createdAt").getAsString()));
transactionDetails.setUpdatedAt(Timestamp.valueOf(transactionDetailsJson.get("updatedAt").getAsString()));
transactionDetails.setComment(transactionDetailsJson.get("comment").getAsString());

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential NullPointerException in transaction processing.

The code assumes that all fields exist in the transaction JSON. Add null checks before accessing JSON fields.

-    transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
+    if (transactionDetailsJson.has("actionTakenBy")) {
+        transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
+    }
📝 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
JsonObject transactionDetailsJson = grievanceJsonData.getAsJsonObject("transactionDetails"); // or another relevant path
// Adding properties for each transaction detail
// transactionDetails.setComplaintId(formattedComplaintId);
// Assuming these fields are coming from your API response
transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
transactionDetails.setStatus(transactionDetailsJson.get("status").getAsString());
transactionDetails.setFileName(transactionDetailsJson.has("fileName") ? transactionDetailsJson.get("fileName").getAsString() : null);
transactionDetails.setFileType(transactionDetailsJson.has("fileType") ? transactionDetailsJson.get("fileType").getAsString() : null);
transactionDetails.setRedressed(transactionDetailsJson.get("redressed").getAsString());
transactionDetails.setCreatedAt(Timestamp.valueOf(transactionDetailsJson.get("createdAt").getAsString()));
transactionDetails.setUpdatedAt(Timestamp.valueOf(transactionDetailsJson.get("updatedAt").getAsString()));
transactionDetails.setComment(transactionDetailsJson.get("comment").getAsString());
JsonObject transactionDetailsJson = grievanceJsonData.getAsJsonObject("transactionDetails"); // or another relevant path
// Adding properties for each transaction detail
// transactionDetails.setComplaintId(formattedComplaintId);
// Assuming these fields are coming from your API response
if (transactionDetailsJson.has("actionTakenBy")) {
transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
}
if (transactionDetailsJson.has("status")) {
transactionDetails.setStatus(transactionDetailsJson.get("status").getAsString());
}
transactionDetails.setFileName(transactionDetailsJson.has("fileName") ? transactionDetailsJson.get("fileName").getAsString() : null);
transactionDetails.setFileType(transactionDetailsJson.has("fileType") ? transactionDetailsJson.get("fileType").getAsString() : null);
if (transactionDetailsJson.has("redressed")) {
transactionDetails.setRedressed(transactionDetailsJson.get("redressed").getAsString());
}
if (transactionDetailsJson.has("createdAt")) {
transactionDetails.setCreatedAt(Timestamp.valueOf(transactionDetailsJson.get("createdAt").getAsString()));
}
if (transactionDetailsJson.has("updatedAt")) {
transactionDetails.setUpdatedAt(Timestamp.valueOf(transactionDetailsJson.get("updatedAt").getAsString()));
}
if (transactionDetailsJson.has("comment")) {
transactionDetails.setComment(transactionDetailsJson.get("comment").getAsString());
}

HttpEntity<Object> request = new HttpEntity<Object>(headers);
RestTemplate restTemplate = new RestTemplate();

ResponseEntity<String> response = restTemplate.exchange(updateGrievanceTransactionDetails + grievanceId, HttpMethod.POST, request, String.class);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Secure URL construction for transaction details.

The grievance ID is directly concatenated to the URL, which could be vulnerable to injection attacks. Use UriComponentsBuilder to safely construct the URL.

-    ResponseEntity<String> response = restTemplate.exchange(updateGrievanceTransactionDetails + grievanceId, HttpMethod.POST, request, String.class);
+    String url = UriComponentsBuilder.fromUriString(updateGrievanceTransactionDetails)
+        .queryParam("grievanceId", grievanceId)
+        .build()
+        .toUriString();
+    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
📝 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
ResponseEntity<String> response = restTemplate.exchange(updateGrievanceTransactionDetails + grievanceId, HttpMethod.POST, request, String.class);
String url = UriComponentsBuilder.fromUriString(updateGrievanceTransactionDetails)
.queryParam("grievanceId", grievanceId)
.build()
.toUriString();
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);

Comment on lines 92 to 284
public List<Map<String, Object>> dataSyncToGrievance() {

int count = 0;
String registeringUser = "";
List<Map<String, Object>> responseData = new ArrayList<>();
List<GrievanceDetails> grievanceDetailsListAS = null;
try {
// Loop to fetch data for multiple pages
while (count >= 0) {
RestTemplate restTemplate = new RestTemplate();

if (GRIEVANCE_AUTH_TOKEN != null && GRIEVANCE_TOKEN_EXP != null
&& GRIEVANCE_TOKEN_EXP > System.currentTimeMillis()) {
// no need of calling auth API
} else {
// call method to generate Auth Token at Everwell end
generateGrievanceAuthToken();
}

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add(USER_AGENT_HEADER, USER_AGENT_VALUE);
headers.add("AUTHORIZATION", GRIEVANCE_AUTH_TOKEN);

Date date = new Date();
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
Calendar calendar = Calendar.getInstance();
calendar.setTime(sqlDate);
calendar.add(Calendar.DATE, -Integer.parseInt(grievanceDataSyncDuration));

// Request object
HttpEntity<Object> request = new HttpEntity<Object>(headers);

// Call rest-template to call API to download master data for given table
ResponseEntity<String> response = restTemplate.exchange(updateGrievanceDetails, HttpMethod.POST, request,
String.class);

if (response != null && response.hasBody()) {
JSONObject obj = new JSONObject(response.getBody());
if (obj != null && obj.has("data") && obj.has(STATUS_CODE) && obj.getInt(STATUS_CODE) == 200) {
logger.info("Grievance data details response received successfully ");

String responseStr = response.getBody();
JsonObject jsnOBJ = new JsonObject();
JsonParser jsnParser = new JsonParser();
JsonElement jsnElmnt = jsnParser.parse(responseStr);
jsnOBJ = jsnElmnt.getAsJsonObject();
JsonObject grievanceJsonData = jsnOBJ.getAsJsonObject("data");

registeringUser = grievanceJsonData.get("userName").getAsString();

if (Integer.parseInt(jsnOBJ.get("TotalRecords").toString()) > 0) {
GrievanceDetails[] grievanceDetailsArray = InputMapper.gson().fromJson(jsnOBJ.get("Data").toString(), GrievanceDetails[].class);
List<GrievanceDetails> grievanceDetailsList = Arrays.asList(grievanceDetailsArray);


//////////////////////
// Loop through the fetched grievance list and integrate transaction details
for (GrievanceDetails grievance : grievanceDetailsList) {
String complaintId = grievanceJsonData.get("complainId").getAsString();
String formattedComplaintId = complaintId.replace("\\/", "/");

// Check if the complaintId is already present in the t_grievance_worklist table
boolean complaintExists = grievanceDataRepo.existsByComplaintId(formattedComplaintId);
if (complaintExists) {
throw new RuntimeException("Complaint ID " + formattedComplaintId + " already exists in the grievance worklist table.");
}


grievance.setComplaintId(formattedComplaintId);

// Fetch related grievance transaction details
List<GrievanceTransaction> transactionDetailsList = fetchGrievanceTransactions(grievance.getGrievanceId());

if (transactionDetailsList != null && !transactionDetailsList.isEmpty()) {
// Loop through each transaction and set individual properties
for (GrievanceTransaction transactionDetails : transactionDetailsList) {

// Assuming transactionDetailsJson is the JSON object representing a single transaction from the API response
JsonObject transactionDetailsJson = grievanceJsonData.getAsJsonObject("transactionDetails"); // or another relevant path

// Adding properties for each transaction detail
// transactionDetails.setComplaintId(formattedComplaintId);
// Assuming these fields are coming from your API response
transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
transactionDetails.setStatus(transactionDetailsJson.get("status").getAsString());
transactionDetails.setFileName(transactionDetailsJson.has("fileName") ? transactionDetailsJson.get("fileName").getAsString() : null);
transactionDetails.setFileType(transactionDetailsJson.has("fileType") ? transactionDetailsJson.get("fileType").getAsString() : null);
transactionDetails.setRedressed(transactionDetailsJson.get("redressed").getAsString());
transactionDetails.setCreatedAt(Timestamp.valueOf(transactionDetailsJson.get("createdAt").getAsString()));
transactionDetails.setUpdatedAt(Timestamp.valueOf(transactionDetailsJson.get("updatedAt").getAsString()));
transactionDetails.setComment(transactionDetailsJson.get("comment").getAsString());

// Save individual transaction detail in the t_grievance_transaction table
grievanceTransactionRepo.save(transactionDetails);
}

// Add the transaction list to the grievance object
grievance.setGrievanceTransactionDetails(transactionDetailsList);
}

// Adding other grievance-related fields (similar to the existing code)
grievance.setSubjectOfComplaint(grievanceJsonData.get("subject").getAsString());
ArrayList<Object[]> lists = grievanceFetchBenDetailsRepo.findByComplaintId(formattedComplaintId);

for (Object[] objects : lists) {
if (objects != null && objects.length <= 4) {
grievance.setComplaintId((String) objects[0]);
grievance.setBeneficiaryRegId((Long) objects[1]);
grievance.setBencallId((Long) objects[2]);
grievance.setProviderServiceMapId((Integer) objects[3]);
String state = locationStateRepository.findByStateIDForGrievance((Integer) objects[4]);
grievance.setState(state);
}
}

// Setting remaining grievance properties (similar to the existing code)
grievance.setAgentId(grievance.getAgentId());
grievance.setDeleted(grievance.getDeleted());
grievance.setCreatedBy(registeringUser);
grievance.setProcessed('N');
grievance.setIsAllocated(false);
grievance.setCallCounter(0);
grievance.setRetryNeeded(true);
}

// Save the grievance details to the t_grievance table
grievanceDetailsListAS = (List<GrievanceDetails>) grievanceDataRepo.saveAll(grievanceDetailsList);

// Combine grievance and transaction data for response

for (GrievanceDetails grievance : grievanceDetailsListAS) {
Map<String, Object> combinedData = new HashMap<>();
combinedData.put("complaintID", grievance.getGrievanceId());
combinedData.put("subjectOfComplaint", grievance.getSubjectOfComplaint());
combinedData.put("complaint", grievance.getComplaint());
combinedData.put("beneficiaryRegID", grievance.getBeneficiaryRegId());
combinedData.put("providerServiceMapId", grievance.getProviderServiceMapId());
// combinedData.put("firstName", grievance.getFirstName());
// combinedData.put("lastName", grievance.getLastName());
combinedData.put("primaryNumber", grievance.getPrimaryNumber());

// Add transaction data
List<Map<String, Object>> transactions = new ArrayList<>();
for (GrievanceTransaction transaction : grievance.getGrievanceTransactionDetails()) {
Map<String, Object> transactionData = new HashMap<>();
transactionData.put("actionTakenBy", transaction.getActionTakenBy());
transactionData.put("status", transaction.getStatus());
transactionData.put("fileName", transaction.getFileName());
transactionData.put("fileType", transaction.getFileType());
transactionData.put("redressed", transaction.getRedressed());
transactionData.put("createdAt", transaction.getCreatedAt().toString());
transactionData.put("updatedAt", transaction.getUpdatedAt().toString());
transactionData.put("comment", transaction.getComment());
transactions.add(transactionData);
}

combinedData.put("transaction", transactions);
combinedData.put("severity", grievance.getSeverety());
combinedData.put("state", grievance.getState());
combinedData.put("agentId", grievance.getAgentId());
combinedData.put("deleted", grievance.getDeleted());
combinedData.put("createdBy", grievance.getCreatedBy());
combinedData.put("createdDate", grievance.getCreatedDate());
combinedData.put("lastModDate", grievance.getLastModDate());
combinedData.put("isCompleted", grievance.getIsCompleted());
// combinedData.put("gender", grievance.getGender());
// combinedData.put("district", grievance.getDistrict());
// combinedData.put("beneficiaryID", grievance.getBeneficiaryId());
// combinedData.put("age", grievance.getAge());
combinedData.put("retryNeeded", grievance.getRetryNeeded());
combinedData.put("callCounter", grievance.getCallCounter());
// combinedData.put("lastCall", grievance.getLastCall());

responseData.add(combinedData);
}


// Return the combined response as required
// return responseData;

} else {
logger.info("No records found for page = " + count);
count = -1;
}
}
}
}
} catch (Exception e) {
logger.error("Error in saving data into t_grievanceworklist: ", e);
}
return responseData;
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 16, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor long method to improve maintainability.

The dataSyncToGrievance method is too long and handles multiple responsibilities. Consider breaking it down into smaller, focused methods.

  1. Extract grievance processing logic (lines 150-216) into a separate method
  2. Extract response mapping logic (lines 223-267) into a separate method
  3. Consider using a builder pattern for response construction

Also, improve error handling to avoid swallowing exceptions:

 } catch (Exception e) {
     logger.error("Error in saving data into t_grievanceworklist: ", e);
+    throw new GrievanceProcessingException("Failed to process grievance data", e);
 }
📝 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
public List<Map<String, Object>> dataSyncToGrievance() {
int count = 0;
String registeringUser = "";
List<Map<String, Object>> responseData = new ArrayList<>();
List<GrievanceDetails> grievanceDetailsListAS = null;
try {
// Loop to fetch data for multiple pages
while (count >= 0) {
RestTemplate restTemplate = new RestTemplate();
if (GRIEVANCE_AUTH_TOKEN != null && GRIEVANCE_TOKEN_EXP != null
&& GRIEVANCE_TOKEN_EXP > System.currentTimeMillis()) {
// no need of calling auth API
} else {
// call method to generate Auth Token at Everwell end
generateGrievanceAuthToken();
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add(USER_AGENT_HEADER, USER_AGENT_VALUE);
headers.add("AUTHORIZATION", GRIEVANCE_AUTH_TOKEN);
Date date = new Date();
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
Calendar calendar = Calendar.getInstance();
calendar.setTime(sqlDate);
calendar.add(Calendar.DATE, -Integer.parseInt(grievanceDataSyncDuration));
// Request object
HttpEntity<Object> request = new HttpEntity<Object>(headers);
// Call rest-template to call API to download master data for given table
ResponseEntity<String> response = restTemplate.exchange(updateGrievanceDetails, HttpMethod.POST, request,
String.class);
if (response != null && response.hasBody()) {
JSONObject obj = new JSONObject(response.getBody());
if (obj != null && obj.has("data") && obj.has(STATUS_CODE) && obj.getInt(STATUS_CODE) == 200) {
logger.info("Grievance data details response received successfully ");
String responseStr = response.getBody();
JsonObject jsnOBJ = new JsonObject();
JsonParser jsnParser = new JsonParser();
JsonElement jsnElmnt = jsnParser.parse(responseStr);
jsnOBJ = jsnElmnt.getAsJsonObject();
JsonObject grievanceJsonData = jsnOBJ.getAsJsonObject("data");
registeringUser = grievanceJsonData.get("userName").getAsString();
if (Integer.parseInt(jsnOBJ.get("TotalRecords").toString()) > 0) {
GrievanceDetails[] grievanceDetailsArray = InputMapper.gson().fromJson(jsnOBJ.get("Data").toString(), GrievanceDetails[].class);
List<GrievanceDetails> grievanceDetailsList = Arrays.asList(grievanceDetailsArray);
//////////////////////
// Loop through the fetched grievance list and integrate transaction details
for (GrievanceDetails grievance : grievanceDetailsList) {
String complaintId = grievanceJsonData.get("complainId").getAsString();
String formattedComplaintId = complaintId.replace("\\/", "/");
// Check if the complaintId is already present in the t_grievance_worklist table
boolean complaintExists = grievanceDataRepo.existsByComplaintId(formattedComplaintId);
if (complaintExists) {
throw new RuntimeException("Complaint ID " + formattedComplaintId + " already exists in the grievance worklist table.");
}
grievance.setComplaintId(formattedComplaintId);
// Fetch related grievance transaction details
List<GrievanceTransaction> transactionDetailsList = fetchGrievanceTransactions(grievance.getGrievanceId());
if (transactionDetailsList != null && !transactionDetailsList.isEmpty()) {
// Loop through each transaction and set individual properties
for (GrievanceTransaction transactionDetails : transactionDetailsList) {
// Assuming transactionDetailsJson is the JSON object representing a single transaction from the API response
JsonObject transactionDetailsJson = grievanceJsonData.getAsJsonObject("transactionDetails"); // or another relevant path
// Adding properties for each transaction detail
// transactionDetails.setComplaintId(formattedComplaintId);
// Assuming these fields are coming from your API response
transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
transactionDetails.setStatus(transactionDetailsJson.get("status").getAsString());
transactionDetails.setFileName(transactionDetailsJson.has("fileName") ? transactionDetailsJson.get("fileName").getAsString() : null);
transactionDetails.setFileType(transactionDetailsJson.has("fileType") ? transactionDetailsJson.get("fileType").getAsString() : null);
transactionDetails.setRedressed(transactionDetailsJson.get("redressed").getAsString());
transactionDetails.setCreatedAt(Timestamp.valueOf(transactionDetailsJson.get("createdAt").getAsString()));
transactionDetails.setUpdatedAt(Timestamp.valueOf(transactionDetailsJson.get("updatedAt").getAsString()));
transactionDetails.setComment(transactionDetailsJson.get("comment").getAsString());
// Save individual transaction detail in the t_grievance_transaction table
grievanceTransactionRepo.save(transactionDetails);
}
// Add the transaction list to the grievance object
grievance.setGrievanceTransactionDetails(transactionDetailsList);
}
// Adding other grievance-related fields (similar to the existing code)
grievance.setSubjectOfComplaint(grievanceJsonData.get("subject").getAsString());
ArrayList<Object[]> lists = grievanceFetchBenDetailsRepo.findByComplaintId(formattedComplaintId);
for (Object[] objects : lists) {
if (objects != null && objects.length <= 4) {
grievance.setComplaintId((String) objects[0]);
grievance.setBeneficiaryRegId((Long) objects[1]);
grievance.setBencallId((Long) objects[2]);
grievance.setProviderServiceMapId((Integer) objects[3]);
String state = locationStateRepository.findByStateIDForGrievance((Integer) objects[4]);
grievance.setState(state);
}
}
// Setting remaining grievance properties (similar to the existing code)
grievance.setAgentId(grievance.getAgentId());
grievance.setDeleted(grievance.getDeleted());
grievance.setCreatedBy(registeringUser);
grievance.setProcessed('N');
grievance.setIsAllocated(false);
grievance.setCallCounter(0);
grievance.setRetryNeeded(true);
}
// Save the grievance details to the t_grievance table
grievanceDetailsListAS = (List<GrievanceDetails>) grievanceDataRepo.saveAll(grievanceDetailsList);
// Combine grievance and transaction data for response
for (GrievanceDetails grievance : grievanceDetailsListAS) {
Map<String, Object> combinedData = new HashMap<>();
combinedData.put("complaintID", grievance.getGrievanceId());
combinedData.put("subjectOfComplaint", grievance.getSubjectOfComplaint());
combinedData.put("complaint", grievance.getComplaint());
combinedData.put("beneficiaryRegID", grievance.getBeneficiaryRegId());
combinedData.put("providerServiceMapId", grievance.getProviderServiceMapId());
// combinedData.put("firstName", grievance.getFirstName());
// combinedData.put("lastName", grievance.getLastName());
combinedData.put("primaryNumber", grievance.getPrimaryNumber());
// Add transaction data
List<Map<String, Object>> transactions = new ArrayList<>();
for (GrievanceTransaction transaction : grievance.getGrievanceTransactionDetails()) {
Map<String, Object> transactionData = new HashMap<>();
transactionData.put("actionTakenBy", transaction.getActionTakenBy());
transactionData.put("status", transaction.getStatus());
transactionData.put("fileName", transaction.getFileName());
transactionData.put("fileType", transaction.getFileType());
transactionData.put("redressed", transaction.getRedressed());
transactionData.put("createdAt", transaction.getCreatedAt().toString());
transactionData.put("updatedAt", transaction.getUpdatedAt().toString());
transactionData.put("comment", transaction.getComment());
transactions.add(transactionData);
}
combinedData.put("transaction", transactions);
combinedData.put("severity", grievance.getSeverety());
combinedData.put("state", grievance.getState());
combinedData.put("agentId", grievance.getAgentId());
combinedData.put("deleted", grievance.getDeleted());
combinedData.put("createdBy", grievance.getCreatedBy());
combinedData.put("createdDate", grievance.getCreatedDate());
combinedData.put("lastModDate", grievance.getLastModDate());
combinedData.put("isCompleted", grievance.getIsCompleted());
// combinedData.put("gender", grievance.getGender());
// combinedData.put("district", grievance.getDistrict());
// combinedData.put("beneficiaryID", grievance.getBeneficiaryId());
// combinedData.put("age", grievance.getAge());
combinedData.put("retryNeeded", grievance.getRetryNeeded());
combinedData.put("callCounter", grievance.getCallCounter());
// combinedData.put("lastCall", grievance.getLastCall());
responseData.add(combinedData);
}
// Return the combined response as required
// return responseData;
} else {
logger.info("No records found for page = " + count);
count = -1;
}
}
}
}
} catch (Exception e) {
logger.error("Error in saving data into t_grievanceworklist: ", e);
}
return responseData;
}
public List<Map<String, Object>> dataSyncToGrievance() {
int count = 0;
String registeringUser = "";
List<Map<String, Object>> responseData = new ArrayList<>();
List<GrievanceDetails> grievanceDetailsListAS = null;
try {
// Loop to fetch data for multiple pages
while (count >= 0) {
RestTemplate restTemplate = new RestTemplate();
if (GRIEVANCE_AUTH_TOKEN != null && GRIEVANCE_TOKEN_EXP != null
&& GRIEVANCE_TOKEN_EXP > System.currentTimeMillis()) {
// no need of calling auth API
} else {
// call method to generate Auth Token at Everwell end
generateGrievanceAuthToken();
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add(USER_AGENT_HEADER, USER_AGENT_VALUE);
headers.add("AUTHORIZATION", GRIEVANCE_AUTH_TOKEN);
Date date = new Date();
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
Calendar calendar = Calendar.getInstance();
calendar.setTime(sqlDate);
calendar.add(Calendar.DATE, -Integer.parseInt(grievanceDataSyncDuration));
// Request object
HttpEntity<Object> request = new HttpEntity<Object>(headers);
// Call rest-template to call API to download master data for given table
ResponseEntity<String> response = restTemplate.exchange(updateGrievanceDetails, HttpMethod.POST, request,
String.class);
if (response != null && response.hasBody()) {
JSONObject obj = new JSONObject(response.getBody());
if (obj != null && obj.has("data") && obj.has(STATUS_CODE) && obj.getInt(STATUS_CODE) == 200) {
logger.info("Grievance data details response received successfully ");
String responseStr = response.getBody();
JsonObject jsnOBJ = new JsonObject();
JsonParser jsnParser = new JsonParser();
JsonElement jsnElmnt = jsnParser.parse(responseStr);
jsnOBJ = jsnElmnt.getAsJsonObject();
JsonObject grievanceJsonData = jsnOBJ.getAsJsonObject("data");
registeringUser = grievanceJsonData.get("userName").getAsString();
if (Integer.parseInt(jsnOBJ.get("TotalRecords").toString()) > 0) {
GrievanceDetails[] grievanceDetailsArray = InputMapper.gson().fromJson(jsnOBJ.get("Data").toString(), GrievanceDetails[].class);
List<GrievanceDetails> grievanceDetailsList = Arrays.asList(grievanceDetailsArray);
//////////////////////
// Loop through the fetched grievance list and integrate transaction details
for (GrievanceDetails grievance : grievanceDetailsList) {
String complaintId = grievanceJsonData.get("complainId").getAsString();
String formattedComplaintId = complaintId.replace("\\/", "/");
// Check if the complaintId is already present in the t_grievance_worklist table
boolean complaintExists = grievanceDataRepo.existsByComplaintId(formattedComplaintId);
if (complaintExists) {
throw new RuntimeException("Complaint ID " + formattedComplaintId + " already exists in the grievance worklist table.");
}
grievance.setComplaintId(formattedComplaintId);
// Fetch related grievance transaction details
List<GrievanceTransaction> transactionDetailsList = fetchGrievanceTransactions(grievance.getGrievanceId());
if (transactionDetailsList != null && !transactionDetailsList.isEmpty()) {
// Loop through each transaction and set individual properties
for (GrievanceTransaction transactionDetails : transactionDetailsList) {
// Assuming transactionDetailsJson is the JSON object representing a single transaction from the API response
JsonObject transactionDetailsJson = grievanceJsonData.getAsJsonObject("transactionDetails"); // or another relevant path
// Adding properties for each transaction detail
// transactionDetails.setComplaintId(formattedComplaintId);
// Assuming these fields are coming from your API response
transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
transactionDetails.setStatus(transactionDetailsJson.get("status").getAsString());
transactionDetails.setFileName(transactionDetailsJson.has("fileName") ? transactionDetailsJson.get("fileName").getAsString() : null);
transactionDetails.setFileType(transactionDetailsJson.has("fileType") ? transactionDetailsJson.get("fileType").getAsString() : null);
transactionDetails.setRedressed(transactionDetailsJson.get("redressed").getAsString());
transactionDetails.setCreatedAt(Timestamp.valueOf(transactionDetailsJson.get("createdAt").getAsString()));
transactionDetails.setUpdatedAt(Timestamp.valueOf(transactionDetailsJson.get("updatedAt").getAsString()));
transactionDetails.setComment(transactionDetailsJson.get("comment").getAsString());
// Save individual transaction detail in the t_grievance_transaction table
grievanceTransactionRepo.save(transactionDetails);
}
// Add the transaction list to the grievance object
grievance.setGrievanceTransactionDetails(transactionDetailsList);
}
// Adding other grievance-related fields (similar to the existing code)
grievance.setSubjectOfComplaint(grievanceJsonData.get("subject").getAsString());
ArrayList<Object[]> lists = grievanceFetchBenDetailsRepo.findByComplaintId(formattedComplaintId);
for (Object[] objects : lists) {
if (objects != null && objects.length <= 4) {
grievance.setComplaintId((String) objects[0]);
grievance.setBeneficiaryRegId((Long) objects[1]);
grievance.setBencallId((Long) objects[2]);
grievance.setProviderServiceMapId((Integer) objects[3]);
String state = locationStateRepository.findByStateIDForGrievance((Integer) objects[4]);
grievance.setState(state);
}
}
// Setting remaining grievance properties (similar to the existing code)
grievance.setAgentId(grievance.getAgentId());
grievance.setDeleted(grievance.getDeleted());
grievance.setCreatedBy(registeringUser);
grievance.setProcessed('N');
grievance.setIsAllocated(false);
grievance.setCallCounter(0);
grievance.setRetryNeeded(true);
}
// Save the grievance details to the t_grievance table
grievanceDetailsListAS = (List<GrievanceDetails>) grievanceDataRepo.saveAll(grievanceDetailsList);
// Combine grievance and transaction data for response
for (GrievanceDetails grievance : grievanceDetailsListAS) {
Map<String, Object> combinedData = new HashMap<>();
combinedData.put("complaintID", grievance.getGrievanceId());
combinedData.put("subjectOfComplaint", grievance.getSubjectOfComplaint());
combinedData.put("complaint", grievance.getComplaint());
combinedData.put("beneficiaryRegID", grievance.getBeneficiaryRegId());
combinedData.put("providerServiceMapId", grievance.getProviderServiceMapId());
// combinedData.put("firstName", grievance.getFirstName());
// combinedData.put("lastName", grievance.getLastName());
combinedData.put("primaryNumber", grievance.getPrimaryNumber());
// Add transaction data
List<Map<String, Object>> transactions = new ArrayList<>();
for (GrievanceTransaction transaction : grievance.getGrievanceTransactionDetails()) {
Map<String, Object> transactionData = new HashMap<>();
transactionData.put("actionTakenBy", transaction.getActionTakenBy());
transactionData.put("status", transaction.getStatus());
transactionData.put("fileName", transaction.getFileName());
transactionData.put("fileType", transaction.getFileType());
transactionData.put("redressed", transaction.getRedressed());
transactionData.put("createdAt", transaction.getCreatedAt().toString());
transactionData.put("updatedAt", transaction.getUpdatedAt().toString());
transactionData.put("comment", transaction.getComment());
transactions.add(transactionData);
}
combinedData.put("transaction", transactions);
combinedData.put("severity", grievance.getSeverety());
combinedData.put("state", grievance.getState());
combinedData.put("agentId", grievance.getAgentId());
combinedData.put("deleted", grievance.getDeleted());
combinedData.put("createdBy", grievance.getCreatedBy());
combinedData.put("createdDate", grievance.getCreatedDate());
combinedData.put("lastModDate", grievance.getLastModDate());
combinedData.put("isCompleted", grievance.getIsCompleted());
// combinedData.put("gender", grievance.getGender());
// combinedData.put("district", grievance.getDistrict());
// combinedData.put("beneficiaryID", grievance.getBeneficiaryId());
// combinedData.put("age", grievance.getAge());
combinedData.put("retryNeeded", grievance.getRetryNeeded());
combinedData.put("callCounter", grievance.getCallCounter());
// combinedData.put("lastCall", grievance.getLastCall());
responseData.add(combinedData);
}
// Return the combined response as required
// return responseData;
} else {
logger.info("No records found for page = " + count);
count = -1;
}
}
}
}
} catch (Exception e) {
logger.error("Error in saving data into t_grievanceworklist: ", e);
throw new GrievanceProcessingException("Failed to process grievance data", e);
}
return responseData;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@srishtigrp78 Thank you for addressing the refactoring suggestions! This will help improve the code maintainability.


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Copy link
Contributor

@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

🧹 Nitpick comments (1)
src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1)

344-347: Make token expiration time configurable.

The token expiration time is hardcoded to 29 days. This should be configurable through application properties.

+    @Value("${grievance.token.expiration.days:29}")
+    private int tokenExpirationDays;

-            grievanceCalendar.add(Calendar.DATE, 29);
+            grievanceCalendar.add(Calendar.DATE, tokenExpirationDays);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0e039b and 9831500.

📒 Files selected for processing (1)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1 hunks)
🔇 Additional comments (4)
src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (4)

290-290: ⚠️ Potential issue

Use UriComponentsBuilder for safe URL construction.

Direct string concatenation in URLs is unsafe and could lead to injection vulnerabilities.

-    ResponseEntity<String> response = restTemplate.exchange(updateGrievanceTransactionDetails + grievanceId, HttpMethod.POST, request, String.class);
+    String url = UriComponentsBuilder.fromUriString(updateGrievanceTransactionDetails)
+        .queryParam("grievanceId", grievanceId)
+        .build()
+        .toUriString();
+    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);

Likely invalid or redundant comment.


273-275: ⚠️ Potential issue

Improve exception handling.

The catch block swallows exceptions after logging them. This could hide critical errors and make debugging difficult.

 } catch (Exception e) {
     logger.error("Error in saving data into t_grievanceworklist: ", e);
+    throw new GrievanceProcessingException("Failed to process grievance data", e);
 }

Likely invalid or redundant comment.


350-356: 🛠️ Refactor suggestion

Improve retry mechanism with proper delay and timeout.

The retry logic should include a delay between attempts and a proper timeout mechanism.

-            while (count > 0) {
-                List<Map<String, Object>> savedGrievanceData = dataSyncToGrievance();
-                if (savedGrievanceData != null)
-                    break;
-                else
-                    count--;
-            }
+            while (count > 0) {
+                try {
+                    List<Map<String, Object>> savedGrievanceData = dataSyncToGrievance();
+                    if (savedGrievanceData != null) {
+                        break;
+                    }
+                    count--;
+                    if (count > 0) {
+                        Thread.sleep(5000); // 5 seconds delay between retries
+                    }
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    break;
+                }
+            }

Likely invalid or redundant comment.


88-89: ⚠️ Potential issue

Convert static fields to instance variables for thread safety.

The authentication token and expiration time should be instance variables to ensure thread safety in a multi-threaded environment.

-    private String GRIEVANCE_AUTH_TOKEN;
-    private Long GRIEVANCE_TOKEN_EXP;
+    private volatile String grievanceAuthToken;
+    private volatile Long grievanceTokenExp;

Likely invalid or redundant comment.

Comment on lines +70 to +86
@Value("${greivanceUserAuthenticate}")
private String grievanceUserAuthenticate;

@Value("${updateGrievanceDetails}")
private String updateGrievanceDetails;

@Value("${updateGrievanceTransactionDetails}")
private String updateGrievanceTransactionDetails;

@Value("${grievanceUserName}")
private String grievanceUserName;

@Value("${grievancePassword}")
private String grievancePassword;

@Value("${grievanceDataSyncDuration}")
private String grievanceDataSyncDuration;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for injected configuration values.

The @value injected properties should be validated during initialization to fail fast if any required configuration is missing.

Add a @PostConstruct method to validate the configuration:

@PostConstruct
private void validateConfiguration() {
    Assert.hasText(grievanceUserAuthenticate, "grievanceUserAuthenticate must not be empty");
    Assert.hasText(updateGrievanceDetails, "updateGrievanceDetails must not be empty");
    Assert.hasText(updateGrievanceTransactionDetails, "updateGrievanceTransactionDetails must not be empty");
    Assert.hasText(grievanceUserName, "grievanceUserName must not be empty");
    Assert.hasText(grievancePassword, "grievancePassword must not be empty");
    Assert.hasText(grievanceDataSyncDuration, "grievanceDataSyncDuration must not be empty");
}

Copy link
Contributor

@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: 1

🧹 Nitpick comments (1)
src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1)

42-44: Add class-level documentation.

Add Javadoc to describe the class's purpose, responsibilities, and its role in the grievance synchronization process.

+/**
+ * Service implementation responsible for synchronizing grievance data with external systems.
+ * Handles authentication, data fetching, and persistence of grievance details and transactions.
+ */
 @Service
 @PropertySource("classpath:application.properties")
 public class GrievanceDataSyncImpl implements GrievanceDataSync {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9831500 and 5cb6886.

📒 Files selected for processing (1)
  • src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Package-test
  • GitHub Check: Analyze (java)
  • GitHub Check: Build
🔇 Additional comments (5)
src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java (5)

349-367: Well-implemented retry mechanism with proper thread handling.

The retry logic includes appropriate delay between attempts and proper handling of thread interruption.


290-290: ⚠️ Potential issue

Use UriComponentsBuilder for secure URL construction.

Direct string concatenation for URL construction could be vulnerable to injection attacks.

-    ResponseEntity<String> response = restTemplate.exchange(updateGrievanceTransactionDetails + grievanceId, HttpMethod.POST, request, String.class);
+    String url = UriComponentsBuilder.fromUriString(updateGrievanceTransactionDetails)
+        .queryParam("grievanceId", grievanceId)
+        .build()
+        .toUriString();
+    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);

Likely invalid or redundant comment.


273-275: ⚠️ Potential issue

Improve exception handling.

The catch block swallows exceptions after logging them, which might hide important errors.

 } catch (Exception e) {
     logger.error("Error in saving data into t_grievanceworklist: ", e);
+    throw new GrievanceProcessingException("Failed to process grievance data", e);
 }

Likely invalid or redundant comment.


70-90: 🛠️ Refactor suggestion

Add validation for required configuration properties.

Add a @PostConstruct method to validate all required properties during initialization to fail fast if any configuration is missing.

+import javax.annotation.PostConstruct;
+import org.springframework.util.Assert;
+
+@PostConstruct
+private void validateConfiguration() {
+    Assert.hasText(grievanceUserAuthenticate, "grievanceUserAuthenticate must not be empty");
+    Assert.hasText(updateGrievanceDetails, "updateGrievanceDetails must not be empty");
+    Assert.hasText(updateGrievanceTransactionDetails, "updateGrievanceTransactionDetails must not be empty");
+    Assert.hasText(grievanceUserName, "grievanceUserName must not be empty");
+    Assert.hasText(grievancePassword, "grievancePassword must not be empty");
+    Assert.hasText(grievanceDataSyncDuration, "grievanceDataSyncDuration must not be empty");
+}

Likely invalid or redundant comment.


171-183: ⚠️ Potential issue

Add null checks for JSON fields.

Direct access to JSON fields without null checks could lead to NullPointerException.

-    transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
+    if (transactionDetailsJson.has("actionTakenBy")) {
+        transactionDetails.setActionTakenBy(transactionDetailsJson.get("actionTakenBy").getAsString());
+    }

Likely invalid or redundant comment.

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
9.1% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link
Member

@ravishanigarapu ravishanigarapu left a comment

Choose a reason for hiding this comment

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

looks ok

@ravishanigarapu ravishanigarapu merged commit b7d3c80 into PSMRI:develop Jan 17, 2025
6 of 7 checks passed
vanitha1822 added a commit to AMRIT-Navadhiti/Common-API that referenced this pull request Jan 21, 2025
@coderabbitai coderabbitai bot mentioned this pull request Jan 22, 2025
10 tasks
@coderabbitai coderabbitai bot mentioned this pull request Jan 31, 2025
10 tasks
This was referenced Mar 20, 2025
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.

2 participants