Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
03da110
Adding api's for syncing diagnostic documents to s3
sehjotsinghunthinkable Sep 11, 2026
0782c44
fix: add the endpoints and schema for downsync from central to local
vanitha1822 Aug 20, 2026
ad2ccaa
fix: downsync implementation
vanitha1822 Aug 24, 2026
b0ca63f
fix: build issue
vanitha1822 Aug 24, 2026
e9ddd75
fix: lastmoddate updation
vanitha1822 Aug 25, 2026
2c957c9
fix: update the response of downsync
vanitha1822 Aug 27, 2026
d0438c1
fix: update the continuous call on check sync progress
vanitha1822 Aug 27, 2026
6eb9221
fix: column name issue for stoptb tables
vanitha1822 Aug 27, 2026
af58fa0
fix: issue in stoptb tables
vanitha1822 Aug 27, 2026
c4a4142
fix: add central id
vanitha1822 Sep 1, 2026
0e957e3
fix: FK Mapping issue
vanitha1822 Sep 1, 2026
48c98e8
fix: downsync issue
vanitha1822 Sep 1, 2026
4c11d31
fix: FK issue in i_ beneficiarymapping table
vanitha1822 Sep 1, 2026
e267058
fix: include U for downsync again
vanitha1822 Sep 1, 2026
7dc70ef
fix: revert central id for m_beneficiaryregidmapping
vanitha1822 Sep 1, 2026
5442526
fix: conflict section
vanitha1822 Sep 1, 2026
1b65b93
fix: manage conflicts and add F flag for resync
vanitha1822 Sep 1, 2026
a4c35a9
fix: pom version update
vanitha1822 Sep 2, 2026
ae5a424
fix: add the condition to block the download from different van
vanitha1822 Sep 2, 2026
2fedd6d
fix: resolve foreign key violation
vanitha1822 Sep 2, 2026
ddf96cd
fix: retry failed records
vanitha1822 Sep 2, 2026
f1c5078
fix: update 'F' flag
vanitha1822 Sep 2, 2026
8009ccc
fix: sonarqube comments
vanitha1822 Sep 4, 2026
df18a20
fix: add clinical staff information
vanitha1822 Jul 14, 2026
3fc6583
fix: pom version
vanitha1822 Jul 14, 2026
f757174
fix: resolve sonarcube comments
vanitha1822 Sep 4, 2026
4c62154
fix: issue in m_beneficiaryregidmapping table
vanitha1822 Sep 9, 2026
80304ef
Adding api's for syncing diagnostic documents to s3
sehjotsinghunthinkable Sep 11, 2026
24ae8ec
Merge branch 'release-3.9.0' into feature/diagnostic-document-sync
sehjotsinghunthinkable Sep 11, 2026
5d1182a
Adding api's for syncing diagnostic documents to s3
sehjotsinghunthinkable Sep 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,12 @@
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- AWS SDK v2 - S3 client, used for diagnostic document sync -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.11.0</version>
</dependency>
<!-- CSV parsing for the Nikshay results-file import (quoted/embedded-comma fields) -->
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down
11 changes: 11 additions & 0 deletions src/main/environment/common_ci.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ spring.datasource.username=@env.DATABASE_USERNAME@
spring.datasource.password=@env.DATABASE_PASSWORD@
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

## S3 storage for diagnostic documents pushed from a van
aws.s3.access-key=@env.AWS_S3_ACCESS_KEY@
aws.s3.secret-key=@env.AWS_S3_SECRET_KEY@
aws.s3.region=@env.AWS_S3_REGION@
diagnostic.documents.s3.bucket=@env.DIAGNOSTIC_DOCUMENTS_S3_BUCKET@

## Carestream URLs, local
carestreamOrderCreateURL=@env.COMMON_API@carestream/createOrder

Expand All @@ -28,6 +34,11 @@ dataSyncUploadUrl=@env.MMU_CENTRAL_SERVER@dataSync/van-to-server
## Data download API, central
dataSyncDownloadUrl=@env.MMU_CENTRAL_SERVER@dataSync/server-to-van

## Diagnostic document push (this server -> further central server)
diagnosticDocumentUploadUrl=@env.MMU_CENTRAL_SERVER@dataSync/diagnostic-documents
diagnosticDocument.push.batchSize=@env.DIAGNOSTIC_DOCUMENT_PUSH_BATCH_SIZE@
diagnostic.documents.storage-root=@env.DIAGNOSTIC_DOCUMENTS_STORAGE_ROOT@

## TC specialist slot booking, # not required
tcSpecialistSlotBook=@env.TM_API@schedule/bookSlot

Expand Down
11 changes: 11 additions & 0 deletions src/main/environment/common_docker.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ spring.datasource.username=${DATABASE_USERNAME}
spring.datasource.password=${DATABASE_PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

## S3 storage for diagnostic documents pushed from a van
aws.s3.access-key=${AWS_S3_ACCESS_KEY}
aws.s3.secret-key=${AWS_S3_SECRET_KEY}
aws.s3.region=${AWS_S3_REGION}
diagnostic.documents.s3.bucket=${DIAGNOSTIC_DOCUMENTS_S3_BUCKET}

## Carestream URLs, local
carestreamOrderCreateURL=${COMMON_API}carestream/createOrder

Expand All @@ -28,6 +34,11 @@ dataSyncUploadUrl=${MMU_CENTRAL_SERVER}dataSync/van-to-server
## Data download API, central
dataSyncDownloadUrl=${MMU_CENTRAL_SERVER}dataSync/server-to-van

## Diagnostic document push (this server -> further central server)
diagnosticDocumentUploadUrl=${MMU_CENTRAL_SERVER}dataSync/diagnostic-documents
diagnosticDocument.push.batchSize=${DIAGNOSTIC_DOCUMENT_PUSH_BATCH_SIZE}
diagnostic.documents.storage-root=${DIAGNOSTIC_DOCUMENTS_STORAGE_ROOT}

## TC specialist slot booking, # not required
tcSpecialistSlotBook=${TM_API}schedule/bookSlot

Expand Down
10 changes: 10 additions & 0 deletions src/main/environment/common_example.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ spring.datasource.username=<Enter your AMRIT DB username>
spring.datasource.password=<Enter your AMRIT DB password>
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

## S3 storage for diagnostic documents pushed from a van (POST /dataSync/diagnostic-documents)
aws.s3.access-key=<Enter your AWS access key here>
aws.s3.secret-key=<Enter your AWS secret key here>
aws.s3.region=ap-south-1
diagnostic.documents.s3.bucket=<Enter your private bucket name here>

## Carestream URLs, local
#carestreamOrderCreateURL =http://localhost:1040/carestream/createOrder
carestreamOrderCreateURL =http://localhost:8083/carestream/createOrder
Expand Down Expand Up @@ -44,6 +50,10 @@ dataSyncUploadUrl=http://10.208.122.38:8080/mmu-api/dataSync/van-to-server
#dataSyncDownloadUrl=http://localhost:82/dataSync/server-to-van
dataSyncDownloadUrl=http://10.208.122.38:8080/mmu-api/dataSync/server-to-van

diagnosticDocumentUploadUrl=http://localhost:8087/mmu-api/dataSync/diagnostic-documents
diagnosticDocument.push.batchSize=3
diagnostic.documents.storage-root=<Enter your diagnostic documents storage root path here>

## TC specialist slot booking, # not required
#tcSpecialistSlotBook=http://localhost:8080/schedule/bookSlot
tcSpecialistSlotBook=http://10.208.122.38:8080/tm-api/schedule/bookSlot
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/com/iemr/mmu/config/S3ClientConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.iemr.mmu.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;

/***
* @purpose S3 client beans used to store diagnostic documents pushed from a van. Built
* once and shared, rather than re-built per request.
*/
@Configuration
public class S3ClientConfig {

@Value("${aws.s3.access-key}")
private String accessKey;

@Value("${aws.s3.secret-key}")
private String secretKey;

@Value("${aws.s3.region}")
private String region;

private StaticCredentialsProvider credentialsProvider() {
return StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey));
}

@Bean
public S3Client diagnosticDocumentS3Client() {
return S3Client.builder().region(Region.of(region)).credentialsProvider(credentialsProvider()).build();
}

@Bean
public S3Presigner diagnosticDocumentS3Presigner() {
return S3Presigner.builder().region(Region.of(region)).credentialsProvider(credentialsProvider()).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
*/
package com.iemr.mmu.controller.dataSyncActivity;

import java.util.Map;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -31,13 +33,16 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.google.gson.Gson;
import com.iemr.mmu.service.dataSyncActivity.DownSyncDataFromServerImpl;
import com.iemr.mmu.service.dataSyncActivity.DownloadDataFromServerImpl;
import com.iemr.mmu.service.dataSyncActivity.DownloadDataFromServerTransactionalImpl;
import com.iemr.mmu.service.dataSyncActivity.UploadDataToServerImpl;
import com.iemr.mmu.service.dataSyncLayerCentral.DiagnosticDocumentFetchService;
import com.iemr.mmu.service.dataSyncLayerCentral.DiagnosticDocumentPushServiceImpl;
import com.iemr.mmu.utils.CookieUtil;
import com.iemr.mmu.utils.response.OutputResponse;

Expand All @@ -60,7 +65,12 @@
@Autowired
private DownloadDataFromServerTransactionalImpl downloadDataFromServerTransactionalImpl;
@Autowired
private DiagnosticDocumentPushServiceImpl diagnosticDocumentPushServiceImpl;
@Autowired

Check warning on line 69 in src/main/java/com/iemr/mmu/controller/dataSyncActivity/StartSyncActivity.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this field injection and use constructor injection instead.

See more on https://sonarcloud.io/project/issues?id=PSMRI_MMU-API&issues=AaCO_4nMeuFP2uxtywSs&open=AaCO_4nMeuFP2uxtywSs&pullRequest=184
private DiagnosticDocumentFetchService diagnosticDocumentFetchService;
@Autowired

Check warning on line 71 in src/main/java/com/iemr/mmu/controller/dataSyncActivity/StartSyncActivity.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this field injection and use constructor injection instead.

See more on https://sonarcloud.io/project/issues?id=PSMRI_MMU-API&issues=AaCO_4nMeuFP2uxtywSt&open=AaCO_4nMeuFP2uxtywSt&pullRequest=184
private DownSyncDataFromServerImpl downSyncDataFromServerImpl;

private static final String GROUP_ID = "groupID";
private static final String PROVIDER_SERVICE_MAP_ID = "providerServiceMapID";

Expand Down Expand Up @@ -91,6 +101,45 @@
return response.toStringWithSerialization();
}

@Operation(summary = "Push pending diagnostic documents (docsProcessed='N') to the further central server, in batches")
@PostMapping(value = { "/diagnostic-documents-to-server" })
public String diagnosticDocumentsToServer(@RequestHeader(value = "Authorization") String authorization,
@RequestHeader(value = "ServerAuthorization") String serverAuthorization,
@RequestParam(required = false) Long villageId) {
OutputResponse response = new OutputResponse();
try {
String s = diagnosticDocumentPushServiceImpl.pushPendingDocuments(serverAuthorization, villageId);
if (s != null)
response.setResponse(s);
else
response.setError(5000, "Error in diagnostic document push");
} catch (Exception e) {
logger.error("Error in diagnostic document push : " + e);

Check warning on line 117 in src/main/java/com/iemr/mmu/controller/dataSyncActivity/StartSyncActivity.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Format specifiers should be used instead of string concatenation.

See more on https://sonarcloud.io/project/issues?id=PSMRI_MMU-API&issues=AaCO_4nMeuFP2uxtywSw&open=AaCO_4nMeuFP2uxtywSw&pullRequest=184
response.setError(e);
}
return response.toString();
}

@Operation(summary = "Fetch a short-lived download URL for the latest successfully-pushed diagnostic document matching a beneficiary+documentType, from this van's own local record")
@GetMapping(value = { "/diagnostic-documents/download" })
public String diagnosticDocumentDownloadUrl(@RequestParam Long beneficiaryId, @RequestParam String documentType,
@RequestHeader(value = "Authorization") String authorization) {
OutputResponse response = new OutputResponse();
try {
Map<String, Object> download = diagnosticDocumentFetchService.getLatestDocumentDownload(beneficiaryId,
documentType);
if (download != null)
response.setResponse(new Gson().toJson(download));
else
response.setError(5000, "No pushed document found for beneficiaryId=" + beneficiaryId
+ ", documentType=" + documentType);
} catch (Exception e) {
logger.error("Error fetching diagnostic document download URL : " + e);

Check warning on line 137 in src/main/java/com/iemr/mmu/controller/dataSyncActivity/StartSyncActivity.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Format specifiers should be used instead of string concatenation.

See more on https://sonarcloud.io/project/issues?id=PSMRI_MMU-API&issues=AaCO_4nMeuFP2uxtywSx&open=AaCO_4nMeuFP2uxtywSx&pullRequest=184
response.setError(e);
}
return response.toString();
}

@Operation(summary = "Get data sync group details")
@GetMapping(value = { "/getSyncGroupDetails" })
public String getSyncGroupDetails() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -35,11 +34,11 @@
import com.iemr.mmu.data.syncActivity_syncLayer.DownSyncDataDigester;
import com.iemr.mmu.data.syncActivity_syncLayer.SyncDownloadMaster;
import com.iemr.mmu.data.syncActivity_syncLayer.SyncUploadDataDigester;
import com.iemr.mmu.service.dataSyncLayerCentral.DiagnosticDocumentIngestService;
import com.iemr.mmu.service.dataSyncLayerCentral.FetchDownloadDataImpl;
import com.iemr.mmu.service.dataSyncLayerCentral.GetDataFromVanAndSyncToDBImpl;
import com.iemr.mmu.service.dataSyncLayerCentral.GetDownSyncDataFromCentralImpl;
import com.iemr.mmu.service.dataSyncLayerCentral.GetMasterDataFromCentralForVanImpl;
import com.iemr.mmu.utils.CookieUtil;
import com.iemr.mmu.utils.response.OutputResponse;

import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -61,6 +60,8 @@
private FetchDownloadDataImpl fetchDownloadDataImpl;
@Autowired
private GetDownSyncDataFromCentralImpl getDownSyncDataFromCentralImpl;
@Autowired

Check warning on line 63 in src/main/java/com/iemr/mmu/controller/dataSyncLayerCentral/MMUDataSyncVanToServer.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this field injection and use constructor injection instead.

See more on https://sonarcloud.io/project/issues?id=PSMRI_MMU-API&issues=AaCO_4nyeuFP2uxtywS1&open=AaCO_4nyeuFP2uxtywS1&pullRequest=184
private DiagnosticDocumentIngestService diagnosticDocumentIngestService;

@Operation(summary = "Sync data from van-to-server")
@PostMapping(value = { "/van-to-server" }, consumes = "application/json", produces = "application/json")
Expand All @@ -81,6 +82,24 @@
return response.toString();
}

@Operation(summary = "Receive diagnostic documents pushed from a van and store each in S3 (no database write here)")
@PostMapping(value = { "/diagnostic-documents" }, consumes = "application/json", produces = "application/json")
public String diagnosticDocumentsFromVan(@RequestBody String requestOBJ,
@RequestHeader(value = "Authorization") String Authorization) {
OutputResponse response = new OutputResponse();
try {
String s = diagnosticDocumentIngestService.ingestDocuments(requestOBJ);
if (s != null)
response.setResponse(s);
else
response.setError(5000, "diagnostic document ingest failed");
} catch (Exception e) {
response.setError(e);
logger.error("Diagnostic document ingest Exception" + e);

Check warning on line 98 in src/main/java/com/iemr/mmu/controller/dataSyncLayerCentral/MMUDataSyncVanToServer.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Format specifiers should be used instead of string concatenation.

See more on https://sonarcloud.io/project/issues?id=PSMRI_MMU-API&issues=AaCO_4nyeuFP2uxtywS2&open=AaCO_4nyeuFP2uxtywS2&pullRequest=184
}
return response.toString();
}

@Operation(summary = "Download data from server-to-van")
@PostMapping(value = { "/server-to-van" }, consumes = "application/json", produces = "application/json")
public String dataDownloadFromServer(@RequestBody SyncDownloadMaster syncDownloadMaster,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.iemr.mmu.service.dataSyncLayerCentral;

import java.time.Duration;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;

/***
* @purpose Hands back a short-lived presigned URL for the latest successfully-pushed
* diagnostic document matching a beneficiary+documentType, generated on demand from
* the object's S3 key (s3_path) - the bucket is private, so no permanent URL is
* ever persisted or handed out.
*/
@Service
public class DiagnosticDocumentFetchService {

private static final Duration URL_VALIDITY = Duration.ofMinutes(15);

@Value("${diagnostic.documents.s3.bucket}")
private String bucket;

@Autowired

Check warning on line 28 in src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DiagnosticDocumentFetchService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this field injection and use constructor injection instead.

See more on https://sonarcloud.io/project/issues?id=PSMRI_MMU-API&issues=AaCO_4f6euFP2uxtywQZ&open=AaCO_4f6euFP2uxtywQZ&pullRequest=184
private DiagnosticDocumentRepository diagnosticDocumentRepository;

@Autowired

Check warning on line 31 in src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DiagnosticDocumentFetchService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this field injection and use constructor injection instead.

See more on https://sonarcloud.io/project/issues?id=PSMRI_MMU-API&issues=AaCO_4f6euFP2uxtywQa&open=AaCO_4f6euFP2uxtywQa&pullRequest=184
private S3Presigner s3Presigner;

/***
* @return null if no successfully-pushed document matches, otherwise the download details
* (documentType, orderType, externalOrderId, contentType, lastModDate, downloadUrl,
* urlExpiresInSeconds)
*/
public Map<String, Object> getLatestDocumentDownload(Long beneficiaryId, String documentType) {
Map<String, Object> row = diagnosticDocumentRepository.findLatestDocument(beneficiaryId, documentType);
if (row == null) {
return null;

Check warning on line 42 in src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DiagnosticDocumentFetchService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Return an empty map instead of null.

See more on https://sonarcloud.io/project/issues?id=PSMRI_MMU-API&issues=AaCO_4f6euFP2uxtywQb&open=AaCO_4f6euFP2uxtywQb&pullRequest=184
}

String s3Key = (String) row.get("s3_path");
GetObjectRequest getObjectRequest = GetObjectRequest.builder().bucket(bucket).key(s3Key).build();
GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder().signatureDuration(URL_VALIDITY)
.getObjectRequest(getObjectRequest).build();
String downloadUrl = s3Presigner.presignGetObject(presignRequest).url().toString();

Map<String, Object> result = new java.util.HashMap<>();
result.put("externalOrderId", row.get("external_order_id"));
result.put("orderType", row.get("order_type"));
result.put("documentType", row.get("document_type"));
result.put("contentType", row.get("content_type"));
result.put("originalFileName", row.get("original_file_name"));
result.put("lastModDate", String.valueOf(row.get("last_mod_date")));
result.put("downloadUrl", downloadUrl);
result.put("urlExpiresInSeconds", URL_VALIDITY.getSeconds());
return result;
}
}
Loading
Loading