Merge pull request #247 from Kitzanos/feature/GEPAFINBE-190

GEPAFINBE-190 (Handle the password expired error for login to Odessa API.)
This commit is contained in:
Rinaldo
2025-03-20 15:36:58 +01:00
committed by GitHub
4 changed files with 134 additions and 48 deletions

View File

@@ -496,6 +496,9 @@ public class GepafinConstant {
public static final String PDF_TRUE="PDF_TRUE"; public static final String PDF_TRUE="PDF_TRUE";
public static final String PDF_FALSE="PDF_FALSE"; public static final String PDF_FALSE="PDF_FALSE";
public static final String PASSWORD_EXPIRED = "PasswordExpired";
public static final String PASSWORD_EXPIRED_LOGIN_TO_ODESSA = "password.expired.for.login.to.odessa";
} }

View File

@@ -14,6 +14,7 @@ import net.gepafin.tendermanagement.config.jwt.TokenProvider;
import net.gepafin.tendermanagement.constants.AppointmentApiConstant; import net.gepafin.tendermanagement.constants.AppointmentApiConstant;
import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.entities.ApplicationEntity; import net.gepafin.tendermanagement.entities.ApplicationEntity;
import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity;
import net.gepafin.tendermanagement.entities.CompanyEntity; import net.gepafin.tendermanagement.entities.CompanyEntity;
import net.gepafin.tendermanagement.entities.DocumentEntity; import net.gepafin.tendermanagement.entities.DocumentEntity;
import net.gepafin.tendermanagement.entities.HubEntity; import net.gepafin.tendermanagement.entities.HubEntity;
@@ -39,6 +40,7 @@ import net.gepafin.tendermanagement.repositories.UserRepository;
import net.gepafin.tendermanagement.service.ApplicationService; import net.gepafin.tendermanagement.service.ApplicationService;
import net.gepafin.tendermanagement.service.CompanyService; import net.gepafin.tendermanagement.service.CompanyService;
import net.gepafin.tendermanagement.service.feignClient.AppointmentApiService; import net.gepafin.tendermanagement.service.feignClient.AppointmentApiService;
import net.gepafin.tendermanagement.service.impl.ApplicationEvaluationServiceImpl;
import net.gepafin.tendermanagement.util.LoggingUtil; import net.gepafin.tendermanagement.util.LoggingUtil;
import net.gepafin.tendermanagement.util.Utils; import net.gepafin.tendermanagement.util.Utils;
import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException;
@@ -60,6 +62,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@@ -138,6 +141,9 @@ public class AppointmentDao {
@Autowired @Autowired
private UserRepository userRepository; private UserRepository userRepository;
@Autowired
private ApplicationEvaluationServiceImpl applicationEvaluationService;
private final Map<Long, ExecutorService> executorMap = new ConcurrentHashMap<>(); private final Map<Long, ExecutorService> executorMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Long, ExecutorService> threadForDocumentMap = new ConcurrentHashMap<>(); private final ConcurrentHashMap<Long, ExecutorService> threadForDocumentMap = new ConcurrentHashMap<>();
@@ -162,10 +168,85 @@ public class AppointmentDao {
applicationRepository.save(application); applicationRepository.save(application);
// Start async processing // Start async processing
HubEntity hub = hubRepository.findByHubId(application.getHubId());
loginToOdessa(hub, application);
startAsyncNdgProcessing(applicationId); startAsyncNdgProcessing(applicationId);
throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS));
} }
private HubEntity loginToOdessa(HubEntity hub, ApplicationEntity application) {
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();
log.info("Got the auth for login to odessa {}", authJwtToken);
hub.setAuthToken(authJwtToken);
hubRepository.save(hub);
Map<String, Object> body = Collections.emptyMap();
ResponseEntity<Object> responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body);
if (responseLogin.getStatusCode() == HttpStatus.OK) {
log.info("Login successful to odessa. Parsing response.");
String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody());
AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson);
// Validate and save token
if (parsedResponse.getTokenId() != null) {
hub.setAppointmentAuthTokenId(parsedResponse.getTokenId());
hub.setAreaCode(parsedResponse.getAreaCode());
hubRepository.save(hub);
log.info("Saved new authToken and areaCode for Hub.");
return hub;
} else {
throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again.");
}
}
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN));
}
catch (FeignException.Forbidden forbiddenException) {
logForbiddenError();
// Extract raw response body
String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response
// Parse JSON to check for "PasswordExpired"
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(responseBody);
JsonNode errorsNode = rootNode.path("errors");
if (errorsNode.isArray()) {
for (JsonNode error : errorsNode) {
// Check the main errorCode
if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) {
application.setNdgStatus(GepafinConstant.NDG_FAILED);
applicationRepository.save(application);
throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
}
// Check inside "subErrors"
JsonNode subErrorsNode = error.path("subErrors");
if (subErrorsNode.isArray()) {
for (JsonNode subError : subErrorsNode) {
if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) {
application.setNdgStatus(GepafinConstant.NDG_FAILED);
applicationRepository.save(application);
throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
}
}
}
}
}
} catch (IOException e) {
log.error("Error parsing JSON response: {}", e.getMessage());
}
// Regenerate the token and retry
loginToOdessa(hub, application);
}
catch (Exception e) {
log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e);
throw new RuntimeException("Authentication failed on Odessa. try again", e);
}
return null;
}
private void startAsyncNdgProcessing(Long applicationId) { private void startAsyncNdgProcessing(Long applicationId) {
// Check if a thread is already running for this application // Check if a thread is already running for this application
@@ -259,18 +340,11 @@ public class AppointmentDao {
application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); application.setStatus(ApplicationStatusTypeEnum.NDG.getValue());
applicationRepository.save(application); applicationRepository.save(application);
companyRepository.save(company); companyRepository.save(company);
ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId());
Map<String, String> placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION);
log.info("NDG saved successfully for applicationId: {}", application.getId()); 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; break;
} }
@@ -309,10 +383,6 @@ public class AppointmentDao {
private void saveNdgAndIdVisura(ApplicationEntity application, CompanyEntity company, String ndg, String idVisura) { 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);
application.setNdg(ndg); application.setNdg(ndg);
application.setIdVisura(idVisura); application.setIdVisura(idVisura);
application.setNdgStatus(GepafinConstant.NDG_GENERATED); application.setNdgStatus(GepafinConstant.NDG_GENERATED);
@@ -320,17 +390,10 @@ public class AppointmentDao {
company.setNdg(ndg); company.setNdg(ndg);
companyRepository.save(company); companyRepository.save(company);
applicationRepository.save(application); applicationRepository.save(application);
Map<String ,String> placeHolders=notificationDao.sendNotificationToBeneficiary(application,NotificationTypeEnum.NDG_GENERATION); ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId());
Map<String, String> placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION); notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION);
// /** 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()); log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg());
} }
@@ -358,18 +421,12 @@ public class AppointmentDao {
private HubEntity authenticateAndSaveToken(HubEntity hub) { private HubEntity authenticateAndSaveToken(HubEntity hub) {
// HubEntity oldHubData = Utils.getClonedEntityForData(hub);
try { try {
//code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call //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(); String authJwtToken = Utils.generateAuthTokenForLoginToOdessa();
log.info("Got the auth for login to odessa {}", authJwtToken); log.info("Got the auth for login to odessa {}", authJwtToken);
hub.setAuthToken(authJwtToken); hub.setAuthToken(authJwtToken);
hubRepository.save(hub); 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());
// Prepare the request body (adjust if necessary for login API) // Prepare the request body (adjust if necessary for login API)
Map<String, Object> body = Collections.emptyMap(); Map<String, Object> body = Collections.emptyMap();
// Perform login API call // Perform login API call
@@ -387,13 +444,6 @@ public class AppointmentDao {
hub.setAreaCode(parsedResponse.getAreaCode()); hub.setAreaCode(parsedResponse.getAreaCode());
hubRepository.save(hub); 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());
log.info("Saved new authToken and areaCode for Hub."); log.info("Saved new authToken and areaCode for Hub.");
return hub; return hub;
} else { } else {
@@ -404,6 +454,38 @@ public class AppointmentDao {
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN));
} catch (FeignException.Forbidden forbiddenException) { } catch (FeignException.Forbidden forbiddenException) {
logForbiddenError(); logForbiddenError();
// Extract raw response body
String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response
// Parse JSON to check for "PasswordExpired"
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(responseBody);
JsonNode errorsNode = rootNode.path("errors");
if (errorsNode.isArray()) {
for (JsonNode error : errorsNode) {
// Check the main errorCode
if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) {
throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
}
// Check inside "subErrors"
JsonNode subErrorsNode = error.path("subErrors");
if (subErrorsNode.isArray()) {
for (JsonNode subError : subErrorsNode) {
if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) {
throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
}
}
}
}
}
} catch (IOException e) {
log.error("Error parsing JSON response: {}", e.getMessage());
}
// Regenerate the token and retry // Regenerate the token and retry
regenerateTokenAndSave(hub); regenerateTokenAndSave(hub);
} catch (Exception e) { } catch (Exception e) {
@@ -435,14 +517,8 @@ public class AppointmentDao {
} }
private String regenerateTokenAndSave(HubEntity hub) { private String regenerateTokenAndSave(HubEntity hub) {
try {
hub = authenticateAndSaveToken(hub); hub = authenticateAndSaveToken(hub);
return "Bearer " + hub.getAppointmentAuthTokenId(); return "Bearer " + hub.getAppointmentAuthTokenId();
} catch (Exception e) {
log.error("Failed to regenerate token from Odessa: {}", e.getMessage());
throw new RuntimeException("Token regeneration failed from Odessa.", e);
}
} }
private AppointmentLoginResponse createVisura(CompanyEntity company, String authorizationToken, HubEntity hub) { private AppointmentLoginResponse createVisura(CompanyEntity company, String authorizationToken, HubEntity hub) {
@@ -625,6 +701,7 @@ public class AppointmentDao {
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION)); throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION));
} }
hub = authenticateAndSaveToken(hub);
// Generate authorization token and fetch template data // Generate authorization token and fetch template data
String authorizationToken = getBearerToken(hub); String authorizationToken = getBearerToken(hub);
Long appointmentTemplateId = application.getCall().getAppointmentTemplateId(); Long appointmentTemplateId = application.getCall().getAppointmentTemplateId();
@@ -787,6 +864,10 @@ public class AppointmentDao {
Claims claims = tokenProvider.getClaimsFromToken(tokenProvider.extractTokenFromRequest(request)); Claims claims = tokenProvider.getClaimsFromToken(tokenProvider.extractTokenFromRequest(request));
Long hubId = Utils.extractHubIdFromPayload(claims.getSubject()); Long hubId = Utils.extractHubIdFromPayload(claims.getSubject());
// Authenticate the hub before proceeding
HubEntity hub = hubRepository.findByHubId(hubId);
authenticateAndSaveToken(hub);
if (systemDoc.getDocumentAttachmentId() != null) { if (systemDoc.getDocumentAttachmentId() != null) {
// If the documentAttachmentId is already set, return the response // If the documentAttachmentId is already set, return the response
log.info("Document already uploaded with documentAttachmentId: {}", systemDoc.getDocumentAttachmentId()); log.info("Document already uploaded with documentAttachmentId: {}", systemDoc.getDocumentAttachmentId());
@@ -825,8 +906,6 @@ public class AppointmentDao {
} }
}); });
return null; return null;
// 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) { private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) {

View File

@@ -396,3 +396,5 @@ validation.failed.checklist=Validation failed for checklist.
error.invalid.limit=Limit should be between 1 and 3000. error.invalid.limit=Limit should be between 1 and 3000.
insufficient.score.msg = Insufficient score to pass to the technical and economic-financial evaluation insufficient.score.msg = Insufficient score to pass to the technical and economic-financial evaluation
password.expired.for.login.to.odessa = Odessa login password has been expired.

View File

@@ -387,4 +387,6 @@ validation.failed.checklist=Convalida fallita per la checklist.
error.invalid.limit=Il limite dovrebbe essere compreso tra 1 e 3000. error.invalid.limit=Il limite dovrebbe essere compreso tra 1 e 3000.
insufficient.score.msg = Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria insufficient.score.msg = Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria
validation.table.message=I dati per il campo {0} non sono presenti. validation.table.message=I dati per il campo {0} non sono presenti.
password.expired.for.login.to.odessa = La password di accesso a Odessa è scaduta