From 69979ee4a39f3d868a0678b15b34310334661a4b Mon Sep 17 00:00:00 2001 From: piyushkag Date: Mon, 9 Dec 2024 16:33:15 +0530 Subject: [PATCH 1/4] Added document attachment id in application evaluation response. --- .../dao/ApplicationAmendmentRequestDao.java | 1 + .../tendermanagement/dao/ApplicationEvaluationDao.java | 6 ++++++ .../model/response/DocumentResponseBean.java | 2 ++ 3 files changed, 9 insertions(+) diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java index c9c19708..41d43b1e 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java @@ -397,6 +397,7 @@ public class ApplicationAmendmentRequestDao { responseBean.setFilePath(documentEntity.getFilePath()); responseBean.setCreatedDate(documentEntity.getCreatedDate()); responseBean.setUpdatedDate(documentEntity.getUpdatedDate()); + responseBean.setDocumentAttachmentId(documentEntity.getDocumentAttachmentId()); return responseBean; }) .toList(); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index a14e7f62..924999f2 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -286,6 +286,7 @@ public class ApplicationEvaluationDao { responseBean.setFilePath(documentEntity.getFilePath()); responseBean.setCreatedDate(documentEntity.getCreatedDate()); responseBean.setUpdatedDate(documentEntity.getUpdatedDate()); + responseBean.setDocumentAttachmentId(documentEntity.getDocumentAttachmentId()); documentResponseBeans.add(responseBean); }); } @@ -399,6 +400,7 @@ public class ApplicationEvaluationDao { responseBean.setFilePath(documentEntity.getFilePath()); responseBean.setCreatedDate(documentEntity.getCreatedDate()); responseBean.setUpdatedDate(documentEntity.getUpdatedDate()); + responseBean.setDocumentAttachmentId(documentEntity.getDocumentAttachmentId()); documentResponseBeans.add(responseBean); allDocumentsDeleted[0] = false; } @@ -902,6 +904,7 @@ public class ApplicationEvaluationDao { responseBean.setFilePath(documentEntity.getFilePath()); responseBean.setCreatedDate(documentEntity.getCreatedDate()); responseBean.setUpdatedDate(documentEntity.getUpdatedDate()); + responseBean.setDocumentAttachmentId(documentEntity.getDocumentAttachmentId()); return responseBean; } @@ -974,6 +977,7 @@ public class ApplicationEvaluationDao { responseBean.setFilePath(documentEntity.getFilePath()); responseBean.setCreatedDate(documentEntity.getCreatedDate()); responseBean.setUpdatedDate(documentEntity.getUpdatedDate()); + responseBean.setDocumentAttachmentId(documentEntity.getDocumentAttachmentId()); documentResponseBeans.add(responseBean); }); } @@ -1224,6 +1228,7 @@ public class ApplicationEvaluationDao { responseBean.setFilePath(documentEntity.getFilePath()); responseBean.setCreatedDate(documentEntity.getCreatedDate()); responseBean.setUpdatedDate(documentEntity.getUpdatedDate()); + responseBean.setDocumentAttachmentId(documentEntity.getDocumentAttachmentId()); return responseBean; } @@ -1350,6 +1355,7 @@ public class ApplicationEvaluationDao { responseBean.setFilePath(documentEntity.getFilePath()); responseBean.setCreatedDate(documentEntity.getCreatedDate()); responseBean.setUpdatedDate(documentEntity.getUpdatedDate()); + responseBean.setDocumentAttachmentId(documentEntity.getDocumentAttachmentId()); documentResponseBeans.add(responseBean); }); } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/DocumentResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/DocumentResponseBean.java index f5706b10..307c4982 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/DocumentResponseBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/DocumentResponseBean.java @@ -25,4 +25,6 @@ public class DocumentResponseBean { private LocalDateTime createdDate; private LocalDateTime updatedDate; + + private String documentAttachmentId; } From 05f64af4047a0306401bcf2419fb115a5bbcb405 Mon Sep 17 00:00:00 2001 From: piyushkag Date: Mon, 9 Dec 2024 19:27:54 +0530 Subject: [PATCH 2/4] Updated code for Ndg generation with async processing. --- .../tendermanagement/dao/AppointmentDao.java | 328 +++++++++--------- .../tendermanagement/util/LoggingUtil.java | 1 - .../gepafin/tendermanagement/util/Utils.java | 38 ++ src/main/resources/message_en.properties | 2 +- src/main/resources/message_it.properties | 2 +- 5 files changed, 197 insertions(+), 174 deletions(-) diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java index ef594da5..86a6aceb 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java @@ -129,82 +129,155 @@ public class AppointmentDao { public NdgResponse checkNdgForAppointment(Long applicationId) { + ApplicationEntity application = applicationService.validateApplication(applicationId); + NdgResponse ndgResponse = new NdgResponse(); + if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_IN_PROGRESS)) { + throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); + } + + if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_GENERATED) && application.getNdg() != null) { + ndgResponse.setNdg(application.getNdg()); + return ndgResponse; + } + + // Update application status + application.setNdgStatus(GepafinConstant.NDG_IN_PROGRESS); + applicationRepository.save(application); + + // Start async processing + startAsyncNdgProcessing(applicationId); + + throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); + } + + private void startAsyncNdgProcessing(Long applicationId) { + // Check if a thread is already running for this application + if (executorMap.containsKey(applicationId)) { + log.warn("Async processing already running for applicationId: {}", applicationId); + return; + } + + // Create a dedicated thread for asynchronous processing + ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { + Thread thread = new Thread(runnable); + thread.setName("AsyncNdgProcessing-" + applicationId); + return thread; + }); + executorMap.put(applicationId, executor); + + executor.submit(() -> { + try { + log.info("Starting async processing for applicationId: {}", applicationId); + processNdgGeneration(applicationId); + } catch (Exception e) { + log.error("Error in async NDG processing for applicationId: {}", applicationId, e); + } finally { + // Cleanup resources + ExecutorService executorToShutdown = executorMap.remove(applicationId); + if (executorToShutdown != null) { + executorToShutdown.shutdown(); + } + log.info("Async processing completed for applicationId: {}", applicationId); + } + }); + } + + private void processNdgGeneration(Long applicationId) { + // Validate application, company, and hub + ApplicationEntity application = applicationService.validateApplication(applicationId); + CompanyEntity company = companyService.validateCompany(application.getCompanyId()); + HubEntity hub = hubRepository.findByHubId(application.getHubId()); + + if (!hub.getUniqueUuid().equals(defaultHubUuid)) { + log.info("Ndg cannot be created for another Hub, it is default for Gepafin."); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_NDG_FOR_ANOTHER_HUB)); + } + try { - NdgResponse ndgResponseToReturn = new NdgResponse(); - // Validate application, company, and hub - ApplicationEntity application = applicationService.validateApplication(applicationId); - if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_IN_PROGRESS)) { - throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); - } - //cloned for old application data - ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); - - CompanyEntity company = companyService.validateCompany(application.getCompanyId()); - HubEntity hub = hubRepository.findByHubId(application.getHubId()); - - if (!hub.getUniqueUuid().equals(defaultHubUuid)) { - log.info("Ndg cannot be created for another Hub, it is default for Gepafin."); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_NDG_FOR_ANOTHER_HUB)); - } - - // Check if NDG and idVisura are already present - if (isNdgAndIdVisuraPresent(application)) { - log.info("NDG already exist for applicationId: {}", applicationId); - ndgResponseToReturn.setNdg(application.getNdg()); - return ndgResponseToReturn; - } - // Authenticate and fetch token if required - if (hub.getAppointmentAuthTokenId() == null && hub.getAreaCode() == null) { - hub = authenticateAndSaveToken(hub); + if (hub.getAppointmentAuthTokenId() == null || hub.getAreaCode() == null) { + authenticateAndSaveToken(hub); } String authorizationToken = getBearerToken(hub); // Try retrieving NDG by VAT number AppointmentLoginResponse ndgResponse = retrieveNdgByVatNumber(company.getVatNumber(), authorizationToken, hub, application); - //For testing purpose Commenting it if (isNdgValid(ndgResponse.getNdg())) { saveNdgAndIdVisura(application, company, ndgResponse.getNdg(), null); - ndgResponseToReturn.setNdg(application.getNdg()); - return ndgResponseToReturn; + log.info("NDG successfully generated for applicationId: {}", applicationId); + } else { + // If NDG isn't immediately available, start polling + handleNdgPolling(application, company, hub, authorizationToken); } - - return getNdgResponse(company, authorizationToken, hub, application, ndgResponseToReturn, oldApplicationData); - } catch (FeignException e) { - log.error("Error in feign client call during NDG handling: {}", e.getMessage(), e); - Utils.callException(e.status(), e); - } catch (CustomValidationException e) { - log.info("Custom validation exception: {}", e.getMessage()); - throw e; } catch (Exception e) { - log.error("Error during NDG handling: {}", e.getMessage(), e); - throw new RuntimeException("Error during fetching NDG."); + log.error("Error during NDG generation for applicationId: {}", applicationId, e); } - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); } - private NdgResponse getNdgResponse(CompanyEntity company, String authorizationToken, HubEntity hub, ApplicationEntity application, NdgResponse ndgResponseToReturn, - ApplicationEntity oldApplicationData) { - // Create Visura if NDG is not found - AppointmentLoginResponse visuraResponse = createVisura(company, authorizationToken, hub); - if (isNdgValid(visuraResponse.getNdg())) { - saveNdgAndIdVisura(application, company, visuraResponse.getNdg(), visuraResponse.getIdVisura()); - ndgResponseToReturn.setNdg(application.getNdg()); - } else if (visuraResponse.getIdVisura() != null) { - application.setNdgStatus(GepafinConstant.NDG_IN_PROGRESS); - application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); - applicationRepository.save(application); + private void handleNdgPolling(ApplicationEntity application, CompanyEntity company, HubEntity hub, String authorizationToken) { - /** This code is responsible for adding a version history log for the "Updating ndg status in application" operation. **/ - loggingUtil.addVersionHistory( - VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); + try { + log.info("Starting NDG polling for applicationId: {}", application.getId()); + long startTime = System.currentTimeMillis(); - startNdgPollingTask(application, company, hub); - throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); + while (true) { + if (application.getNdg() != null) { + log.info("NDG retrieved for applicationId: {}", application.getId()); + break; + } + + try { + // Fetch Visura list and attempt to parse NDG + String visuraListJson = getVisuraList(application.getIdVisura(), authorizationToken, application, hub); + String ndg = parseNdgFromVisuraListResponse(visuraListJson); + + if (isNdgValid(ndg)) { + // CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company); + // ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); + + company.setNdg(ndg); + application.setNdg(ndg); + application.setNdgStatus(GepafinConstant.NDG_GENERATED); + application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); + applicationRepository.save(application); + companyRepository.save(company); + log.info("NDG saved successfully for applicationId: {}", application.getId()); + + // /** This code is responsible for adding a version history log for the "update application ndg code, status, and Id visura" + // operation. **/ + // loggingUtil.addVersionHistory( + // VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData) + // .newData(application).build()); + // + // /** This code is responsible for adding a version history log for the "update company ndg code" operation. **/ + // loggingUtil.addVersionHistory( + // VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData) + // .newData(company).build()); + break; + } + + // Check if polling has timed out + if (System.currentTimeMillis() - startTime > TimeUnit.HOURS.toMillis(2)) { + log.warn("NDG polling timed out for applicationId: {}", application.getId()); + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + break; + } + + // Wait before the next polling attempt + Thread.sleep(TimeUnit.MINUTES.toMillis(15)); + } catch (InterruptedException e) { + log.warn("NDG polling interrupted for applicationId: {}", application.getId()); + Thread.currentThread().interrupt(); + break; + } catch (Exception e) { + log.error("Error during NDG polling for applicationId: {}", application.getId(), e); + } + } + } finally { + log.info("NDG polling completed for applicationId: {}", application.getId()); } - - return ndgResponseToReturn; } private static String getBearerToken(HubEntity hub) { @@ -212,102 +285,6 @@ public class AppointmentDao { return "Bearer " + hub.getAppointmentAuthTokenId(); } - private void startNdgPollingTask(ApplicationEntity application, CompanyEntity company, HubEntity hub) { - //cloned for all data - ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); - CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company); - - // Check if a thread is already running for this application - if (executorMap.containsKey(application.getId())) { - log.warn("Polling task already running for applicationId: {}", application.getId()); - return; - } - - // Create a dedicated thread (single-threaded executor) for this application - ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { - Thread thread = new Thread(runnable); - thread.setName(GepafinConstant.POLLING_THREAD_NAME + application.getId()); - return thread; - }); - executorMap.put(application.getId(), executor); - - // Submit polling task to this thread - executor.submit(() -> { - try { - log.info("Polling task started for applicationId: {} on thread: {}", application.getId(), Thread.currentThread().getName()); - long startTime = System.currentTimeMillis(); - - while (true) { - if (application.getNdg() != null) - break; - try { - String visuraListJson = getVisuraList(application.getIdVisura(), hub.getAppointmentAuthTokenId(), application, hub); - String ndg = parseNdgFromVisuraListResponse(visuraListJson); - - if (isNdgValid(ndg)) { - company.setNdg(ndg); - application.setNdgStatus(GepafinConstant.NDG_GENERATED); - application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); - application.setNdg(ndg); - applicationRepository.save(application); - companyRepository.save(company); - - log.info("NDG obtained for applicationId: {} and saved successfully.", application.getId()); - break; // Exit the loop after successful NDG retrieval - } else { - log.warn("NDG not found for applicationId: {} in Visura List API response.", application.getId()); - } - - // Check if polling time has exceeded the limit - if (System.currentTimeMillis() - startTime > TimeUnit.HOURS.toMillis(2)) { - log.warn("Polling timed out for applicationId: {}", application.getId()); - // Mark NDG status as FAILED for this application - application.setNdgStatus(GepafinConstant.NDG_FAILED); - application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); - applicationRepository.save(application); - log.info("NDG status marked as FAILED for applicationId: {}", application.getId()); - break; - } - - // Wait before the next polling attempt - Thread.sleep(TimeUnit.MINUTES.toMillis(15)); - - } catch (InterruptedException e) { - log.warn("Polling task interrupted for applicationId: {}", application.getId()); - Thread.currentThread().interrupt(); - break; - } catch (Exception e) { - log.error("Error during NDG polling for applicationId: {}", application.getId(), e); - } - } - } finally { - // Cleanup: Shut down the thread for this application - executor.shutdown(); - executorMap.remove(application.getId()); - log.info("Polling task completed and thread shut down for applicationId: {}", application.getId()); - } - }); - /** This code is responsible for adding a version history log for the "Update application ndgCode and ndgStatus" operation. **/ - loggingUtil.addVersionHistory( - VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); - - /** This code is responsible for adding a version history log for the "Update company ndgCode" operation. **/ - loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData).newData(company).build()); - } - - private boolean isNdgAndIdVisuraPresent(ApplicationEntity application) { - - String ndg = application.getNdg(); - String idVisura = application.getIdVisura(); - - if (ndg != null && idVisura == null) { - return true; - } else if (ndg == null && idVisura != null) { - return false; - } else - return ndg != null; - } - private boolean isNdgValid(String ndg) { return ndg != null && !ndg.isEmpty(); @@ -316,8 +293,8 @@ public class AppointmentDao { private void saveNdgAndIdVisura(ApplicationEntity application, CompanyEntity company, String ndg, String idVisura) { //cloned for old application and company data - ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); - CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company); + // ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); + // CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company); application.setNdg(ndg); application.setIdVisura(idVisura); @@ -327,12 +304,13 @@ public class AppointmentDao { companyRepository.save(company); applicationRepository.save(application); - /** This code is responsible for adding a version history log for the "update application ndg code, status, and Id visura" operation. **/ - loggingUtil.addVersionHistory( - VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); - - /** This code is responsible for adding a version history log for the "update company ndg code" operation. **/ - loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData).newData(company).build()); + // /** This code is responsible for adding a version history log for the "update application ndg code, status, and Id visura" operation. **/ + // loggingUtil.addVersionHistory( + // VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); + // + // /** This code is responsible for adding a version history log for the "update company ndg code" operation. **/ + // loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData).newData + // (company).build()); log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg()); } @@ -361,7 +339,7 @@ public class AppointmentDao { private HubEntity authenticateAndSaveToken(HubEntity hub) { - HubEntity oldHubData = Utils.getClonedEntityForData(hub); + // HubEntity oldHubData = Utils.getClonedEntityForData(hub); try { //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); @@ -369,8 +347,9 @@ public class AppointmentDao { hub.setAuthToken(authJwtToken); hubRepository.save(hub); - /** This code is responsible for adding a version history log for the "Updating auth token for login api in hub" operation. **/ - loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData(hub).build()); + // /** This code is responsible for adding a version history log for the "Updating auth token for login api in hub" operation. **/ + // loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData + // (hub).build()); // Prepare the request body (adjust if necessary for login API) Map body = Collections.emptyMap(); @@ -389,10 +368,12 @@ public class AppointmentDao { hub.setAreaCode(parsedResponse.getAreaCode()); hubRepository.save(hub); - /** This code is responsible for adding a version history log for the "inserting token and areaCode from login odessa response for appointment flow api's" - * operation. **/ - loggingUtil.addVersionHistory( - VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData(hub).build()); + // /** This code is responsible for adding a version history log for the "inserting token and areaCode from login odessa response for + // appointment flow api's" + // * operation. **/ + // loggingUtil.addVersionHistory( + // VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData(hub) + // .build()); log.info("Saved new authToken and areaCode for Hub."); return hub; @@ -419,7 +400,7 @@ public class AppointmentDao { // Parse and return the NDG response return parseNdgResponse(responseJson); } catch (FeignException.Forbidden forbiddenException) { - log.error("403 Forbidden received while retrieving NDG. Regenerating token..."); + logForbiddenError(); // Regenerate the token and retry String newAuthorizationToken = regenerateTokenAndSave(hub); return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application); @@ -448,7 +429,7 @@ public class AppointmentDao { String responseJson = Utils.convertObjectToJson(response.getBody()); return parseVisuraResponse(responseJson); } catch (FeignException.Forbidden forbiddenException) { - log.error("403 Forbidden received while retrieving NDG. Regenerating token..."); + logForbiddenError(); // Regenerate the token and retry String newAuthorizationToken = regenerateTokenAndSave(hub); return createVisura(company, newAuthorizationToken, hub); @@ -458,6 +439,11 @@ public class AppointmentDao { } } + private static void logForbiddenError() { + + log.error("403 Forbidden received while retrieving NDG. Regenerating token..."); + } + private static AppointmentNdgRequest getAppointmentNdgRequest(String vatNumber) { AppointmentNdgRequest request = new AppointmentNdgRequest(); diff --git a/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java b/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java index 319795ec..e2ab4521 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java +++ b/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java @@ -9,7 +9,6 @@ import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.UserActionEntity; import net.gepafin.tendermanagement.entities.UserEntity; import net.gepafin.tendermanagement.entities.VersionHistoryEntity; -import net.gepafin.tendermanagement.enums.VersionActionTypeEnum; import net.gepafin.tendermanagement.model.request.UserActionRequest; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.repositories.UserActionsRepository; diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index b7383161..a560b29d 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -60,6 +60,9 @@ import static org.apache.commons.lang3.StringUtils.isEmpty; public class Utils { +// @Autowired +// private static TokenProvider tokenProvider; + public static final Logger log = LoggerFactory.getLogger(Utils.class); private static final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) @@ -612,4 +615,39 @@ public class Utils { throw new RuntimeException("Failed to generate JWT token", e); } } + +// public static void setHttpServletRequestForNdgProcess(HttpServletRequest originalRequest) { +// +// // Validate original request +// if (originalRequest == null) { +// throw new IllegalArgumentException("Original request cannot be null."); +// } +// +// // Create a mock request +// Claims tokenClaims = tokenProvider.getClaimsFromToken(tokenProvider.extractTokenFromRequest(originalRequest)); +// MockHttpServletRequest mockRequest = new MockHttpServletRequest(); +// mockRequest.setRequestURI(originalRequest.getRequestURI()); +// mockRequest.setMethod(originalRequest.getMethod()); +// +// // Copy essential headers and attributes from the original request +// Enumeration headerNames = originalRequest.getHeaderNames(); +// while (headerNames.hasMoreElements()) { +// String headerName = headerNames.nextElement(); +// String headerValue = originalRequest.getHeader(headerName); +// if (headerValue != null) { +// mockRequest.addHeader(headerName, headerValue); +// } +// } +// +// // Set a specific attribute if required +// if (originalRequest.getAttribute(GepafinConstant.USER_ACTION_ID) != null) { +// mockRequest.setAttribute(GepafinConstant.USER_ACTION_ID, originalRequest.getAttribute(GepafinConstant.USER_ACTION_ID)); +// } +// +// ServletRequestAttributes attributes = new ServletRequestAttributes(mockRequest); +// RequestContextHolder.setRequestAttributes(attributes); +// // Log successful context setting +// log.info("Successfully set mock request for NDG process with URI: {}", mockRequest.getRequestURI()); +// } + } diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index ad5a3282..487c8d95 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -320,7 +320,7 @@ atleast.one.id.required=At least one of companyId or applicationId must be provi ndg.generated = NDG Generated. ndg.available = NDG Available. ndg.generation.in.progress = NDG generation is in progress. -ndg.fetch.successfully = NDG fetch successfully. +ndg.fetch.successfully = NDG fetched successfully. appointment.already.created = Appointment Already Created. ndg.not.found.for.this.application.or.invalid = Ndg not found for this application or invalid. provide.valid.application.document.id = Provide valid application document id. diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index c7d29ceb..ac8b75d9 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -310,7 +310,7 @@ atleast.one.id.required=Almeno uno tra companyId o applicationId deve essere for #Appointment flow messages ndg.available = NDG disponibile. ndg.generation.in.progress = La generazione NDG ? in corso. -ndg.fetch.successfully = Recupero NDG riuscito. +ndg.fetch.successfully = NDG recuperato con successo. appointment.already.created = Appuntamento gi? creato. ndg.not.found.for.this.application.or.invalid = NDG non trovato per questa applicazione o non valido. provide.valid.application.document.id = Fornisci un ID documento applicativo valido. From 2a5f344ea0fdeab800e099c43b12d145dfc62632 Mon Sep 17 00:00:00 2001 From: piyushkag Date: Tue, 10 Dec 2024 12:47:11 +0530 Subject: [PATCH 3/4] Added async loading for document uploading to external system. --- .../constants/GepafinConstant.java | 2 + .../tendermanagement/dao/AppointmentDao.java | 117 +++++++++++------- .../service/AppointmentService.java | 2 +- .../service/impl/AppointmentServiceImpl.java | 5 +- .../gepafin/tendermanagement/util/Utils.java | 21 +++- .../web/rest/api/AppointmentApi.java | 3 +- .../rest/api/impl/AppointmentController.java | 4 +- src/main/resources/message_en.properties | 1 + src/main/resources/message_it.properties | 1 + 9 files changed, 100 insertions(+), 56 deletions(-) diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index b553df9f..14d82b91 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -339,5 +339,7 @@ public class GepafinConstant { public static final String HMAC_ALGO = "HmacSHA256"; public static final String ERROR_IN_GENERATING_NDG_TRY_AGAIN = "error.try.again"; public static final String POLLING_THREAD_NAME = "Ndg-Polling-Thread-"; + public static final String DOCUMENT_UPLOADING_IN_PROGRESS = "document.uploading.is.in.progress"; + public static final String ASYNC_DOCUMENT_UPLOAD_NAME = "AsyncDocumentUpload-"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java index 86a6aceb..1b644d6e 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java @@ -5,9 +5,11 @@ import com.amazonaws.services.s3.model.GetObjectRequest; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import feign.FeignException; +import io.jsonwebtoken.Claims; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import net.gepafin.tendermanagement.config.Translator; +import net.gepafin.tendermanagement.config.jwt.TokenProvider; import net.gepafin.tendermanagement.constants.AppointmentApiConstant; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.ApplicationEntity; @@ -125,8 +127,15 @@ public class AppointmentDao { @Autowired private LoggingUtil loggingUtil; + @Autowired + private TokenProvider tokenProvider; + private final Map executorMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap threadForDocumentMap = new ConcurrentHashMap<>(); + + private static final ThreadLocal threadLocalHubId = new ThreadLocal<>(); + public NdgResponse checkNdgForAppointment(Long applicationId) { ApplicationEntity application = applicationService.validateApplication(applicationId); @@ -719,87 +728,101 @@ public class AppointmentDao { return appointmentCreationRequest; } - public DocumentUploadResponse uploadDocumentToExternalSystem(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, Long applicationId) { - - DocumentUploadResponse response = new DocumentUploadResponse(); + public DocumentUploadResponse uploadDocumentToExternalSystem(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { + // Check if the document is already being processed DocumentEntity systemDoc = documentDao.validateDocument(documentId); - ApplicationEntity application = getApplicationEntityForDocument(applicationId, systemDoc); - - //cloned for old document data - DocumentEntity oldDocumentEntity = Utils.getClonedEntityForData(systemDoc); - - if (!docToExternalSystemRequest.getInput().getAttributes().getNdg().equalsIgnoreCase(application.getNdg())) { - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_MATCHED_OR_NOT_FOUND)); + Claims claims = tokenProvider.getClaimsFromToken(tokenProvider.extractTokenFromRequest(request)); + Long hubId = Utils.extractHubIdFromPayload(claims.getSubject()); + if (systemDoc.getDocumentAttachmentId() != null) { + // If the documentAttachmentId is already set, return the response + log.info("Document already uploaded with documentAttachmentId: {}", systemDoc.getDocumentAttachmentId()); + DocumentUploadResponse response = new DocumentUploadResponse(); + response.setDocumentAttachmentId(systemDoc.getDocumentAttachmentId()); + return response; } + // Check if a thread is already running for this document upload + if (threadForDocumentMap.containsKey(documentId)) { + log.warn("Document upload already running for documentId: {}", documentId); + throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADING_IN_PROGRESS)); + } + // Start the upload process in the background + ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { + Thread thread = new Thread(runnable); + thread.setName(GepafinConstant.ASYNC_DOCUMENT_UPLOAD_NAME + documentId); + return thread; + }); + threadForDocumentMap.put(documentId, executor); - Long hubId = application.getHubId(); + executor.submit(() -> { + threadLocalHubId.set(hubId); + try { + log.info("Starting async document upload for documentId: {}", documentId); + uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest); + } catch (Exception e) { + log.error("Error in async document upload for documentId: {}", documentId, e); + } finally { + // Cleanup resources + ExecutorService executorToShutdown = threadForDocumentMap.remove(documentId); + if (executorToShutdown != null) { + executorToShutdown.shutdown(); + threadLocalHubId.remove(); + } + log.info("Async document upload completed for documentId: {}", documentId); + } + }); + // Return an immediate response indicating the process is in progress + throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADING_IN_PROGRESS)); + } + + private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { + // Synchronous upload logic + DocumentEntity systemDoc = documentDao.validateDocument(documentId); + + Long hubId = threadLocalHubId.get(); HubEntity hub = hubRepository.findByHubId(hubId); if (!hub.getUniqueUuid().equals(defaultHubUuid)) { log.info("Document cannot be uploaded for another Hub, it is default for Gepafin."); throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_DOCUMENT_UPLOAD_FOR_ANOTHER_HUB)); } - log.info("Got Document in system {}", systemDoc); + log.info("Got Document in system: {}", systemDoc); String oldUrl = systemDoc.getFilePath(); - log.info("Processing {}", oldUrl); - String authorizationToken = getBearerToken(hub); + try { File localFile = downloadFileFromS3(oldUrl); MultipartFile multipartFile = convertFileToMultipartFile(localFile); UploadDocToExternalSystemRequest externalSystemRequest = new UploadDocToExternalSystemRequest(); - - UploadDocToExternalSystemRequest.Input input = getUploadDocumentInput(docToExternalSystemRequest); - externalSystemRequest.setInput(input); + externalSystemRequest.setInput(getUploadDocumentInput(docToExternalSystemRequest)); String uploadDocRequest = Utils.convertObjectToJson(externalSystemRequest); ResponseEntity uploadedDocumentData = appointmentApiService.uploadDocumentToExternalSystemForAppointment(authorizationToken, context, uploadDocRequest, multipartFile); - String responseData = Utils.convertObjectToJson(uploadedDocumentData.getBody()); - DocumentUploadResponse parsedDocumentUploadResponse = parseDocumentUploadResponse(responseData); - if (parsedDocumentUploadResponse == null) { + String responseData = Utils.convertObjectToJson(uploadedDocumentData.getBody()); + DocumentUploadResponse parsedResponse = parseDocumentUploadResponse(responseData); + + if (parsedResponse == null) { throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_UPLOADING_DOCUMENT)); } - systemDoc.setDocumentAttachmentId(parsedDocumentUploadResponse.getDocumentAttachmentId()); + // Save the documentAttachmentId to the database + systemDoc.setDocumentAttachmentId(parsedResponse.getDocumentAttachmentId()); documentRepository.save(systemDoc); - /** This code is responsible for adding a version history log for the "Update document with document attachment id" operation. **/ - loggingUtil.addVersionHistory( - VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldDocumentEntity).newData(systemDoc).build()); - - log.info("Document uploaded successfully to external system : {}", parsedDocumentUploadResponse); - response.setDocumentAttachmentId(systemDoc.getDocumentAttachmentId()); - return response; + log.info("Document uploaded successfully to external system: {}", parsedResponse); } catch (FeignException.Forbidden forbiddenException) { - log.error("403 Forbidden received while uploading document to external system. Regenerating token..."); - + log.error("403 Forbidden received while uploading document. Regenerating token..."); regenerateTokenAndSave(hub); - return uploadDocumentToExternalSystem(systemDoc.getSourceId(), docToExternalSystemRequest, applicationId); + uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest); } catch (Exception e) { - log.error("Exception in uploading document to external system {}", e.getMessage()); + log.error("Exception during document upload: {}", e.getMessage(), e); throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.EXTERNAL_DOCUMENT_UPLOAD_FAILURE_MSG)); } } - private ApplicationEntity getApplicationEntityForDocument(Long applicationId, DocumentEntity systemDoc) { - - if (systemDoc.getDocumentAttachmentId() != null) { - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.DOCUMENT_ALREADY_UPLOADED)); - } - - ApplicationEntity application; - - if (systemDoc.getSource().equalsIgnoreCase(DocumentSourceTypeEnum.APPLICATION.getValue()) && Objects.equals(systemDoc.getSourceId(), applicationId)) { - application = applicationService.validateApplication(systemDoc.getSourceId()); - } else { - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.PROVIDE_VALID_APPLICATION_DOC_ID)); - } - return application; - } private UploadDocToExternalSystemRequest.Input getUploadDocumentInput(UploadDocToExternalSystemRequest docToExternalSystemRequest) { diff --git a/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java b/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java index 512014f3..842901a4 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java @@ -12,5 +12,5 @@ public interface AppointmentService { AppointmentCreationResponse createAppointmentForApplication(HttpServletRequest request, Long applicationId, CreateAppointmentRequest createAppointmentRequest); - DocumentUploadResponse uploadDocToExternalSystem(HttpServletRequest request, Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, Long applicationId); + DocumentUploadResponse uploadDocToExternalSystem(HttpServletRequest request, Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest); } diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java index f9c1ba85..2e7960df 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java @@ -30,9 +30,8 @@ public class AppointmentServiceImpl implements AppointmentService { } @Override - public DocumentUploadResponse uploadDocToExternalSystem(HttpServletRequest request, Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, - Long applicationId) { + public DocumentUploadResponse uploadDocToExternalSystem(HttpServletRequest request, Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { - return appointmentDao.uploadDocumentToExternalSystem(documentId, docToExternalSystemRequest, applicationId); + return appointmentDao.uploadDocumentToExternalSystem(documentId, docToExternalSystemRequest); } } diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index a560b29d..e93c8ed9 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -19,12 +19,15 @@ import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; +import io.jsonwebtoken.Claims; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; import org.apache.commons.collections4.MapUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -650,4 +653,20 @@ public class Utils { // log.info("Successfully set mock request for NDG process with URI: {}", mockRequest.getRequestURI()); // } -} + public static Long extractHubIdFromPayload(String payload) { + + Long hubId; + try { + String[] parts = payload.split(":"); + if (parts.length > 2) { + hubId = Long.valueOf(parts[2]); + return hubId; + } else { + hubId = null; + } + } catch (Exception e) { + throw new RuntimeException("No Hub id present in payload", e); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java index 04616f92..5507492a 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java @@ -52,9 +52,8 @@ public interface AppointmentApi { @ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })), @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { @ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) })) }) - @PostMapping(value = "/application/{applicationId}/document/{documentId}", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "/document/{documentId}", produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity> uploadDocumentToExternalSystem(HttpServletRequest request, @Parameter(description = "The document id", required = true) @PathVariable(value = "documentId", required = true) Long documentId, - @Parameter(description = "The application id", required = true) @PathVariable(value = "applicationId", required = true) Long applicationId, @RequestBody UploadDocToExternalSystemRequest docToExternalSystemRequest); } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java index a8986626..ca55e55f 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java @@ -61,14 +61,14 @@ public class AppointmentController implements AppointmentApi { } @Override - public ResponseEntity> uploadDocumentToExternalSystem(HttpServletRequest request, Long documentId, Long applicationId, + public ResponseEntity> uploadDocumentToExternalSystem(HttpServletRequest request, Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { /** This code is responsible for creating user action logs for the "Upload document to external system" operation. **/ loggingUtil.logUserAction( UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.UPLOAD).actionContext(UserActionContextEnum.UPLOAD_DOCUMENT_TO_EXTERNAL_SYSTEM).build()); - DocumentUploadResponse documentUploadResponse = appointmentService.uploadDocToExternalSystem(request, documentId, docToExternalSystemRequest, applicationId); + DocumentUploadResponse documentUploadResponse = appointmentService.uploadDocToExternalSystem(request, documentId, docToExternalSystemRequest); return ResponseEntity.status(HttpStatus.OK) .body(new Response<>(documentUploadResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADED_SUCCESSFULLY_TO_EXTERNAL_SYSTEM))); diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index 487c8d95..24150081 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -334,3 +334,4 @@ appointment.creation.is.only.for.gepafin = Appointment creation is only allowed upload.document.is.only.for.gepafin = Document cant be uploaded, this is only available for GEPAFIN Hub. appointment.created.successfully = Appointment created successfully. error.try.again = Service call error while performing the operation. Please try again. +document.uploading.is.in.progress = Document uploading is in progress. diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index ac8b75d9..084fe6aa 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -324,3 +324,4 @@ appointment.creation.is.only.for.gepafin = La creazione degli appuntamenti ? con upload.document.is.only.for.gepafin = Il documento non pu? essere caricato, questa operazione ? disponibile solo per il Hub GEPAFIN. appointment.created.successfully = Appuntamento creato con successo. error.try.again = Errore di chiamata di servizio durante l'esecuzione dell'operazione. Riprovare. +document.uploading.is.in.progress = Il documento è in fase di caricamento. From 3937fb76f1b044e89a80c724c0ba1be406fd5f6f Mon Sep 17 00:00:00 2001 From: nisha Date: Thu, 12 Dec 2024 11:42:51 +0530 Subject: [PATCH 4/4] Code for amendment module --- .../constants/GepafinConstant.java | 1 + .../dao/ApplicationAmendmentRequestDao.java | 73 +++++++++++++++++-- .../tendermanagement/dao/ApplicationDao.java | 2 +- .../dao/ApplicationEvaluationDao.java | 2 +- .../tendermanagement/dao/ProtocolDao.java | 8 +- .../ApplicationAmendmentRequestEntity.java | 3 + .../entities/ProtocolEntity.java | 3 + .../enums/ProtocolTypeEnum.java | 24 ++++++ .../model/request/AmendmentFieldRequest.java | 13 ++++ .../request/ApplicationAmendmentRequest.java | 1 + .../ApplicationAmendmentRequestBean.java | 1 + .../response/AmendmentDocumentResponse.java | 12 +++ .../ApplicationAmendmentRequestResponse.java | 1 + .../db/changelog/db.changelog-1.0.0.xml | 9 ++- src/main/resources/message_en.properties | 1 + src/main/resources/message_it.properties | 1 + 16 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 src/main/java/net/gepafin/tendermanagement/enums/ProtocolTypeEnum.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/request/AmendmentFieldRequest.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/response/AmendmentDocumentResponse.java diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 14d82b91..42a9cd41 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -341,5 +341,6 @@ public class GepafinConstant { public static final String POLLING_THREAD_NAME = "Ndg-Polling-Thread-"; public static final String DOCUMENT_UPLOADING_IN_PROGRESS = "document.uploading.is.in.progress"; public static final String ASYNC_DOCUMENT_UPLOAD_NAME = "AsyncDocumentUpload-"; + public static final String All_DOCUMENT_CHECKED_AND_ONE_CHECKLIST_CHECKED="all.document.checked.and.one.checklist.checked"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java index 41d43b1e..72f540f7 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java @@ -105,12 +105,33 @@ public class ApplicationAmendmentRequestDao { @Autowired private AssignedApplicationsDao assignedApplicationsDao; + @Autowired + private ApplicationEvaluationDao applicationEvaluationDao; + public ApplicationAmendmentRequestResponse getApplicationDataForAmendment(Long applicationEvaluationId) { log.info("Fetching the application data for the Amendment process {}", applicationEvaluationId); ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(applicationEvaluationId); Long applicationId = applicationEvaluationEntity.getApplicationId(); - ApplicationEntity application = applicationService.validateApplication(applicationId); + List evaluationFileRequests=new ArrayList<>(); + List checklistRequests=new ArrayList<>(); + ApplicationEntity application = applicationService.validateApplication(applicationId); + String file=applicationEvaluationEntity.getFile(); + String checkList=applicationEvaluationEntity.getChecklist(); + if(file != null){ + evaluationFileRequests=Utils.convertJsonStringToList(file,FieldRequest.class); + } + Boolean allValid = evaluationFileRequests.stream() + .anyMatch(fieldRequest -> fieldRequest.getValid() == null); + if(checkList != null) { + checklistRequests=Utils.convertJsonStringToList(checkList,ChecklistRequest.class); + } + boolean resultCheckList = checklistRequests.stream() + .anyMatch(checklistRequest -> Boolean.TRUE.equals(checklistRequest.getValid())) ? false : true; + + if(Boolean.TRUE.equals(allValid) || Boolean.TRUE.equals(resultCheckList)){ + throw new CustomValidationException(Status.BAD_REQUEST,Translator.toLocale(GepafinConstant.All_DOCUMENT_CHECKED_AND_ONE_CHECKLIST_CHECKED)); + } // Set common application-level details String callName = application.getCall().getName(); Long protocolNumber = (application.getProtocol() != null && application.getProtocol().getProtocolNumber() != null) @@ -135,11 +156,18 @@ public class ApplicationAmendmentRequestDao { List forms = applicationFormRepository.findByApplicationId(applicationId); List allFormFields = new ArrayList<>(); - + Map fieldRequestMap = evaluationFileRequests.stream() + .collect(Collectors.toMap(FieldRequest::getId, fieldRequest -> fieldRequest)); for (ApplicationFormEntity form : forms) { String content = form.getForm().getContent(); List> result = filterByName(content, "fileupload"); - allFormFields.addAll(getIdAndLabelFromResult(result)); + List amendmentFormFieldResponses= getIdAndLabelFromResult(result); + amendmentFormFieldResponses.removeIf(amendmentFormFieldResponse -> { + FieldRequest matchingRequest = fieldRequestMap.get(amendmentFormFieldResponse.getFieldId()); + // Remove if no matching FieldRequest exists or if valid is true + return matchingRequest == null || Boolean.TRUE.equals(matchingRequest.getValid()); + }); + allFormFields.addAll(amendmentFormFieldResponses); } response.setFormFields(allFormFields); @@ -243,6 +271,9 @@ public class ApplicationAmendmentRequestDao { String formFieldsJson = Utils.convertObjectToJson(formFieldRequestBean); applicationAmendmentRequestEntity.setFormFields(formFieldsJson); } + if(Boolean.FALSE.equals(applicationAmendmentRequest.getAmendmentDocument().isEmpty())) { + setAmendmentDocuments(applicationAmendmentRequest.getAmendmentDocument(), applicationAmendmentRequestEntity); + } List amendmentRequest = applicationAmendmentRequestRepository.findAllByApplicationEvaluationIdAndIsDeletedFalse(applicationEvaluationEntity.getId()); // Ensure startDate and initialDays are not null to avoid NullPointerException if (amendmentRequest !=null && amendmentRequest.isEmpty() && applicationEvaluationEntity.getStartDate() != null && applicationEvaluationEntity.getInitialDays() != null ) { @@ -261,7 +292,7 @@ public class ApplicationAmendmentRequestDao { Long protocolNumber = protocolDao.getProtocolNumber(userEntity.getHub()); ProtocolEntity protocolEntity = protocolDao.createProtocolEntity( applicationEvaluationEntity.getAssignedApplicationsEntity().getApplication(), protocolNumber, - userEntity.getHub().getId()); + userEntity.getHub().getId(),false); applicationAmendmentRequestEntity.setProtocol(protocolEntity); ApplicationAmendmentRequestEntity applicationAmendment = saveApplicationAmendmentRequestEntity(applicationAmendmentRequestEntity, null, VersionActionTypeEnum.INSERT); String evaluationStatusType = applicationEvaluationEntity.getStatus(); @@ -302,6 +333,16 @@ public class ApplicationAmendmentRequestDao { return applicationAmendment; } + private void setAmendmentDocuments(List amendmentFieldRequest, ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity) { + amendmentFieldRequest.stream().forEach(amendmentData->{ + String fieldValue=amendmentData.getFileValue(); + if(fieldValue!=null){ + documentService.validateDocument(Long.valueOf(fieldValue)); + } + }); + applicationAmendmentRequestEntity.setAmendmentDocument(Utils.convertListToJsonString(amendmentFieldRequest)); + } + public ApplicationAmendmentRequestEntity saveApplicationAmendmentRequestEntity(ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity,ApplicationAmendmentRequestEntity oldApplicationAmendmentEntity,VersionActionTypeEnum actionTypeEnum) { ApplicationAmendmentRequestEntity applicationAmendmentRequest = applicationAmendmentRequestRepository.save(applicationAmendmentRequestEntity); @@ -320,12 +361,31 @@ public class ApplicationAmendmentRequestDao { List amendmentFormFields = Utils.convertJsonStringToList( applicationAmendmentRequestEntity.getFormFields(), AmendmentFormField.class); Map formFieldEntityMap = getApplicationFormFieldEntityMap(applicationAmendmentRequestEntity, amendmentFormFields); - + List amendmentFieldRequests = Utils.convertJsonStringToList(applicationAmendmentRequestEntity.getAmendmentDocument(),AmendmentFieldRequest.class); + if (amendmentFieldRequests != null) { + List amendmentDocumentResponses = amendmentFieldRequests.stream() + .map(this::createAmendmentDocumentResponse) + .toList(); + response.setAmendmentDocuments(amendmentDocumentResponses); + } processFormFields(amendmentFormFields, fieldIdToLabelMap, formFieldEntityMap, response); return response; } + private AmendmentDocumentResponse createAmendmentDocumentResponse(AmendmentFieldRequest amendmentFieldRequest) { + AmendmentDocumentResponse amendmentDocumentResponse = new AmendmentDocumentResponse(); + amendmentDocumentResponse.setFieldId(amendmentFieldRequest.getFieldId()); + amendmentDocumentResponse.setNameValue(amendmentFieldRequest.getNameValue()); + amendmentDocumentResponse.setIsValid(amendmentFieldRequest.getIsValid()); + + DocumentEntity documentEntity = documentService.validateDocument(Long.valueOf(amendmentFieldRequest.getFileValue())); + DocumentResponseBean responseBean = applicationEvaluationDao.createDocumentResponseBean(documentEntity); + amendmentDocumentResponse.setFileValue(responseBean); + + return amendmentDocumentResponse; + } + private ApplicationAmendmentRequestResponse initializeBasicResponse(ApplicationAmendmentRequestEntity entity) { ApplicationAmendmentRequestResponse response = new ApplicationAmendmentRequestResponse(); response.setId(entity.getId()); @@ -519,6 +579,7 @@ public class ApplicationAmendmentRequestDao { } existingApplicationAmendment.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); + setAmendmentDocuments(updateRequest.getAmendmentDocuments(),existingApplicationAmendment); ApplicationAmendmentRequestEntity updatedApplicationAmendment = saveApplicationAmendmentRequestEntity(existingApplicationAmendment,oldApplicationAmendmentEntity,VersionActionTypeEnum.UPDATE); ApplicationAmendmentRequestResponse response = convertEntityToResponse(updatedApplicationAmendment); log.info("Application Amendment updated successfully: {}", response); @@ -585,7 +646,7 @@ public class ApplicationAmendmentRequestDao { String fieldId) { AmendmentFormField amendmentFormField = amendmentFormFieldMap.get(fieldId); if (amendmentFormField == null) { - throw new CustomValidationException(Status.BAD_REQUEST, GepafinConstant.APPLICATION_FORM_FIELD_NOT_FOUND); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPLICATION_FORM_FIELD_NOT_FOUND)); } return amendmentFormField; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index dbccf3b3..203b4f3c 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -813,7 +813,7 @@ public class ApplicationDao { if (status.equals(ApplicationStatusTypeEnum.SUBMIT) && Boolean.TRUE.equals(applicationEntity.getStatus().equals(ApplicationStatusTypeEnum.READY.getValue()))) { callService.validatePublishedCall(applicationEntity.getCall().getId(), userEntity.getHub().getId()); Long protocolNumber = protocolDao.getProtocolNumber(userEntity.getHub()); - ProtocolEntity protocolEntity = protocolDao.createProtocolEntity(applicationEntity, protocolNumber, userEntity.getHub().getId()); + ProtocolEntity protocolEntity = protocolDao.createProtocolEntity(applicationEntity, protocolNumber, userEntity.getHub().getId(),true); applicationEntity.setProtocol(protocolEntity); applicationEntity.setStatus(ApplicationStatusTypeEnum.SUBMIT.getValue()); applicationEntity.setSubmissionDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index 924999f2..11562a11 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -894,7 +894,7 @@ public class ApplicationEvaluationDao { } - private DocumentResponseBean createDocumentResponseBean(DocumentEntity documentEntity) { + public DocumentResponseBean createDocumentResponseBean(DocumentEntity documentEntity) { DocumentResponseBean responseBean = new DocumentResponseBean(); responseBean.setId(documentEntity.getId()); responseBean.setName(documentEntity.getFileName()); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ProtocolDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ProtocolDao.java index a7606c9a..4601ef8c 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ProtocolDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ProtocolDao.java @@ -4,6 +4,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.enums.ProtocolTypeEnum; import net.gepafin.tendermanagement.enums.VersionActionTypeEnum; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.util.LoggingUtil; @@ -42,7 +43,7 @@ public class ProtocolDao { return (maxProtocolNumber != null) ? maxProtocolNumber + 1 : startNumber; } - public ProtocolEntity createProtocolEntity(ApplicationEntity applicationEntity,Long protocolNumber, Long hubId){ + public ProtocolEntity createProtocolEntity(ApplicationEntity applicationEntity,Long protocolNumber, Long hubId,Boolean isForApplication){ ProtocolEntity protocolEntity=new ProtocolEntity(); protocolEntity.setCall(applicationEntity.getCall().getId()); LocalDateTime utcDateTime = DateTimeUtil.DateServerToUTC(LocalDateTime.now()); @@ -51,6 +52,11 @@ public class ProtocolDao { protocolEntity.setTime(LocalTime.now()); protocolEntity.setApplicationId(applicationEntity.getId()); protocolEntity.setHubId(hubId); + if(Boolean.TRUE.equals(isForApplication)){ + protocolEntity.setType(ProtocolTypeEnum.INPUT.getValue()); + }else { + protocolEntity.setType(ProtocolTypeEnum.OUTPUT.getValue()); + } protocolRepository.save(protocolEntity); /** This code is responsible for adding a version history log for "create protocol" operation. **/ diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java index 4b3fe79c..8a8701a1 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java @@ -50,4 +50,7 @@ public class ApplicationAmendmentRequestEntity extends BaseEntity { @Column(name = "end_date") private LocalDateTime endDate; + @Column(name = "amendment_document") + private String amendmentDocument; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java index ad57c1ee..47dc5065 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java @@ -28,4 +28,7 @@ public class ProtocolEntity extends BaseEntity { @Column(name="HUB_ID") private Long hubId; + @Column(name = "type") + private String type; + } diff --git a/src/main/java/net/gepafin/tendermanagement/enums/ProtocolTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/ProtocolTypeEnum.java new file mode 100644 index 00000000..f890fb42 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/enums/ProtocolTypeEnum.java @@ -0,0 +1,24 @@ +package net.gepafin.tendermanagement.enums; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum ProtocolTypeEnum { + INPUT("INPUT"), + OUTPUT("OUTPUT"); + + private String value; + + ProtocolTypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/AmendmentFieldRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/AmendmentFieldRequest.java new file mode 100644 index 00000000..99de2df0 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/request/AmendmentFieldRequest.java @@ -0,0 +1,13 @@ +package net.gepafin.tendermanagement.model.request; + +import lombok.Data; + +@Data +public class AmendmentFieldRequest { + + private String fieldId; + private String nameValue; + private String fileValue; + private Boolean isValid = false; + +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationAmendmentRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationAmendmentRequest.java index ade4c676..97e74446 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationAmendmentRequest.java +++ b/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationAmendmentRequest.java @@ -11,4 +11,5 @@ public class ApplicationAmendmentRequest { private Long responseDays; private Boolean isSendNotification; private Boolean isSendEmail; + private List amendmentDocument; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationAmendmentRequestBean.java b/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationAmendmentRequestBean.java index 94141ef0..6973254d 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationAmendmentRequestBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationAmendmentRequestBean.java @@ -8,4 +8,5 @@ import lombok.Data; public class ApplicationAmendmentRequestBean { private String note; private List applicationFormFields; + private List amendmentDocuments; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/AmendmentDocumentResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/AmendmentDocumentResponse.java new file mode 100644 index 00000000..7fb1b220 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/AmendmentDocumentResponse.java @@ -0,0 +1,12 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; + +@Data +public class AmendmentDocumentResponse { + + private String fieldId; + private String nameValue; + private DocumentResponseBean fileValue; + private Boolean isValid = false; +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponse.java index 5523098e..0f77f602 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponse.java @@ -21,6 +21,7 @@ public class ApplicationAmendmentRequestResponse { private String beneficiaryName; private List formFields; private List applicationFormFields; + private List amendmentDocuments; private Long applicationId; private Long applicationEvaluationId; private LocalDateTime evaluationEndDate; diff --git a/src/main/resources/db/changelog/db.changelog-1.0.0.xml b/src/main/resources/db/changelog/db.changelog-1.0.0.xml index eb77049e..607b22b3 100644 --- a/src/main/resources/db/changelog/db.changelog-1.0.0.xml +++ b/src/main/resources/db/changelog/db.changelog-1.0.0.xml @@ -1991,5 +1991,12 @@ - + + + + + + + + diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index 24150081..974162f9 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -335,3 +335,4 @@ upload.document.is.only.for.gepafin = Document cant be uploaded, this is only av appointment.created.successfully = Appointment created successfully. error.try.again = Service call error while performing the operation. Please try again. document.uploading.is.in.progress = Document uploading is in progress. +all.document.checked.and.one.checklist.checked=All document should be checked and at least one checklist should be checked. \ No newline at end of file diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index 084fe6aa..97390d1d 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -325,3 +325,4 @@ upload.document.is.only.for.gepafin = Il documento non pu? essere caricato, ques appointment.created.successfully = Appuntamento creato con successo. error.try.again = Errore di chiamata di servizio durante l'esecuzione dell'operazione. Riprovare. document.uploading.is.in.progress = Il documento è in fase di caricamento. +all.document.checked.and.one.checklist.checked=Tutti i documenti devono essere controllati e almeno una checklist deve essere controllata. \ No newline at end of file