From cac4a7efacd6d975697199197217e57ff2409d4c Mon Sep 17 00:00:00 2001 From: rajesh Date: Mon, 2 Dec 2024 18:20:08 +0530 Subject: [PATCH 01/16] User management --- .../config/SamlSuccessHandler.java | 2 +- .../constants/GepafinConstant.java | 1 + .../gepafin/tendermanagement/dao/UserDao.java | 195 ++++++++++++++---- .../entities/SystemEmailTemplatesEntity.java | 2 + .../enums/EmailScenarioTypeEnum.java | 2 + .../enums/UserActionContextEnum.java | 4 +- .../repositories/UserRepository.java | 32 ++- .../tendermanagement/service/UserService.java | 2 +- .../service/impl/AuthenticationService.java | 19 +- .../service/impl/UserServiceImpl.java | 4 +- .../gepafin/tendermanagement/util/Utils.java | 11 +- .../web/rest/api/UserApi.java | 4 +- .../api/impl/CustomUserDetailsService.java | 6 +- .../web/rest/api/impl/UserApiController.java | 23 ++- .../db/changelog/db.changelog-1.0.0.xml | 12 +- ...template_for_reset_password_28_11_2024.sql | 65 ++++++ ...tem_email_template_for_user_28_11_2024.sql | 29 +++ 17 files changed, 351 insertions(+), 62 deletions(-) create mode 100644 src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql create mode 100644 src/main/resources/db/dump/insert_system_email_template_for_user_28_11_2024.sql diff --git a/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java b/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java index 14f2b1bf..850d2451 100644 --- a/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java +++ b/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java @@ -63,7 +63,7 @@ public class SamlSuccessHandler implements AuthenticationSuccessHandler { Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) samlAuth.getPrincipal(); Map> userAttributes = principal.getAttributes(); - String token = Utils.generateSecureToken(); + String token = Utils.generateSecureSamlToken(); logger.info("SAML User Attributes: " + userAttributes); // Extracting raw SAML response diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 97730f80..aaa3279b 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -302,5 +302,6 @@ public class GepafinConstant { public static final String USER_ID = "userId"; public static final String LOGIN_ATTEMPT_ID = "loginAttemptId"; public static final String USER_ACTION_ID = "userActionId"; + public static final String RESET_PASSWORD_URL_FORMAT = "/reset-password?token=%s&email=%s"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java index da5c6f07..41483d36 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java @@ -6,21 +6,15 @@ import net.gepafin.tendermanagement.config.SamlSuccessHandler; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.*; -import net.gepafin.tendermanagement.enums.RoleStatusEnum; -import net.gepafin.tendermanagement.enums.UserActionContextEnum; -import net.gepafin.tendermanagement.enums.UserActionLogsEnum; -import net.gepafin.tendermanagement.enums.UserStatusEnum; -import net.gepafin.tendermanagement.enums.VersionActionTypeEnum; +import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.model.request.*; -import net.gepafin.tendermanagement.model.response.CompanyResponse; -import net.gepafin.tendermanagement.model.response.RoleResponseBean; -import net.gepafin.tendermanagement.model.response.UserSamlResponse; -import net.gepafin.tendermanagement.model.response.UserResponseBean; +import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.JWTToken; import net.gepafin.tendermanagement.repositories.BeneficiaryRepository; import net.gepafin.tendermanagement.repositories.UserRepository; import net.gepafin.tendermanagement.service.HubService; import net.gepafin.tendermanagement.service.RoleService; +import net.gepafin.tendermanagement.service.SystemEmailTemplatesService; import net.gepafin.tendermanagement.service.impl.AuthenticationService; import net.gepafin.tendermanagement.util.LoggingUtil; import net.gepafin.tendermanagement.util.Utils; @@ -39,6 +33,7 @@ import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static net.gepafin.tendermanagement.util.Utils.setIfUpdated; @@ -90,6 +85,18 @@ public class UserDao { @Autowired private HttpServletRequest request; + @Autowired + private SystemEmailTemplatesService systemEmailTemplatesService; + + @Autowired + private EmailLogDao emailLogDao; + + @Autowired + private EmailNotificationDao emailNotificationDao; + + @Value("${fe.base.url}") + private String feBaseUrl; + public JWTToken createUser(HttpServletRequest request, String tempToken, UserReq userReq) { if (StringUtils.isEmpty(userReq.getHubUuid())) { @@ -120,9 +127,35 @@ public class UserDao { /** This code is responsible for adding a version history log for the "Create user" operation. **/ loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).newData(userEntity).build()); + if(beneficiary == null){ + sendEmailToOnboardingUser(userEntity); + } return token; } - + public void sendEmailToOnboardingUser(UserEntity userEntity){ + SystemEmailTemplateResponse emailTemplate = systemEmailTemplatesService.retrieveTemplateByTypeAndCall( + SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum.USER_ONBOARDING, userEntity.getHub(), null); + EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(emailTemplate.getEmailScenario(), RecipientTypeEnum.USER, userEntity.getId(), userEntity.getEmail(), + userEntity.getId(), null, null, null); + String firstName = userEntity.getFirstName() != null ? userEntity.getFirstName() : ""; + String lastName = userEntity.getLastName() != null ? userEntity.getLastName() : ""; + String userName = String.join(" ", firstName, lastName).trim(); + String subject = Utils.replacePlaceholders(emailTemplate.getSubject(), Map.of( + "{{user_name}}", userName + )); + String body = Utils.replacePlaceholders(emailTemplate.getHtmlContent(), Map.of( + "{{user_name}}", userName, + "{{user_email}}", userEntity.getEmail() + )); + + emailNotificationDao.sendMail( + userEntity.getHub().getId(), + subject, + body, + List.of(userEntity.getEmail()), + emailLogRequest + ); + } private BeneficiaryEntity createBeneficiary(RoleEntity roleEntity, UserReq userReq, HubEntity hub) { BeneficiaryEntity beneficiaryEntity = null; if (RoleStatusEnum.ROLE_BENEFICIARY.getValue().equals(roleEntity.getRoleType())) { @@ -164,12 +197,10 @@ public class UserDao { Translator.toLocale(GepafinConstant.VALIDATE_EMAIL)); } log.info("Creating user with email: {}", userReq.getEmail()); - if (userRepository.existsByEmailIgnoreCaseAndHubUniqueUuid(userReq.getEmail(), userReq.getHubUuid())) { - log.error("User creation failed: Email {} already exists", userReq.getEmail()); - throw new CustomValidationException(Status.VALIDATION_ERROR, - Translator.toLocale(GepafinConstant.EMAIL_ALREADY_EXISTS)); - } - if (Boolean.FALSE.equals(StringUtils.isEmpty(userReq.getCodiceFiscale())) + RoleEntity roleEntity = roleService.validateRole(userReq.getRoleId()); + validateDuplicateEmail(userReq.getEmail(), userReq.getHubUuid(), roleEntity.getRoleType()); + + if (Boolean.FALSE.equals(StringUtils.isEmpty(userReq.getCodiceFiscale())) && userRepository.existsByBeneficiaryCodiceFiscaleAndHubId(userReq.getCodiceFiscale(), hub.getId())) { log.error("User creation failed: CodiceFiscale {} already exists", userReq.getCodiceFiscale()); throw new CustomValidationException(Status.VALIDATION_ERROR, @@ -191,8 +222,29 @@ public class UserDao { } } } + private void validateDuplicateEmail(String email, String hubUuid, String roleType) { + Boolean existsForNonBeneficiaries = userRepository.existsByEmailIgnoreCaseForNonBeneficiaries( + email, hubUuid, RoleStatusEnum.ROLE_BENEFICIARY.getValue()); - private void validatePassword(String password, String confirmPassword, String tempToken) { + Boolean beneficiaryExistsInHub = userRepository.existsByEmailIgnoreCaseForBeneficiaries( + email, hubUuid, RoleStatusEnum.ROLE_BENEFICIARY.getValue()); + + if (Boolean.TRUE.equals(RoleStatusEnum.ROLE_BENEFICIARY.getValue().equals(roleType))) { + if (beneficiaryExistsInHub) { + throw new CustomValidationException(Status.VALIDATION_ERROR, + Translator.toLocale(GepafinConstant.EMAIL_ALREADY_EXISTS)); + } + } + else { + if (existsForNonBeneficiaries) { + throw new CustomValidationException(Status.VALIDATION_ERROR, + Translator.toLocale(GepafinConstant.EMAIL_ALREADY_EXISTS)); + } + } + } + + + private void validatePassword(String password, String confirmPassword, String tempToken) { if (StringUtils.isEmpty(password) || StringUtils.isEmpty(confirmPassword)) { if(tempToken == null) { throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.VALIDATE_PASSWORD)); @@ -263,7 +315,7 @@ public class UserDao { userEntity.setAddress(userReq.getAddress()); userEntity.setPhoneNumber(userReq.getPhoneNumber()); userEntity.setDateOfBirth(userReq.getDateOfBirth()); - } + } return userRepository.save(userEntity); } @@ -362,25 +414,83 @@ public class UserDao { return user; } - public String initiatePasswordReset(InitiatePasswordResetReq resetReq) { - UserEntity user = userRepository - .findByEmailIgnoreCaseAndHubUniqueUuid(resetReq.getEmail(), resetReq.getHubUuid()) - .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, - Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); + public void initiatePasswordReset(InitiatePasswordResetReq resetReq) { + UserEntity user = userRepository.findUserExcludingRoleType( + resetReq.getEmail(), + resetReq.getHubUuid(), + RoleStatusEnum.ROLE_BENEFICIARY.getValue() + ).orElseThrow(() -> new ResourceNotFoundException( + Status.NOT_FOUND, + Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG) + )); + UserEntity oldUserEntity = Utils.getClonedEntityForData(user); String token = Utils.generateSecureToken(); user.setResetPasswordToken(token); userRepository.save(user); + + /** This code is responsible for adding a version history log for the "Initiate password reset request" operation **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldUserEntity).newData(user).build()); + log.info("Password reset token generated for user: {}", resetReq.getEmail()); - return token; + + sendResetPasswordTokenEmail(user, token); + } + public void sendResetPasswordTokenEmail(UserEntity user, String token) { + + SystemEmailTemplateResponse emailTemplate = systemEmailTemplatesService.retrieveTemplateByTypeAndCall( + SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum.PASSWORD_RESET, user.getHub(), null); + String redirectUrl = feBaseUrl; + if (Boolean.FALSE.equals(StringUtils.isEmpty(user.getHub().getDomainName()))) { + redirectUrl = user.getHub().getDomainName(); + } + + EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest( + emailTemplate.getEmailScenario(), + RecipientTypeEnum.USER, + user.getId(), + user.getEmail(), + user.getId(), + null, + null, + null); + redirectUrl = String.format( + user.getHub().getDomainName() + GepafinConstant.RESET_PASSWORD_URL_FORMAT, + token, + user.getEmail() + ); + String firstName = user.getFirstName() != null ? user.getFirstName() : ""; + String lastName = user.getLastName() != null ? user.getLastName() : ""; + String userName = String.join(" ", firstName, lastName).trim(); + String subject = Utils.replacePlaceholders(emailTemplate.getSubject(), Map.of( + "{{user_name}}", userName + )); + String body = Utils.replacePlaceholders(emailTemplate.getHtmlContent(), Map.of( + "{{user_name}}", userName, + "{{reset_password_link}}", redirectUrl + )); + emailNotificationDao.sendMail( + user.getHub().getId(), + subject, + body, + List.of(user.getEmail()), + emailLogRequest + ); + + log.info("Password reset token email sent to: {}", user.getEmail()); } public Boolean resetPassword(ResetPasswordReq resetPasswordReq) { - UserEntity user = userRepository - .findByEmailIgnoreCaseAndHubUniqueUuid(resetPasswordReq.getEmail(), resetPasswordReq.getHubUuid()) - .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, - Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); - + UserEntity user = userRepository.findUserExcludingRoleType( + resetPasswordReq.getEmail(), + resetPasswordReq.getHubUuid(), + RoleStatusEnum.ROLE_BENEFICIARY.getValue() + ).orElseThrow(() -> new ResourceNotFoundException( + Status.NOT_FOUND, + Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG) + )); + + UserEntity oldUserEntity = Utils.getClonedEntityForData(user); if (!resetPasswordReq.getNewPassword().equals(resetPasswordReq.getConfirmPassword())) { log.info("User creation failed: Passwords do not match for email {}", user.getEmail()); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PASSWORD_DOESNT_MATCH)); @@ -395,25 +505,36 @@ public class UserDao { user.setPassword(passwordEncoder.encode(resetPasswordReq.getNewPassword())); user.setResetPasswordToken(null); userRepository.save(user); + + /** This code is responsible for adding a version history log for the "Reset Password " operation **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldUserEntity).newData(user).build()); + log.info("Password successfully reset for user: {}", resetPasswordReq.getEmail()); return true; } - public Boolean changePassword(UserEntity userEntity, ChangePasswordRequest request) { + public Boolean changePassword(UserEntity userEntity, ChangePasswordRequest changePasswordRequest) { UserEntity user = userRepository - .findByEmailIgnoreCaseAndHubUniqueUuid(request.getEmail(), userEntity.getHub().getUniqueUuid()) - .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, - Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); - - if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { + .findUserExcludingRoleType(changePasswordRequest.getEmail(), userEntity.getHub().getUniqueUuid(),RoleStatusEnum.ROLE_BENEFICIARY.getValue()) + .orElseThrow(() -> new ResourceNotFoundException( + Status.NOT_FOUND, + Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG) + )); + UserEntity oldUserEntity = Utils.getClonedEntityForData(userEntity); + if (!passwordEncoder.matches(changePasswordRequest.getPassword(), user.getPassword())) { throw new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.CURRENT_PASSWORD_INCORRECT)); } - if (!request.getNewPassword().equals(request.getConfirmPassword())) { + if (!changePasswordRequest.getNewPassword().equals(changePasswordRequest.getConfirmPassword())) { log.info("User creation failed: Passwords do not match for email {}", user.getEmail()); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PASSWORD_DOESNT_MATCH)); } - user.setPassword(passwordEncoder.encode(request.getNewPassword())); + user.setPassword(passwordEncoder.encode(changePasswordRequest.getNewPassword())); userRepository.save(user); + + /** This code is responsible for adding a version history log for the "Change user password" operation **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldUserEntity).newData(user).build()); + + return true; } public void logout(HttpServletRequest request, HttpServletResponse response) { diff --git a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java index f7974eca..dac3a432 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java @@ -47,6 +47,8 @@ public class SystemEmailTemplatesEntity extends BaseEntity { INADMISSIBILITY_NOTIFICATION_DUE_TO_FAILURE("INADMISSIBILITY_NOTIFICATION_DUE_TO_FAILURE"), ADMISSIBILITY_NOTIFICATION("ADMISSIBILITY_NOTIFICATION"), AMENDMENT_REMINDER("AMENDMENT_REMINDER"), + USER_ONBOARDING("USER_ONBOARDING"), + PASSWORD_RESET("PASSWORD_RESET"), INADMISSIBILITY_TEMPLATE("INADMISSIBILITY_NOTIFICATION"); private String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java index 493f987c..415f1dd2 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java @@ -9,6 +9,8 @@ public enum EmailScenarioTypeEnum { APPLICATION_AMENDMENT_EXPIRED("APPLICATION_AMENDMENT_EXPIRED"), APPLICATION_AMENDMENT_REMINDER("APPLICATION_AMENDMENT_REMINDER"), APPLICATION_APPROVED("APPLICATION_APPROVED"), + USER_CREATION("USER_CREATION"), + PASSWORD_RESET_REQUEST("PASSWORD_RESET_REQUEST"), APPLICATION_REJECTED("APPLICATION_REJECTED"); private final String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java index 0a92143b..a4d7f3c2 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java @@ -24,7 +24,9 @@ public enum UserActionContextEnum { VALIDATE_EXISTING_USER_WITH_SPID_TOKEN("VALIDATE_EXISTING_USER_WITH_SPID_TOKEN"), GET_VALID_USER_DETAILS("GET_VALID_USER_DETAILS"), GET_ALL_USERS_BY_ROLE("GET_ALL_USERS_BY_ROLE"), - + CHANGE_USER_PASSWORD("CHANGE_USER_PASSWORD"), + INITIATE_PASSWORD_RESET_REQUEST("INITIATE_PASSWORD_RESET_REQUEST"), + RESET_USER_PASSWORD("RESET_USER_PASSWORD"), /** application action context **/ GET_APPLICATION("GET_APPLICATION"), CREATE_UPDATE_APPLICATION_FORM("CREATE_UPDATE_APPLICATION_FORM"), diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java index 47ab16b8..3a0cf957 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java @@ -2,6 +2,8 @@ package net.gepafin.tendermanagement.repositories; import net.gepafin.tendermanagement.entities.UserEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; @@ -12,9 +14,16 @@ public interface UserRepository extends JpaRepository { UserEntity findByBeneficiaryId(Long beneficiaryId); - Optional findByEmailIgnoreCaseAndHubUniqueUuid(String email, String hubUuid); +// Optional findByEmailIgnoreCaseAndHubUniqueUuid(String email, String hubUuid); - boolean existsByEmailIgnoreCaseAndHubUniqueUuid(String email, String hubUuid); + @Query("SELECT u FROM UserEntity u WHERE LOWER(u.email) = LOWER(:email) AND u.hub.uniqueUuid = :hubUuid AND u.roleEntity.roleType <> :roleType") + Optional findUserExcludingRoleType( + @Param("email") String email, + @Param("hubUuid") String hubUuid, + @Param("roleType") String roleType + ); + +// boolean existsByEmailIgnoreCaseAndHubUniqueUuid(String email, String hubUuid); List findByRoleEntityIdAndHubId(Long roleId, Long hubId); @@ -24,5 +33,22 @@ public interface UserRepository extends JpaRepository { Optional findByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); - boolean existsByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); + Boolean existsByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); + + @Query("SELECT COUNT(u) > 0 " + + "FROM UserEntity u " + + "WHERE u.email = :email " + + "AND u.hub.uniqueUuid = :hubUuid " + + "AND u.roleEntity.roleType <> :beneficiaryRoleType") + Boolean existsByEmailIgnoreCaseForNonBeneficiaries(@Param("email") String email, + @Param("hubUuid") String hubUuid, + @Param("beneficiaryRoleType") String beneficiaryRoleType); + + @Query("SELECT CASE WHEN COUNT(u) > 0 THEN true ELSE false END " + + "FROM UserEntity u " + + "WHERE LOWER(u.email) = LOWER(:email) AND u.hub.uniqueUuid = :hubUuid " + + "AND u.roleEntity.roleType = :beneficiaryRoleType") + Boolean existsByEmailIgnoreCaseForBeneficiaries(String email, String hubUuid, String beneficiaryRoleType); + + } diff --git a/src/main/java/net/gepafin/tendermanagement/service/UserService.java b/src/main/java/net/gepafin/tendermanagement/service/UserService.java index 21109a5e..aa534bea 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/UserService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/UserService.java @@ -27,7 +27,7 @@ public interface UserService { UserEntity validateUser(Long userId); - String initiatePasswordReset(InitiatePasswordResetReq resetReq); + void initiatePasswordReset(InitiatePasswordResetReq resetReq); Boolean resetPassword(ResetPasswordReq resetPasswordReq); diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java b/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java index d4dab052..1f0194d9 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java @@ -12,10 +12,7 @@ import net.gepafin.tendermanagement.entities.HubEntity; import net.gepafin.tendermanagement.entities.LoginAttemptEntity; import net.gepafin.tendermanagement.entities.SamlResponseEntity; import net.gepafin.tendermanagement.entities.UserEntity; -import net.gepafin.tendermanagement.enums.LoginAttemptResultEnum; -import net.gepafin.tendermanagement.enums.LoginAttemptTypeEnum; -import net.gepafin.tendermanagement.enums.UserStatusEnum; -import net.gepafin.tendermanagement.enums.VersionActionTypeEnum; +import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.model.request.LoginReq; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.model.response.CompanyResponse; @@ -40,6 +37,7 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.stereotype.Service; @@ -95,15 +93,22 @@ public class AuthenticationService { LoginAttemptEntity loginAttemptEntity = prepareLoginAttemptEntity(loginReq, request); try { log.info("Attempting login for email: {}", loginReq.getEmail()); + user = userRepository.findUserExcludingRoleType( + loginReq.getEmail(), + loginReq.getHubUuid(), + RoleStatusEnum.ROLE_BENEFICIARY.getValue() + ).orElseThrow(() -> new ResourceNotFoundException( + Status.NOT_FOUND, + Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG) + )); + String emailWithHubId = loginReq.getEmail()+":"+loginReq.getHubUuid(); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( emailWithHubId, loginReq.getPassword()); Authentication authentication = this.authenticationManager.authenticate(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); log.info("Authentication successful for email: {}", loginReq.getEmail()); - user = userRepository.findByEmailIgnoreCaseAndHubUniqueUuid(loginReq.getEmail(), loginReq.getHubUuid()) - .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, - Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); + loginAttemptEntity.setUserId(user.getId()); if (Boolean.FALSE.equals(UserStatusEnum.ACTIVE.getValue().equals(user.getStatus()))) { throw new ResourceNotFoundException(Status.NOT_FOUND, diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java index 9f95b830..744fbacb 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java @@ -77,8 +77,8 @@ public class UserServiceImpl implements UserService { } @Override - public String initiatePasswordReset(InitiatePasswordResetReq resetReq) { - return userDao.initiatePasswordReset(resetReq); + public void initiatePasswordReset(InitiatePasswordResetReq resetReq) { + userDao.initiatePasswordReset(resetReq); } @Override diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index 4949e5d5..b3993900 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -201,7 +201,7 @@ public class Utils { return new String(decodedBytes, StandardCharsets.UTF_8); } - public static String generateSecureToken() { + public static String generateSecureSamlToken() { SecureRandom secureRandom = new SecureRandom(); byte[] tokenBytes = new byte[24]; secureRandom.nextBytes(tokenBytes); @@ -209,7 +209,14 @@ public class Utils { log.debug("Generated secure token: {}", token); return token; } - + public static String generateSecureToken() { + SecureRandom secureRandom = new SecureRandom(); + byte[] tokenBytes = new byte[5]; + secureRandom.nextBytes(tokenBytes); + String token = Base64.getUrlEncoder().withoutPadding().encodeToString(tokenBytes); + log.debug("Generated secure token: {}", token); + return token; + } public static Map> convertStringIntoMap(String jsonString) { try { return mapper.readValue(jsonString, new TypeReference>>() { diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java index c7b8e530..8bf9b780 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java @@ -118,7 +118,7 @@ public interface UserApi { @RequestMapping(value = "/reset-password/initiate", produces = {"application/json"}, method = RequestMethod.POST) - ResponseEntity> initiatePasswordReset( + ResponseEntity> initiatePasswordReset(HttpServletRequest request, @Parameter(description = "Initiate password reset request object", required = true) @Valid @RequestBody InitiatePasswordResetReq initiatePasswordResetReq); @Operation(summary = "Api to reset password", @@ -131,7 +131,7 @@ public interface UserApi { @RequestMapping(value = "/reset-password", produces = {"application/json"}, method = RequestMethod.POST) - ResponseEntity> resetPassword( + ResponseEntity> resetPassword(HttpServletRequest request, @Parameter(description = "Reset password request object", required = true) @Valid @RequestBody ResetPasswordReq resetPasswordReq); @Operation(summary = "Api to change user password", responses = { diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CustomUserDetailsService.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CustomUserDetailsService.java index d26ab560..dd6ef1ae 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CustomUserDetailsService.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CustomUserDetailsService.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.web.rest.api.impl; import net.gepafin.tendermanagement.entities.RoleEntity; import net.gepafin.tendermanagement.entities.UserEntity; +import net.gepafin.tendermanagement.enums.RoleStatusEnum; import net.gepafin.tendermanagement.repositories.UserRepository; import org.slf4j.Logger; @@ -35,7 +36,10 @@ public class CustomUserDetailsService implements UserDetailsService { String email = loginParts[0]; String hubId = loginParts[1]; - UserEntity user = userRepository.findByEmailIgnoreCaseAndHubUniqueUuid(email, hubId) + UserEntity user = userRepository.findUserExcludingRoleType( + email, + hubId, + RoleStatusEnum.ROLE_BENEFICIARY.getValue()) .orElseThrow( () -> new UsernameNotFoundException("User " + email + " was not found in the database")); return createSpringSecurityUser(user); diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java index 7ff12c2a..9f02a8f6 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java @@ -125,20 +125,35 @@ public class UserApiController implements UserApi { @Override public ResponseEntity> changePassword(HttpServletRequest httpServletRequest, @Valid @RequestBody ChangePasswordRequest request) { log.info("Change Password attempt for email: {}", request.getEmail()); + + /** This code is responsible for "Change user password" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(httpServletRequest).actionType(UserActionLogsEnum.UPDATE) + .actionContext(UserActionContextEnum.CHANGE_USER_PASSWORD).build()); + userService.changePassword(httpServletRequest, request); return ResponseEntity.ok(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.SUCCESS_PASSWORD_CHANGED))); } @Override - public ResponseEntity> initiatePasswordReset(InitiatePasswordResetReq request) { + public ResponseEntity> initiatePasswordReset(HttpServletRequest httpServletRequest,InitiatePasswordResetReq request) { log.info("Initiating password reset for email: {}", request.getEmail()); - String resetToken = userService.initiatePasswordReset(request); + + /** This code is responsible for "Initiating Password Reset Request" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(httpServletRequest).actionType(UserActionLogsEnum.UPDATE) + .actionContext(UserActionContextEnum.INITIATE_PASSWORD_RESET_REQUEST).build()); + + userService.initiatePasswordReset(request); log.info("Password reset token generated for email: {}", request.getEmail()); - return ResponseEntity.ok(new Response<>(resetToken, Status.SUCCESS, Translator.toLocale(GepafinConstant.RESET_PASSWORD_INITIATED))); + return ResponseEntity.ok(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.RESET_PASSWORD_INITIATED))); } @Override - public ResponseEntity> resetPassword(ResetPasswordReq request) { + public ResponseEntity> resetPassword(HttpServletRequest httpServletRequest,ResetPasswordReq request) { log.info("Resetting password for username: {}", request.getEmail()); + + /** This code is responsible for "Resest user password" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(httpServletRequest).actionType(UserActionLogsEnum.UPDATE) + .actionContext(UserActionContextEnum.RESET_USER_PASSWORD).build()); + Boolean success = userService.resetPassword(request); if (success) { log.info("Password reset successfully for username: {}", request.getEmail()); 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 c209f76a..cc2ee820 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 @@ -1875,5 +1875,15 @@ - + + select + setval('gepafin_schema.system_email_template_id_seq', (select + max(id)+1 + from gepafin_schema.system_email_template), false) + + + + diff --git a/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql b/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql new file mode 100644 index 00000000..facb4fdf --- /dev/null +++ b/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql @@ -0,0 +1,65 @@ +INSERT INTO gepafin_schema.system_email_template +(template_name, "type", html_content, subject, "json", "system", is_deleted, created_date, updated_date,email_scenario) +VALUES +( + 'Password Reset Link Email (Italian)', + 'PASSWORD_RESET', + ' + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+

Richiesta di Reimpostazione Password

+
+

Gentile {{user_name}},

+

Hai richiesto di reimpostare la tua password. Puoi farlo cliccando sul link sottostante:

+
+ {{reset_password_link}} +
+

Cordiali saluti,

+

{{email_signature}}

+
+ + Reimposta la Password +
+
', + 'Richiesta di Reimpostazione Password', + NULL, + true, + false, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + 'PASSWORD_RESET_REQUEST'); diff --git a/src/main/resources/db/dump/insert_system_email_template_for_user_28_11_2024.sql b/src/main/resources/db/dump/insert_system_email_template_for_user_28_11_2024.sql new file mode 100644 index 00000000..cfe54849 --- /dev/null +++ b/src/main/resources/db/dump/insert_system_email_template_for_user_28_11_2024.sql @@ -0,0 +1,29 @@ +INSERT INTO gepafin_schema.system_email_template +(template_name, "type", html_content, subject, "json", "system", is_deleted, created_date, updated_date, email_scenario) +VALUES +( + 'Welcome Email for New User', + 'USER_ONBOARDING', + ' + +
+

Benvenuto!

+

Ciao {{user_name}},

+

Siamo lieti di averti con noi. Di seguito trovi alcune informazioni utili:

+
    +
  • Nome Utente: {{user_email}}
  • +
+

Per accedere, utilizza il tuo indirizzo email registrato. Se hai bisogno di supporto, non esitare a contattarci.

+

Distinti saluti,

+

{{email_signature}}

+
+ + ', + 'Welcome - {{user_name}}', + NULL, + true, + false, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + 'USER_CREATION' +); From 034d17d1fb689a27b74d1c398609e56da31bcda0 Mon Sep 17 00:00:00 2001 From: nisha Date: Tue, 24 Dec 2024 18:20:53 +0530 Subject: [PATCH 02/16] Amendment Scheduler updates --- .../dao/ApplicationEvaluationDao.java | 1 + .../entities/ApplicationEvaluationEntity.java | 3 + .../ApplicationAmendmentScheduler.java | 75 ++++++++++++++++--- .../db/changelog/db.changelog-1.0.0.xml | 5 ++ 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index 50fdb16d..aad85864 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -1775,6 +1775,7 @@ public class ApplicationEvaluationDao { String statusType = application.getStatus(); if (application.getStatus().equals(ApplicationStatusTypeEnum.APPROVED.getValue()) || application.getStatus().equals(ApplicationStatusTypeEnum.REJECTED.getValue())) { existingEntity.setStatus(ApplicationEvaluationStatusTypeEnum.CLOSE.getValue()); + existingEntity.setClosingDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); assignedApplicationsEntity.setStatus(AssignedApplicationEnum.CLOSE.getValue()); } entity = applicationEvaluationRepository.save(existingEntity); diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java index 45a8c842..15e3d3d7 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java @@ -62,4 +62,7 @@ public class ApplicationEvaluationEntity extends BaseEntity{ @Column(name = "STOP_DATE_TIME") private LocalDateTime stopDateTime; + @Column(name = "CLOSING_DATE") + private LocalDateTime closingDate; + } diff --git a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java index 56cb3663..b9d63acd 100644 --- a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java +++ b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java @@ -6,24 +6,24 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import net.gepafin.tendermanagement.entities.ApplicationEntity; +import net.gepafin.tendermanagement.entities.AssignedApplicationsEntity; +import net.gepafin.tendermanagement.enums.*; +import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; +import net.gepafin.tendermanagement.repositories.ApplicationRepository; +import net.gepafin.tendermanagement.repositories.AssignedApplicationsRepository; +import net.gepafin.tendermanagement.service.ApplicationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; + import jakarta.servlet.http.HttpServletRequest; import net.gepafin.tendermanagement.dao.ApplicationAmendmentRequestDao; -import net.gepafin.tendermanagement.dao.EmailNotificationDao; import net.gepafin.tendermanagement.entities.ApplicationAmendmentRequestEntity; import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity; -import net.gepafin.tendermanagement.enums.ApplicationAmendmentRequestEnum; -import net.gepafin.tendermanagement.enums.UserActionContextEnum; -import net.gepafin.tendermanagement.enums.UserActionLogsEnum; -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.ApplicationAmendmentRequestRepository; @@ -40,8 +40,6 @@ public class ApplicationAmendmentScheduler { @Autowired private ApplicationAmendmentRequestRepository applicationAmendmentRepository; - @Autowired - private EmailNotificationDao emailNotificationDao; @Autowired private ApplicationAmendmentRequestDao applicationAmendmentRequestDao; @@ -49,6 +47,18 @@ public class ApplicationAmendmentScheduler { @Autowired private LoggingUtil loggingUtil; + @Autowired + private AssignedApplicationsRepository assignedApplicationsRepository; + + @Autowired + private ApplicationService applicationService; + + @Autowired + private ApplicationRepository applicationRepository; + + @Autowired + private ApplicationEvaluationRepository applicationEvaluationRepository; + private static final Logger log = LoggerFactory.getLogger(ApplicationAmendmentScheduler.class); @Scheduled(cron = "0 0 1 * * ?") @@ -79,14 +89,14 @@ public class ApplicationAmendmentScheduler { amendmentRequests.forEach(request -> { try { ApplicationAmendmentRequestEntity oldAmendmentRequestEntity = Utils.getClonedEntityForData(request); - request.setStatus(ApplicationAmendmentRequestEnum.CLOSE.getValue()); + request.setStatus(ApplicationAmendmentRequestEnum.EXPIRED.getValue()); request = applicationAmendmentRepository.save(request); /** This code is responsible for adding a version history log for the "Update Application Amendment" operation. **/ loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(httpServletRequest).actionType(VersionActionTypeEnum.UPDATE).oldData(oldAmendmentRequestEntity).newData(request).build()); // emailNotificationDao.sendApplicationFailureNotificationEmail(request); - log.info("Updated status to CLOSED for ApplicationAmendmentRequest with ID: {}", request.getId()); + log.info("Updated status to EXPIRED for ApplicationAmendmentRequest with ID: {}", request.getId()); } catch (Exception e) { log.error("Error expiring ApplicationAmendmentRequest with ID {}: {}", request.getId(), e.getMessage(), e); @@ -104,6 +114,16 @@ public class ApplicationAmendmentScheduler { evaluationsWithoutActiveAmendmentList.forEach(evaluation -> { try { applicationAmendmentRequestDao.calculateEndDateAndSuspensionDays(evaluation); + + updateEvaluationStatus(evaluation); + + // Update AssignedApplicationsEntity status + updateAssignedApplicationStatus(evaluation.getAssignedApplicationsEntity()); + + // Update ApplicationEntity status + updateApplicationStatus(evaluation.getAssignedApplicationsEntity().getApplication()); + + log.info("Updated EndDate and suspension days for ApplicationEvaluation with ID: {}", evaluation.getId()); } catch (Exception e) { @@ -119,4 +139,35 @@ public class ApplicationAmendmentScheduler { } + public void updateAssignedApplicationStatus(AssignedApplicationsEntity assignedApplicationsEntity){ + AssignedApplicationsEntity oldAssignedApplicationEntity = Utils.getClonedEntityForData(assignedApplicationsEntity); + assignedApplicationsEntity.setStatus(AssignedApplicationEnum.OPEN.getValue()); + assignedApplicationsRepository.save(assignedApplicationsEntity); + log.info("Updated status to OPEN for Assigned Application with ID: {}", assignedApplicationsEntity.getId()); + + /** This code is responsible for adding a version history log for the "Update Assigned Application status" operation. **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(httpServletRequest).actionType(VersionActionTypeEnum.UPDATE).oldData(oldAssignedApplicationEntity).newData(assignedApplicationsEntity).build()); + + } + + public void updateApplicationStatus(ApplicationEntity applicationEntity){ + ApplicationEntity oldApplicationEntity = Utils.getClonedEntityForData(applicationEntity); + applicationEntity.setStatus(ApplicationStatusTypeEnum.EVALUATION.getValue()); + applicationRepository.save(applicationEntity); + log.info("Updated status to EVALUATION for Application with ID: {}",applicationEntity.getId()); + + /** This code is responsible for adding a version history log for the "Update Application Status" operation. **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(httpServletRequest).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationEntity).newData(applicationEntity).build()); + + } + public void updateEvaluationStatus(ApplicationEvaluationEntity applicationEvaluationEntity){ + ApplicationEvaluationEntity oldApplicationEvaluationEntity = Utils.getClonedEntityForData(applicationEvaluationEntity); + applicationEvaluationEntity.setStatus(ApplicationEvaluationStatusTypeEnum.OPEN.getValue()); + applicationEvaluationRepository.save(applicationEvaluationEntity); + log.info("Updated status to OPEN for ApplicationEvaluation with ID: {}", applicationEvaluationEntity.getId()); + + /** This code is responsible for adding a version history log for the "Update Application Status" operation. **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(httpServletRequest).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationEvaluationEntity).newData(applicationEvaluationEntity).build()); + + } } 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 6eee497f..90ab3ca8 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 @@ -2064,5 +2064,10 @@ + + + + + From ab1afaf90aab375fdb9d31380813581160db80ff Mon Sep 17 00:00:00 2001 From: rajesh Date: Thu, 2 Jan 2025 17:36:05 +0530 Subject: [PATCH 03/16] Updated code --- .../gepafin/tendermanagement/dao/UserDao.java | 20 ++++++----- .../repositories/UserRepository.java | 34 +++++++++++-------- ...template_for_reset_password_28_11_2024.sql | 7 +--- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java index 6622ca95..1c06ccaf 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java @@ -222,21 +222,23 @@ public class UserDao { } } } + private void validateDuplicateEmail(String email, String hubUuid, String roleType) { - Boolean existsForNonBeneficiaries = userRepository.existsByEmailIgnoreCaseForNonBeneficiaries( - email, hubUuid, RoleStatusEnum.ROLE_BENEFICIARY.getValue()); + String beneficiaryRoleType = RoleStatusEnum.ROLE_BENEFICIARY.getValue(); - Boolean beneficiaryExistsInHub = userRepository.existsByEmailIgnoreCaseForBeneficiaries( - email, hubUuid, RoleStatusEnum.ROLE_BENEFICIARY.getValue()); + if (beneficiaryRoleType.equals(roleType)) { + Boolean beneficiaryExistsInHub = userRepository.existsByEmailIgnoreCaseForBeneficiaries( + email, hubUuid, beneficiaryRoleType); - if (Boolean.TRUE.equals(RoleStatusEnum.ROLE_BENEFICIARY.getValue().equals(roleType))) { - if (beneficiaryExistsInHub) { + if (Boolean.TRUE.equals(beneficiaryExistsInHub)) { throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.EMAIL_ALREADY_EXISTS)); } - } - else { - if (existsForNonBeneficiaries) { + } else { + Boolean existsForNonBeneficiaries = userRepository.existsByEmailIgnoreCaseForNonBeneficiaries( + email, hubUuid, beneficiaryRoleType); + + if (Boolean.TRUE.equals(existsForNonBeneficiaries)) { throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.EMAIL_ALREADY_EXISTS)); } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java index ae41b9b1..ab00d3e0 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java @@ -33,24 +33,28 @@ public interface UserRepository extends JpaRepository { Optional findByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); - Boolean existsByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); +// Boolean existsByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); - @Query("SELECT COUNT(u) > 0 " + - "FROM UserEntity u " + - "WHERE u.email = :email " + - "AND u.hub.uniqueUuid = :hubUuid " + - "AND u.roleEntity.roleType <> :beneficiaryRoleType") - Boolean existsByEmailIgnoreCaseForNonBeneficiaries(@Param("email") String email, - @Param("hubUuid") String hubUuid, - @Param("beneficiaryRoleType") String beneficiaryRoleType); + @Query("SELECT COUNT(u) > 0 " + + "FROM UserEntity u " + + "WHERE LOWER(u.email) = LOWER(:email) " + + "AND u.hub.uniqueUuid = :hubUuid " + + "AND u.roleEntity.roleType <> :beneficiaryRoleType") + Boolean existsByEmailIgnoreCaseForNonBeneficiaries(@Param("email") String email, + @Param("hubUuid") String hubUuid, + @Param("beneficiaryRoleType") String beneficiaryRoleType); - @Query("SELECT CASE WHEN COUNT(u) > 0 THEN true ELSE false END " + - "FROM UserEntity u " + - "WHERE LOWER(u.email) = LOWER(:email) AND u.hub.uniqueUuid = :hubUuid " + - "AND u.roleEntity.roleType = :beneficiaryRoleType") - Boolean existsByEmailIgnoreCaseForBeneficiaries(String email, String hubUuid, String beneficiaryRoleType); + @Query("SELECT COUNT(u) > 0 " + + "FROM UserEntity u " + + "WHERE LOWER(u.email) = LOWER(:email) " + + "AND u.hub.uniqueUuid = :hubUuid " + + "AND u.roleEntity.roleType = :beneficiaryRoleType") + Boolean existsByEmailIgnoreCaseForBeneficiaries(@Param("email") String email, + @Param("hubUuid") String hubUuid, + @Param("beneficiaryRoleType") String beneficiaryRoleType); - // existsByBebooleanneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); + + boolean existsByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); List findByRoleEntity_RoleTypeAndHubId(String roleType, Long hubId); diff --git a/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql b/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql index facb4fdf..927345b0 100644 --- a/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql +++ b/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql @@ -20,12 +20,7 @@ VALUES

Gentile {{user_name}},

-

Hai richiesto di reimpostare la tua password. Puoi farlo cliccando sul link sottostante:

- - - - - {{reset_password_link}} +

Hai richiesto di reimpostare la tua password.

From 3a7ca2b2224b083ffa30956a60fb7b030d00626c Mon Sep 17 00:00:00 2001 From: rajesh Date: Thu, 2 Jan 2025 19:22:58 +0530 Subject: [PATCH 04/16] Updated code --- .../tendermanagement/dao/CompanyDao.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java b/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java index 48ec017b..6c5f0856 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java @@ -189,15 +189,15 @@ public class CompanyDao { setIfUpdated(companyEntity::getCountry, companyEntity::setCountry, companyRequest.getCountry()); setIfUpdated(companyEntity::getNumberOfEmployees, companyEntity::setNumberOfEmployees, companyRequest.getNumberOfEmployees()); setIfUpdated(companyEntity::getAnnualRevenue, companyEntity::setAnnualRevenue, companyRequest.getAnnualRevenue()); - - if(StringUtils.isNotBlank(companyRequest.getVatNumber())) { - CompanyEntity existingCompany = companyRepository.findByVatNumberAndHubId(companyRequest.getVatNumber(), userEntity.getHub().getId()); - if(existingCompany!=null){ - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.VATNUMBER_ALREADY_EXISTS)); - } - companyEntity.setVatNumber(companyRequest.getVatNumber()); - - } +// +// if(StringUtils.isNotBlank(companyRequest.getVatNumber())) { +// CompanyEntity existingCompany = companyRepository.findByVatNumberAndHubId(companyRequest.getVatNumber(), userEntity.getHub().getId()); +// if(existingCompany!=null){ +// throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.VATNUMBER_ALREADY_EXISTS)); +// } +// companyEntity.setVatNumber(companyRequest.getVatNumber()); +// +// } companyRepository.save(companyEntity); /** This code is responsible for adding a version history log for the "Update company" operation. **/ From 0445a371333cbb0128173f2300bcbd0f094893fb Mon Sep 17 00:00:00 2001 From: nisha Date: Fri, 3 Jan 2025 13:15:07 +0530 Subject: [PATCH 05/16] Updated form field data --- .../db/changelog/db.changelog-1.0.0.xml | 7 ++ .../update_form_field_data_03_01_2025.sql | 101 ++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/main/resources/db/dump/update_form_field_data_03_01_2025.sql 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 cf3c3987..1a25de27 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 @@ -2139,5 +2139,12 @@ + + + TRUNCATE TABLE FORM_FIELD RESTART IDENTITY; + + + diff --git a/src/main/resources/db/dump/update_form_field_data_03_01_2025.sql b/src/main/resources/db/dump/update_form_field_data_03_01_2025.sql new file mode 100644 index 00000000..c3c08ca7 --- /dev/null +++ b/src/main/resources/db/dump/update_form_field_data_03_01_2025.sql @@ -0,0 +1,101 @@ +INSERT INTO FORM_FIELD (SORT_ORDER, NAME, LABEL, DESCRIPTION, SETTINGS, VALIDATORS, CREATED_DATE, UPDATED_DATE) +VALUES +(1, 'textinput', 'Testo Breve', 'Per risposte concise (nomi, titoli, brevi descrizioni)', + '[{"name": "label", "value": "Testo Breve"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": false, "minLength": null, "maxLength": null, "pattern": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(2, 'textarea', 'Testo Lungo', 'Campo di testo esteso per paragrafi, descrizioni, proposte', + '[{"name": "label", "value": "Testo Lungo"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": false, "minLength": null, "maxLength": null, "pattern": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(3, 'wysiwyg', 'Campo di Testo Formattato', 'Editor avanzato per testo con formattazione', + '[{"name": "label", "value": "Testo Formattato"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": false, "minLength": null, "maxLength": null, "pattern": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(4, 'numberinput', 'Campo Numerico', 'Per l''inserimento di valori numerici (quantità, importi, percentuali)', + '[{"name": "label", "value": "Numero"}, {"name": "placeholder", "value": "0"}, {"name": "step", "value": "0"},{"name": "isRequestedAmount","value": false}]', + '{"isRequired": false, "min": null, "max": null, "pattern": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(5, 'radio', 'Scelta Singola', 'Gruppo di opzioni per selezione singola', + '[{"name": "label", "value": "Scelta Singola"}, {"name": "options", "value": []}]', + '{"isRequired": false, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(6, 'select', 'Menu a Tendina', 'Selezione da opzioni predefinite', + '[{"name": "label", "value": "Menu a Tendina"}, {"name": "options", "value": []}]', + '{"isRequired": false, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(7, 'checkboxes', 'Scelta Multipla', 'Gruppo di opzioni per selezione singola o multipla', + '[{"name": "label", "value": "Scelta Multipla"}, {"name": "options", "value": []}]', + '{"isRequired": false, "min": null, "max": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(8, 'switch', 'Casella di Spunta', 'Per selezioni binarie, accettazioni, conferme', + '[{"name": "label", "value": "Casella di Spunta"}]', + '{"isRequired": false}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(9, 'datepicker', 'Data', 'Selezione di data', + '[{"name": "label", "value": "Data"}]', + '{"isRequired": false}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(10, 'fileupload', 'Caricamento File', 'Per l''upload di documenti o immagini', + '[{"name": "label", "value": "Caricamento File"}, {"name": "mime", "value": []}]', + '{"isRequired": false, "maxSize": 100000}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(11, 'textinput', 'Campo Partita IVA', 'Specifico per l''inserimento del numero di Partita IVA', + '[{"name": "label", "value": "Partita IVA"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": true, "custom": "isPIVA"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(12, 'textinput', 'Campo Codice Fiscale','Specifico per l''inserimento del Codice Fiscale italiano per persone fisiche e giuridiche', + '[{"name": "label", "value": "Codice Fiscale"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": true, "custom": "isCodiceFiscale"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(13, 'textinput', 'Campo CAP','Per l''inserimento del Codice di Avviamento Postale', + '[{"name": "label", "value": "CAP"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": true, "custom": "isCAP"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(14, 'textinput', 'Campo IBAN', 'Per l''inserimento del codice IBAN', + '[{"name": "label", "value": "IBAN"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": true, "custom": "isIBAN"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(15, 'textinput', 'Campo Email', 'Per l''inserimento di indirizzi email standard (non PEC)', + '[{"name": "label", "value": "Campo Email"}, {"name": "placeholder", "value": "nome@esempio.it"}]', + '{"isRequired": false, "custom": "isEmail"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(16, 'textinput', 'Campo PEC', 'Specifico per l''inserimento di un indirizzo di Posta Elettronica Certificata', + '[{"name": "label", "value": "Campo PEC"}, {"name": "placeholder", "value": "nome@pec.it"}]', + '{"isRequired": false, "custom": "isEmailPEC"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(17, 'textinput', 'Campo URL', 'Per l''inserimento di indirizzi web', + '[{"name": "label", "value": "Indirizzo URL"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": false, "custom": "isUrl"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(18, 'textinput', 'Marca da bollo', 'Per inserire codice di marca da bollo', + '[{"name": "label", "value": "Marca da bollo"}, {"name": "placeholder", "value": "Numero identificativo"}]', + '{"isRequired": false, "custom": "isMarcaDaBollo"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(19, 'paragraph', 'Paragrafo', 'Semplice testo formattato', + '[{"name": "text", "value": ""}]', + '{}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(20, 'table', 'Tabella', 'Tabella', + '[{"name": "label", "value": "Tabella"}, {"name": "table_columns", "value": []}]', + '{}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); From dd5cf63a16d2a2647f55d0d0f109bb9e415f58ed Mon Sep 17 00:00:00 2001 From: piyushkag Date: Fri, 3 Jan 2025 15:12:44 +0530 Subject: [PATCH 06/16] Websocket config fix. --- .../gepafin/tendermanagement/TendermanagementApplication.java | 3 ++- .../net/gepafin/tendermanagement/config/WebSocketConfig.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java b/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java index c220b176..6cdd9142 100644 --- a/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java +++ b/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java @@ -23,7 +23,8 @@ public class TendermanagementApplication { @Override public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**").allowedOrigins("http://localhost:3000").allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD").allowCredentials(true); + registry.addMapping("/**").allowedOrigins("http://localhost:3000", "http://127.0.0.1:5500", "https://bandi-staging.memento.credit", "https://bandi.gepafin.it") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD").allowCredentials(true); } } diff --git a/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java b/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java index 1b90a882..0212f720 100644 --- a/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java +++ b/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java @@ -33,7 +33,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/wss").setAllowedOrigins("http://localhost:3000", "http://127.0.0.1:5500/", "https://bandi-staging.memento.credit/**", "https://bandi.gepafin.it/**") + registry.addEndpoint("/wss").setAllowedOrigins("http://localhost:3000", "http://127.0.0.1:5500", "https://bandi-staging.memento.credit", "https://bandi.gepafin.it") .withSockJS(); } } From c687849c933898647988687bccd5c1cf52f06d55 Mon Sep 17 00:00:00 2001 From: rajesh Date: Fri, 3 Jan 2025 19:17:11 +0530 Subject: [PATCH 07/16] Done ticket GEPAFINBE-129 --- .../dao/ApplicationAmendmentRequestDao.java | 2 +- .../dao/ApplicationEvaluationDao.java | 7 ++ .../tendermanagement/dao/DashboardDao.java | 79 ++++++++++++++++++- .../entities/ApplicationEvaluationEntity.java | 3 + .../enums/UserActionContextEnum.java | 1 + .../ApplicationWidgetResponseBean.java | 23 ++++++ .../ApplicationEvaluationRepository.java | 25 ++++++ .../repositories/ApplicationRepository.java | 16 ++++ .../service/DashboardService.java | 3 +- .../service/impl/DashboardServiceImpl.java | 7 ++ .../web/rest/api/DashboardApi.java | 14 +++- .../rest/api/impl/DashboardApiController.java | 10 +++ .../db/changelog/db.changelog-1.0.0.xml | 6 +- 13 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 src/main/java/net/gepafin/tendermanagement/model/response/ApplicationWidgetResponseBean.java diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java index 2937da2c..e478061e 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java @@ -298,7 +298,7 @@ public class ApplicationAmendmentRequestDao { ApplicationAmendmentRequestEntity applicationAmendment = saveApplicationAmendmentRequestEntity(applicationAmendmentRequestEntity, null, VersionActionTypeEnum.INSERT); String evaluationStatusType = applicationEvaluationEntity.getStatus(); if (Boolean.FALSE.equals(evaluationStatusType.equals((ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue())))){ - applicationEvaluationEntity.setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue()); +// applicationEvaluationEntity.setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue()); //Set Status applicationEvaluationEntity.setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue()); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index aad85864..29fa8814 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -24,7 +24,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; +import java.time.Duration; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -1778,6 +1780,11 @@ public class ApplicationEvaluationDao { existingEntity.setClosingDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); assignedApplicationsEntity.setStatus(AssignedApplicationEnum.CLOSE.getValue()); } + if (existingEntity.getStartDate() != null && existingEntity.getClosingDate() != null) { + long activeDays = ChronoUnit.DAYS.between(existingEntity.getStartDate(), existingEntity.getClosingDate()); + activeDays -= existingEntity.getSuspendedDays() != null ? existingEntity.getSuspendedDays() : 0; + existingEntity.setActiveDays(activeDays); + } entity = applicationEvaluationRepository.save(existingEntity); assignedApplicationsRepository.save(assignedApplicationsEntity); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/DashboardDao.java b/src/main/java/net/gepafin/tendermanagement/dao/DashboardDao.java index 0afc8fc8..a9caba7b 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/DashboardDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/DashboardDao.java @@ -6,18 +6,20 @@ import net.gepafin.tendermanagement.entities.UserWithCompanyEntity; import net.gepafin.tendermanagement.enums.CallStatusEnum; import net.gepafin.tendermanagement.enums.RoleStatusEnum; import net.gepafin.tendermanagement.enums.UserStatusEnum; +import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.Widget1; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; -import net.gepafin.tendermanagement.repositories.ApplicationRepository; -import net.gepafin.tendermanagement.repositories.CallRepository; -import net.gepafin.tendermanagement.repositories.CompanyRepository; -import net.gepafin.tendermanagement.repositories.UserRepository; +import net.gepafin.tendermanagement.repositories.*; import net.gepafin.tendermanagement.service.CompanyService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; @Component public class DashboardDao { @@ -37,6 +39,9 @@ public class DashboardDao { @Autowired private CompanyService companyService; + @Autowired + private ApplicationEvaluationRepository applicationEvaluationRepository; + public SuperAdminWidgetResponseBean getDashboardWidget(UserEntity requestedUserEntity) { SuperAdminWidgetResponseBean widgetResponseBean = new SuperAdminWidgetResponseBean(); widgetResponseBean.setWidget1(createWidget1(requestedUserEntity)); @@ -121,4 +126,70 @@ public class DashboardDao { } return beneficiaryWidgetResponseBean; } + + public ApplicationWidgetResponseBean getApplicationDetails(UserEntity userEntity) { + ApplicationWidgetResponseBean applicationWidgetResponseBean = initializeResponseBean(); + + Long hubId = userEntity.getHub().getId(); + + setApplicationCounts(applicationWidgetResponseBean, hubId); + calculateEvaluationAverageTime(applicationWidgetResponseBean, hubId); + + return applicationWidgetResponseBean; + } + + private ApplicationWidgetResponseBean initializeResponseBean() { + return ApplicationWidgetResponseBean.builder() + .numberOfApplication(0L) + .numberOfAssignedApplication(0L) + .numberOfAcceptedApplication(0L) + .numberOfApplicationInAmendmentState(0L) + .numberOfDueApplication(0L) + .evaluationAverageTime(BigDecimal.ZERO) + .build(); + } + + private void setApplicationCounts(ApplicationWidgetResponseBean responseBean, Long hubId) { + Long activeApplications = applicationRepository.countApplicationsByHubId(hubId); + if (activeApplications != null) { + responseBean.setNumberOfApplication(activeApplications); + } + + Long assignedApplications = applicationRepository.countAssignedApplicationsByHubId(hubId); + if (assignedApplications != null) { + responseBean.setNumberOfAssignedApplication(assignedApplications); + } + + Long approvedApplications = applicationRepository.countApprovedApplicationsByHubId(hubId); + if (approvedApplications != null) { + responseBean.setNumberOfAcceptedApplication(approvedApplications); + } + + Long soccorsoApplications = applicationRepository.countSoccorsoApplicationsByHubId(hubId); + if (soccorsoApplications != null) { + responseBean.setNumberOfApplicationInAmendmentState(soccorsoApplications); + } + } + + private void calculateEvaluationAverageTime(ApplicationWidgetResponseBean responseBean, Long hubId) { + List applicationIds = applicationRepository.findApplicationIdsByHubId(hubId); + + if (Boolean.FALSE.equals(applicationIds.isEmpty())) { + BigDecimal averageTime = applicationEvaluationRepository.findAverageEvaluationTimeByApplicationIds(applicationIds); + responseBean.setEvaluationAverageTime(averageTime != null ? averageTime : BigDecimal.ZERO); + } + LocalDate twoDaysLater = LocalDate.now().plusDays(2); + + Long dueApplications = applicationEvaluationRepository.countDueApplicationsBetween( + applicationIds, + LocalDate.now(), + twoDaysLater + ); + + if (dueApplications != null) { + responseBean.setNumberOfDueApplication(dueApplications); + } + } + + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java index 15e3d3d7..6ab7b72c 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java @@ -65,4 +65,7 @@ public class ApplicationEvaluationEntity extends BaseEntity{ @Column(name = "CLOSING_DATE") private LocalDateTime closingDate; + @Column(name = "ACTIVE_DAYS") + private Long activeDays; + } diff --git a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java index 20f917fe..4a992e31 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java @@ -134,6 +134,7 @@ public enum UserActionContextEnum { /** Dashboard action context **/ GET_DASHBOARD_WIDGET_FOR_SUPER_ADMIN("GET_DASHBOARD_WIDGET_FOR_SUPER_ADMIN"), GET_DASHBOARD_WIDGET_FOR_BENEFICIARY("GET_DASHBOARD_WIDGET_FOR_BENEFICIARY"), + GET_APPLICATION_DETAILS("GET_APPLICATION_DETAILS"), /** Evaluation criteria action context **/ GET_EVALUATION_CRITERIA("GET_EVALUATION_CRITERIA"), diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationWidgetResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationWidgetResponseBean.java new file mode 100644 index 00000000..c119121b --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationWidgetResponseBean.java @@ -0,0 +1,23 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; + +@Builder +@Data +public class ApplicationWidgetResponseBean { + + private Long numberOfApplication; + + private Long numberOfAssignedApplication; + + private Long numberOfAcceptedApplication; + + private Long numberOfApplicationInAmendmentState; + + private Long numberOfDueApplication; + + private BigDecimal evaluationAverageTime; +} diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java index f9a7b0ac..daaae5a8 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java @@ -7,6 +7,8 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.math.BigDecimal; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -31,4 +33,27 @@ public interface ApplicationEvaluationRepository extends JpaRepository findAllByIsDeletedFalseAndEndDateBefore(@Param("currentDate") LocalDateTime currentDate); +// @Query("SELECT AVG(DATEDIFF(DAY, e.startDate, e.endDate)) FROM ApplicationEvaluationEntity e WHERE e.applicationId IN :applicationIds AND e.startDate IS NOT NULL AND e.endDate IS NOT NULL AND e.isDeleted = false ") + @Query(""" + SELECT AVG(e.activeDays) + FROM ApplicationEvaluationEntity e + WHERE e.applicationId IN :applicationIds + AND e.activeDays IS NOT NULL + AND e.isDeleted = false + """) + BigDecimal findAverageEvaluationTimeByApplicationIds(@Param("applicationIds") List applicationIds); + @Query(""" + SELECT COUNT(e) + FROM ApplicationEvaluationEntity e + WHERE e.applicationId IN :applicationIds + AND FUNCTION('DATE', e.endDate) BETWEEN :startDate AND :endDate + AND e.isDeleted = false +""") + Long countDueApplicationsBetween( + @Param("applicationIds") List applicationIds, + @Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate + ); + + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java index db645f95..3b35e921 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java @@ -44,4 +44,20 @@ public interface ApplicationRepository extends JpaRepository findApplicationIdsByHubId(@Param("hubId") Long hubId); + + } diff --git a/src/main/java/net/gepafin/tendermanagement/service/DashboardService.java b/src/main/java/net/gepafin/tendermanagement/service/DashboardService.java index 6328ca6e..0e34c1b6 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/DashboardService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/DashboardService.java @@ -1,6 +1,7 @@ package net.gepafin.tendermanagement.service; import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; @@ -9,5 +10,5 @@ public interface DashboardService { public SuperAdminWidgetResponseBean getDashboardWidgetForSuperAdmin(HttpServletRequest request); public BeneficiaryWidgetResponseBean getDashboardWidgetForBeneficiary(HttpServletRequest request, Long companyId); - + public ApplicationWidgetResponseBean getApplicationDetails(HttpServletRequest request); } diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/DashboardServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/DashboardServiceImpl.java index 1a6cd6fd..e578a3dc 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/DashboardServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/DashboardServiceImpl.java @@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpServletRequest; import net.gepafin.tendermanagement.dao.DashboardDao; import net.gepafin.tendermanagement.entities.CompanyEntity; import net.gepafin.tendermanagement.entities.UserEntity; +import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; import net.gepafin.tendermanagement.service.DashboardService; @@ -32,4 +33,10 @@ public class DashboardServiceImpl implements DashboardService { CompanyEntity company = validator.validateUserWithCompany(request, companyId); return dashboardDao.getDashboardWidgetForBeneficiary(userEntity, company); } + + @Override + public ApplicationWidgetResponseBean getApplicationDetails(HttpServletRequest request) { + UserEntity userEntity=validator.validateUser(request); + return dashboardDao.getApplicationDetails(userEntity); + } } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java index 753473f2..e8e99aff 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java @@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; import net.gepafin.tendermanagement.model.util.Response; @@ -46,7 +47,18 @@ public interface DashboardApi { produces = { "application/json" }) ResponseEntity> getDashboardWidgetForBeneficiary(HttpServletRequest request, @Parameter(description = "The company id", required = true) @PathVariable(value = "companyId", required = true) Long companyId); - + @Operation(summary = "Api to get Application details", + responses = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE) })), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @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) })) }) + @GetMapping(value = "/application", + produces = { "application/json" }) + ResponseEntity> getApplicationDetails(HttpServletRequest request); } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/DashboardApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/DashboardApiController.java index 7ff7be61..ed1f3d68 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/DashboardApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/DashboardApiController.java @@ -6,6 +6,7 @@ import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.enums.UserActionContextEnum; import net.gepafin.tendermanagement.enums.UserActionLogsEnum; import net.gepafin.tendermanagement.model.request.UserActionRequest; +import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; import net.gepafin.tendermanagement.model.util.Response; @@ -49,5 +50,14 @@ public class DashboardApiController implements DashboardApi { return ResponseEntity.status(HttpStatus.CREATED) .body(new Response<>(widgetResponseBean, Status.SUCCESS, Translator.toLocale(GepafinConstant.DASHBOARD_WIDGET_FETCHED_SUCCESSFULLY))); } + @Override + public ResponseEntity> getApplicationDetails(HttpServletRequest request) { + /** This code is responsible for creating user action logs for the "Get complete application page" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.VIEW).actionContext(UserActionContextEnum.GET_APPLICATION_DETAILS).build()); + + ApplicationWidgetResponseBean widgetResponseBean= dashboardService.getApplicationDetails(request); + return ResponseEntity.status(HttpStatus.CREATED) + .body(new Response<>(widgetResponseBean, Status.SUCCESS, Translator.toLocale(GepafinConstant.DASHBOARD_WIDGET_FETCHED_SUCCESSFULLY))); + } } 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 90ab3ca8..f4ad96e7 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 @@ -2069,5 +2069,9 @@ - + + + + + From a63e1bd640e390885fbda00345dbab8777d850f5 Mon Sep 17 00:00:00 2001 From: rajesh Date: Mon, 6 Jan 2025 12:53:01 +0530 Subject: [PATCH 08/16] Done ticket GEPAFINBE-135 --- .../net/gepafin/tendermanagement/dao/ApplicationDao.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index 0b0edf19..e281615f 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -295,7 +295,12 @@ public class ApplicationDao { log.info("Deleting application with ID: {}", id); ApplicationEntity applicationEntity= validateApplication(id); - + if (Boolean.FALSE.equals(ApplicationStatusTypeEnum.DRAFT.getValue().equals(applicationEntity.getStatus()))) { + throw new CustomValidationException( + Status.VALIDATION_ERROR, + Translator.toLocale(GepafinConstant.APPLICATION_NOT_IN_DRAFT_STATUS) + ); + } ApplicationEntity oldApplicationDataEntity = Utils.getClonedEntityForData(applicationEntity); validator.validateUserWithCompany(request, applicationEntity.getCompanyId()); From de8196c4617c726a9e9017690555dbb87223859b Mon Sep 17 00:00:00 2001 From: rajesh Date: Mon, 6 Jan 2025 13:11:02 +0530 Subject: [PATCH 09/16] Updated code --- .../net/gepafin/tendermanagement/web/rest/api/DashboardApi.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java index e8e99aff..31ae1be3 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java @@ -59,6 +59,7 @@ public interface DashboardApi { @ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) })) }) @GetMapping(value = "/application", produces = { "application/json" }) + @PreAuthorize("hasRole('ROLE_SUPER_ADMIN') || hasRole('ROLE_INSTRUCTOR_MANAGER')") ResponseEntity> getApplicationDetails(HttpServletRequest request); } From 5e293c43e68d457a161eb00abd26c47ca74d7db2 Mon Sep 17 00:00:00 2001 From: piyushkag Date: Mon, 6 Jan 2025 15:57:30 +0530 Subject: [PATCH 10/16] Added prod config for NDG. --- src/main/resources/application-production.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-production.properties b/src/main/resources/application-production.properties index df295088..07dd31ff 100644 --- a/src/main/resources/application-production.properties +++ b/src/main/resources/application-production.properties @@ -24,7 +24,7 @@ default.hub.uuid=p4lk3bcx1RStqTaIVVbXs # TEST DEPLOY Configuration #Login to Odessa, Appointment Creation, Upload document Configuration -appointment.base.url=https://demo.galileonetwork.it/gateway/rest +appointment.base.url=https://prd.galileonetwork.it/gateway/rest appointment.portal.user=UtenzaAPIPortal@621 appointment.portal.password=u13nzaAP1P0rtal appointment.portal.source=GEPAFINPORTAL From de85d6ba90d535b3ab326f4c10761c71bf825393 Mon Sep 17 00:00:00 2001 From: rajesh Date: Mon, 6 Jan 2025 17:20:42 +0530 Subject: [PATCH 11/16] Done ticket GEPAFINBE-138 --- .../tendermanagement/dao/VatCheckDao.java | 51 +++++++++++++------ .../gepafin/tendermanagement/util/Utils.java | 19 +++++++ 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java b/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java index 85af1388..4d74fb71 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java @@ -68,25 +68,46 @@ public class VatCheckDao { if (response.getStatusCode() == HttpStatus.OK && response.hasBody()) { log.info("Successfully checked vat number"); Map responseMap = response.getBody(); - if (responseMap != null && responseMap.containsKey("data")) { - responseBody = (Map) responseMap.get("data"); - responseBody.remove("timestamp_creation"); - responseBody.remove("timestamp_last_update"); - responseBody.remove("data_iscrizione"); - responseBody.remove("id"); - Map data = new LinkedHashMap<>(); - data.put("data", responseBody); - vatCheckResponseBean.setValid(true); - vatCheckResponseBean.setMessage(Translator.toLocale(GepafinConstant.VALID_VATNUMBER_MSG)); - vatCheckResponseBean.setVatCheckResponse(data); - } + processValidResponse(responseMap, vatCheckResponseBean); } } catch (FeignException ex) { - log.error("Exception occurred while checking vat number: {0}", ex); - Utils.callException(ex.status(), ex); - } + if (ex.status() == 406) { + try { + Map errorResponse = Utils.parseErrorResponse(ex.contentUTF8()); + processValidResponse(errorResponse, vatCheckResponseBean); + } catch (Exception parseEx) { + log.error("Failed to parse 406 error response: {0}", parseEx); + } + } else { + log.error("Exception occurred while checking vat number: {0}", ex); + Utils.callException(ex.status(), ex); + } + } return vatCheckResponseBean; } + public static void processValidResponse(Map responseMap, VatCheckResponseBean vatCheckResponseBean) { + if (responseMap != null && responseMap.containsKey("data")) { + Map responseBody = (Map) responseMap.get("data"); + + if (responseBody != null) { + responseBody.remove("timestamp_creation"); + responseBody.remove("timestamp_last_update"); + responseBody.remove("data_iscrizione"); + responseBody.remove("id"); + + Map data = new LinkedHashMap<>(); + data.put("data", responseBody); + + vatCheckResponseBean.setValid(true); + vatCheckResponseBean.setMessage(Translator.toLocale(GepafinConstant.VALID_VATNUMBER_MSG)); + vatCheckResponseBean.setVatCheckResponse(data); + } else { + vatCheckResponseBean.setMessage(Translator.toLocale(GepafinConstant.INVALID_VATNUMBER)); + } + } else { + vatCheckResponseBean.setMessage(Translator.toLocale(GepafinConstant.INVALID_VATNUMBER)); + } + } public VatCheckResponseBean checkVatNumber(String vatNumber) { try { diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index fb15759f..40f95e3b 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -24,6 +24,7 @@ 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 org.apache.commons.collections4.MapUtils; import org.slf4j.Logger; @@ -697,4 +698,22 @@ public class Utils { public static String createChannelForUserAndCompany(Long userId, Long companyId) { return GepafinConstant.COMMON_SINGLE_CHANNEL_PREFIX + userId + GepafinConstant.COMPANY_PREFIX + companyId; } + + public static Map parseErrorResponse(String responseBody) { + if (StringUtils.isBlank(responseBody)) { + return defaultErrorResponse(); + } + try { + return mapper.readValue(responseBody, Map.class); + } catch (Exception e) { + log.error("Failed to parse error response: {}", e.getMessage(), e); + return defaultErrorResponse(); + } + } + + private static Map defaultErrorResponse() { + return Collections.singletonMap("message", Translator.toLocale(GepafinConstant.INVALID_VATNUMBER)); + } + + } \ No newline at end of file From 184cda9fd24c3203cd85bf6b0973832fb99a73a1 Mon Sep 17 00:00:00 2001 From: rajesh Date: Tue, 7 Jan 2025 12:25:28 +0530 Subject: [PATCH 12/16] removed code smells --- .../ApplicationAmendmentScheduler.java | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java index 045dfd45..6a4c7b81 100644 --- a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java +++ b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java @@ -6,29 +6,23 @@ import net.gepafin.tendermanagement.dao.NotificationDao; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import net.gepafin.tendermanagement.entities.ApplicationEntity; import net.gepafin.tendermanagement.entities.AssignedApplicationsEntity; -import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; import net.gepafin.tendermanagement.repositories.ApplicationRepository; import net.gepafin.tendermanagement.repositories.AssignedApplicationsRepository; -import net.gepafin.tendermanagement.service.ApplicationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; - -import jakarta.servlet.http.HttpServletRequest; -import net.gepafin.tendermanagement.dao.ApplicationAmendmentRequestDao; import net.gepafin.tendermanagement.entities.ApplicationAmendmentRequestEntity; -import net.gepafin.tendermanagement.entities.ApplicationEntity; import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity; -import net.gepafin.tendermanagement.entities.AssignedApplicationsEntity; import net.gepafin.tendermanagement.enums.ApplicationAmendmentRequestEnum; import net.gepafin.tendermanagement.enums.ApplicationEvaluationStatusTypeEnum; import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum; @@ -40,25 +34,9 @@ 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.ApplicationAmendmentRequestRepository; -import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; -import net.gepafin.tendermanagement.repositories.ApplicationRepository; -import net.gepafin.tendermanagement.repositories.AssignedApplicationsRepository; -import net.gepafin.tendermanagement.service.ApplicationService; import net.gepafin.tendermanagement.util.DateTimeUtil; import net.gepafin.tendermanagement.util.LoggingUtil; import net.gepafin.tendermanagement.util.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; @Component public class ApplicationAmendmentScheduler { @@ -82,9 +60,6 @@ public class ApplicationAmendmentScheduler { @Autowired private AssignedApplicationsRepository assignedApplicationsRepository; - @Autowired - private ApplicationService applicationService; - @Autowired private ApplicationRepository applicationRepository; From af5d31b9fe46f31d8607899f18ab37df9027a242 Mon Sep 17 00:00:00 2001 From: piyushkag Date: Tue, 7 Jan 2025 13:15:25 +0530 Subject: [PATCH 13/16] Done ticket GEPAFINBE-133 User action api response. --- pom.xml | 6 + .../config/JacksonConfig.java | 20 +++ .../constants/GepafinConstant.java | 5 + .../repositories/UserActionsRepository.java | 2 +- .../tendermanagement/util/LoggingUtil.java | 40 ++++- .../util/UserActionAspect.java | 148 ++++++++++++++++++ .../gepafin/tendermanagement/util/Utils.java | 3 + 7 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/gepafin/tendermanagement/config/JacksonConfig.java create mode 100644 src/main/java/net/gepafin/tendermanagement/util/UserActionAspect.java diff --git a/pom.xml b/pom.xml index c463be91..02b5fb51 100644 --- a/pom.xml +++ b/pom.xml @@ -245,6 +245,12 @@ reactor-netty + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.15.2 + + diff --git a/src/main/java/net/gepafin/tendermanagement/config/JacksonConfig.java b/src/main/java/net/gepafin/tendermanagement/config/JacksonConfig.java new file mode 100644 index 00000000..51ad3191 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/config/JacksonConfig.java @@ -0,0 +1,20 @@ +package net.gepafin.tendermanagement.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class JacksonConfig { + + @Bean + public ObjectMapper objectMapper() { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + return mapper; + } +} + diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 42853112..5869fc81 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -355,5 +355,10 @@ public class GepafinConstant { public static final String NOTIFICATION_DELETED_SUCCESSFULLY="notification.deleted.successfully"; public static final String NOTIFICATION_UPDATED_SUCCESSFULLY="notification.updated.successfully"; public static final String USER_WITH_COMPANY_NOT_FOUND = "user.with.company.not.found"; + + //action log response + public static final String STATUS_CODE_STRING = "statusCode"; + public static final String GET_STATUS_CODE_STRING = "status"; + public static final String MESSAGE_STRING = "message"; } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/UserActionsRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/UserActionsRepository.java index f0163c47..804fdb9b 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/UserActionsRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/UserActionsRepository.java @@ -6,5 +6,5 @@ import org.springframework.stereotype.Repository; @Repository public interface UserActionsRepository extends JpaRepository { - UserActionEntity findUserActionById(Long id); + UserActionEntity findUserActionByIdAndIsDeletedFalse(Long id); } diff --git a/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java b/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java index e2ab4521..90b7a052 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java +++ b/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java @@ -41,6 +41,8 @@ public class LoggingUtil { @Autowired private TokenProvider tokenProvider; + private static final ThreadLocal userActionIdHolder = new ThreadLocal<>(); + public UserActionEntity logUserAction(UserActionRequest userActionRequest) { UserActionEntity userAction = new UserActionEntity(); try { @@ -83,12 +85,22 @@ public class LoggingUtil { userAction.setResponse(response); userActionsRepository.save(userAction); userActionRequest.getRequest().setAttribute(GepafinConstant.USER_ACTION_ID, userAction.getId()); + userActionIdHolder.set(userAction.getId()); } catch (Exception e) { log.error("Error logging user action: {}", e.getMessage(), e); } return userAction; } + public Long getUserActionId() { + return userActionIdHolder.get(); + } + + public void clearUserActionId() { + userActionIdHolder.remove(); + log.info("UserActionId cleared from ThreadLocal"); + } + private String normalizeUrl(String url) { url = url.replaceAll("(? getVersionHistoryLogById(Long id) { return versionHistoryRepository.findVersionHistoryByUserActionId(id); } + + public void updateUserActionWithError(Long userActionId, String errorDetails) { + if (userActionId == null) { + return; + } + + UserActionEntity userAction = userActionsRepository.findUserActionByIdAndIsDeletedFalse(userActionId); + if (userAction != null) { + userAction.setResponse(errorDetails); + userActionsRepository.save(userAction); + } + } + + public void updateUserActionWithResponse(Long userActionId, String response) { + if (userActionId == null) { + return; + } + + UserActionEntity userAction = userActionsRepository.findUserActionByIdAndIsDeletedFalse(userActionId); + if (userAction != null) { + userAction.setResponse(response); + userActionsRepository.save(userAction); + } + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/util/UserActionAspect.java b/src/main/java/net/gepafin/tendermanagement/util/UserActionAspect.java new file mode 100644 index 00000000..0064bb82 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/util/UserActionAspect.java @@ -0,0 +1,148 @@ +package net.gepafin.tendermanagement.util; + +import com.amazonaws.services.alexaforbusiness.model.UnauthorizedException; +import jakarta.persistence.EntityNotFoundException; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; +import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException; +import net.gepafin.tendermanagement.web.rest.api.errors.Status; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.server.ResponseStatusException; + +import java.lang.reflect.InvocationTargetException; +import java.nio.file.AccessDeniedException; +import java.util.LinkedHashMap; +import java.util.Map; + +@Aspect +@Component +@Slf4j +public class UserActionAspect { + + @Autowired + private LoggingUtil loggingUtil; + + @Around("execution(public * net.gepafin.tendermanagement.web.rest.api.impl..*(..))") + public Object logApiResponse(ProceedingJoinPoint joinPoint) throws Throwable { + + Object result; + + HttpServletRequest request = getRequestFromContext(); + try { + Long userActionId = getUserActionIdFromRequest(request); + + if (userActionId != null) { + request.setAttribute(GepafinConstant.USER_ACTION_ID, userActionId); + log.info("Stored userActionId in RequestContext: {}", userActionId); + } else { + userActionId = loggingUtil.getUserActionId(); + if (userActionId != null) { + request.setAttribute(GepafinConstant.USER_ACTION_ID, userActionId); + } + } + + result = joinPoint.proceed(); + + if (result instanceof ResponseEntity) { + Long storedUserActionId = (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID); + handleSuccessResponse((ResponseEntity) result, storedUserActionId == null ? userActionId : storedUserActionId); + } + } catch (Exception ex) { + log.error("Exception occurred: ", ex); + handleError(ex, getUserActionIdFromRequest(request)); + throw ex; + } finally { + loggingUtil.clearUserActionId(); + } + + return result; + } + + private void handleSuccessResponse(ResponseEntity responseEntity, Long userActionId) { + + if (userActionId != null) { + Map responseWithUserAction = new LinkedHashMap<>(); + responseWithUserAction.put(GepafinConstant.STATUS_CODE_STRING, responseEntity.getStatusCode().value()); + + // Log and update user action + loggingUtil.updateUserActionWithResponse(userActionId, Utils.convertMapIntoJsonString(responseWithUserAction)); + log.info("Updated userActionId with response: {}", userActionId); + } + } + + private void handleError(Throwable ex, Long userActionId) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { + + HttpStatus status = getStatusCodeFromException(ex); + log.info("Status Code received from exception : {}", status); + String errorMessage = ex.getMessage(); + + Map errorResponse = new LinkedHashMap<>(); + errorResponse.put(GepafinConstant.STATUS_CODE_STRING, status.value()); + errorResponse.put(GepafinConstant.GET_STATUS_CODE_STRING, status); + errorResponse.put(GepafinConstant.MESSAGE_STRING, errorMessage); + + if (userActionId != null) { + String errorDetails = Utils.convertMapIntoJsonString(errorResponse); + loggingUtil.updateUserActionWithError(userActionId, errorDetails); + log.info("Updated userActionId with error details: {}", userActionId); + } + } + + private HttpServletRequest getRequestFromContext() { + + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + return attributes != null ? attributes.getRequest() : null; + } + + private Long getUserActionIdFromRequest(HttpServletRequest request) { + + if (request != null) { + Object userActionIdAttr = request.getAttribute(GepafinConstant.USER_ACTION_ID); + return userActionIdAttr != null ? Long.valueOf(userActionIdAttr.toString()) : null; + } + return null; + } + + private HttpStatus getStatusCodeFromException(Throwable ex) { + + if (ex instanceof ResourceNotFoundException) { + return HttpStatus.NOT_FOUND; + } + + if (ex instanceof ResponseStatusException responseStatusException) { + return (HttpStatus) responseStatusException.getStatusCode(); + } + + if (ex instanceof CustomValidationException) { + return HttpStatus.BAD_REQUEST; + } + + if (ex instanceof EntityNotFoundException) { + return HttpStatus.NOT_FOUND; + } + if (ex instanceof IllegalArgumentException || ex instanceof MissingServletRequestParameterException || ex instanceof MethodArgumentNotValidException) { + return HttpStatus.BAD_REQUEST; + } + if (ex instanceof AccessDeniedException) { + return HttpStatus.FORBIDDEN; + } + if (ex instanceof UnauthorizedException) { + return HttpStatus.UNAUTHORIZED; + } + + return HttpStatus.INTERNAL_SERVER_ERROR; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index 40f95e3b..0770bce5 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -154,6 +154,9 @@ public class Utils { public static String convertMapIntoJsonString(Map map) { try { ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.enable(SerializationFeature.INDENT_OUTPUT); if (MapUtils.isNotEmpty(map)) { return mapper.writeValueAsString(map); } From b4d8ad45f2b79b20fc56a3fedb665c792def2430 Mon Sep 17 00:00:00 2001 From: piyushkag Date: Tue, 7 Jan 2025 14:10:53 +0530 Subject: [PATCH 14/16] Added config for frame error on FE. --- .../net/gepafin/tendermanagement/config/SecurityConfig.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java index 8c11eac2..61f84825 100644 --- a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java +++ b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java @@ -12,6 +12,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -97,7 +98,9 @@ public class SecurityConfig { } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.csrf(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth -> auth + http.csrf(AbstractHttpConfigurer::disable).headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin) + .contentSecurityPolicy(csp -> csp.policyDirectives("frame-ancestors 'self' https://bandi-staging.memento.credit, https://bandi.gepafin.it"))) + .authorizeHttpRequests(auth -> auth // Allow public access to the login endpoints .requestMatchers("/v1/user/login").permitAll() // JWT-based login .requestMatchers("/v1/user").permitAll() // User registration From c53b7e59d3e380087dd5df27da5722619c1f930d Mon Sep 17 00:00:00 2001 From: piyushkag Date: Tue, 7 Jan 2025 14:14:54 +0530 Subject: [PATCH 15/16] Updated cnfig code. --- .../net/gepafin/tendermanagement/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java index 61f84825..462d5cc9 100644 --- a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java +++ b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java @@ -99,7 +99,7 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable).headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin) - .contentSecurityPolicy(csp -> csp.policyDirectives("frame-ancestors 'self' https://bandi-staging.memento.credit, https://bandi.gepafin.it"))) + .contentSecurityPolicy(csp -> csp.policyDirectives("frame-ancestors 'self' https://bandi-staging.memento.credit https://bandi.gepafin.it"))) .authorizeHttpRequests(auth -> auth // Allow public access to the login endpoints .requestMatchers("/v1/user/login").permitAll() // JWT-based login From 887100ed74eb05b4dceb2234b15ad3581c8718be Mon Sep 17 00:00:00 2001 From: rajesh Date: Tue, 7 Jan 2025 18:17:23 +0530 Subject: [PATCH 16/16] Fixed issue during user creation from SPID --- src/main/java/net/gepafin/tendermanagement/dao/UserDao.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java index 1c06ccaf..0ffa0b42 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java @@ -190,6 +190,8 @@ public class UserDao { userReq.setHubUuid(userEntity.getHub().getUniqueUuid()); }else { samlSuccessHandler.validateToken(tempToken, userReq.getCodiceFiscale(), userReq.getHubUuid()); + RoleEntity roleEntity = roleDao.getRoleByType(RoleStatusEnum.ROLE_BENEFICIARY); + userReq.setRoleId(roleEntity.getId()); } if (Boolean.FALSE.equals(Utils.isValidEmail(userReq.getEmail()))) {