From f32ad3c4a9655dd0b20e9a4d0088aed5472d8bac Mon Sep 17 00:00:00 2001 From: nisha Date: Fri, 20 Sep 2024 17:17:04 +0530 Subject: [PATCH] Done ticket GEPAFINBE-18 --- pom.xml | 6 ++ .../TendermanagementApplication.java | 2 + .../constants/GepafinConstant.java | 21 +++++ .../gepafin/tendermanagement/dao/FormDao.java | 38 +++++++-- .../tendermanagement/dao/VatCheckDao.java | 74 +++++++++++++++++ .../model/response/VatNumberResponseBean.java | 15 ++++ .../service/feignClient/VatCheckService.java | 23 ++++++ .../tendermanagement/util/FieldValidator.java | 82 +++++++++++++++++++ src/main/resources/application-dev.properties | 4 +- src/main/resources/application.properties | 5 +- src/main/resources/message_en.properties | 13 +++ src/main/resources/message_it.properties | 13 ++- 12 files changed, 287 insertions(+), 9 deletions(-) create mode 100644 src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/response/VatNumberResponseBean.java create mode 100644 src/main/java/net/gepafin/tendermanagement/service/feignClient/VatCheckService.java diff --git a/pom.xml b/pom.xml index 713de99a..55765564 100644 --- a/pom.xml +++ b/pom.xml @@ -140,6 +140,12 @@ 0.23.0 + + org.springframework.cloud + spring-cloud-starter-openfeign + 4.1.3 + + diff --git a/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java b/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java index f9a97be8..7ec98464 100644 --- a/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java +++ b/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java @@ -2,12 +2,14 @@ package net.gepafin.tendermanagement; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @EnableScheduling +@EnableFeignClients @SpringBootApplication public class TendermanagementApplication { diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index acf2e124..159d21c1 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -133,5 +133,26 @@ public class GepafinConstant { public static final String INITAL_AND_FINAL_FORM_CANNOT_NULL="initial.and.final.form.cannot.null"; public static final String APPLICATION_FORM_NOT_FOUND="application.form.not.found"; public static final String UPDATING_FORM_VALUE_IMPACT_ON_FLOW="updating.form.value.impact.on.flow"; + public static final String AUTHORIZATION = "Authorization"; + public static final String CHECK_VATNUMBER_V2_NEW_URL = "https://imprese.openapi.it/advance"; + public static final String VATNUMBER_V2 = "https://imprese.openapi.it/advance"; + public static final String VALIDATION_FIELD_CUSTOM="validation.field.custom"; + public static final String VALIDATION_CODICE_FISCALE = "validation.codice.fiscale"; + public static final String VALIDATION_CAP = "validation.cap"; + public static final String VALIDATION_IBAN = "validation.iban"; + public static final String VALIDATION_EMAIL = "validation.email"; + public static final String VALIDATION_EMAIL_PEC = "validation.email.pec"; + public static final String VALIDATION_URL = "validation.url"; + public static final String VALIDATION_MARCA_DA_BOLLO = "validation.marca.da.bollo"; + public static final String VALIDATION_PIVA = "validation.piva"; + public static final String VALIDATION_VALID_PIVA="valid.vat.number"; + public static final String IS_MARCA_DA_BOLLO="isMarcaDaBollo"; + public static final String IS_URL="isUrl"; + public static final String IS_EMAIL_PEC="isEmailPEC"; + public static final String IS_EMAIL="isEmail"; + public static final String IS_IBAN="isIBAN"; + public static final String IS_CAP="isCAP"; + public static final String IS_CODICE_FISCALE="isCodiceFiscale"; + public static final String IS_PIVA="isPIVA"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/FormDao.java b/src/main/java/net/gepafin/tendermanagement/dao/FormDao.java index ab882abd..820c261d 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/FormDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/FormDao.java @@ -6,6 +6,7 @@ import net.gepafin.tendermanagement.entities.*; import net.gepafin.tendermanagement.model.request.*; import net.gepafin.tendermanagement.model.response.ContentResponseBean; import net.gepafin.tendermanagement.model.response.FormResponseBean; +import net.gepafin.tendermanagement.model.response.VatNumberResponseBean; import net.gepafin.tendermanagement.repositories.*; import net.gepafin.tendermanagement.service.CallService; import net.gepafin.tendermanagement.util.DateTimeUtil; @@ -16,6 +17,7 @@ import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundExceptio import net.gepafin.tendermanagement.web.rest.api.errors.Status; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import java.text.MessageFormat; import java.time.LocalDateTime; @@ -45,6 +47,9 @@ public class FormDao { @Autowired private FlowEdgesRepository flowEdgesRepository; + @Autowired + private VatCheckDao vatCheckDao; + @Autowired private CallRepository callRepository; @@ -180,7 +185,7 @@ public class FormDao { } public void validateFormField(List applicationFormFieldRequestList, ApplicationEntity applicationEntity, FormEntity formEntity) { - Map formFieldMap = new LinkedHashMap(); + Map formFieldMap = new LinkedHashMap(); for(ApplicationFormFieldRequestBean applicationFormFieldRequestBean:applicationFormFieldRequestList) { formFieldMap.put(applicationFormFieldRequestBean.getFieldId(),applicationFormFieldRequestBean.getFieldValue()); } @@ -191,20 +196,25 @@ public class FormDao { FieldValidator validator = FieldValidator.create(); formResponseBean.getContent().forEach(contentResponseBean -> { String fieldId = contentResponseBean.getId(); - String value = formFieldMap.get(fieldId); + String value = String.valueOf(formFieldMap.get(fieldId)); + if(value == null && isApplicationFormExist) { return; } - FieldValidatorBean fieldValidatorBean = Utils.convertSourceObjectToDestinationObject(contentResponseBean, FieldValidatorBean.class); + FieldValidatorBean fieldValidatorBean = Utils.convertSourceObjectToDestinationObject(contentResponseBean.getValidators(), FieldValidatorBean.class); validator .notNull(value, fieldId) + .isRequired(value,fieldValidatorBean.getIsRequired(),fieldId) .minLength(value, fieldValidatorBean.getMinLength(), fieldId) // Only applies if minLength is not null .maxLength(value, fieldValidatorBean.getMaxLength(), fieldId) // Only applies if maxLength is not null - .matchesPattern(value, fieldValidatorBean.getPattern(), fieldId); // Only applies if pattern is present + .matchesPattern(value, fieldValidatorBean.getPattern(), fieldId) // Only applies if pattern is present + .validateCustom(value, fieldValidatorBean.getCustom(), fieldId); // Add the custom validation here + if (fieldValidatorBean.getCustom() != null && fieldValidatorBean.getCustom().equals(GepafinConstant.IS_PIVA)) { + String error = validateVatNumber(value, fieldValidatorBean.getCustom(), fieldId); + validator.addError(error); + } }); - validator.validate(); - } private Boolean getApplicationFormExist(ApplicationFormEntity applicationFormEntity) { @@ -213,4 +223,20 @@ public class FormDao { } return false; } + + public String validateVatNumber(String value,String customRule,String fieldId){ + String error=null; + if (value.matches("^\\d{1,11}$")) { + Map customData=null; + try { + Map vatCheckResponse = vatCheckDao.checkVatNumberApi(value); + if (Boolean.FALSE.equals(CollectionUtils.isEmpty(vatCheckResponse))) { + customData = vatCheckResponse; + } + } catch (Exception e) { + error=(MessageFormat.format(Translator.toLocale(GepafinConstant.VALIDATION_VALID_PIVA), fieldId)); + } + } + return error; + } } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java b/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java new file mode 100644 index 00000000..4d1d0f58 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java @@ -0,0 +1,74 @@ +package net.gepafin.tendermanagement.dao; + +import feign.FeignException; +import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.service.feignClient.VatCheckService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +@Component +public class VatCheckDao { + + @Autowired + private VatCheckService vatCheckService; + + @Value("${vatCheckNewToken}") + public String vatCheckNewToken; + + @Value("${isVatCheckGloballyDisabled}") + public String isVatCheckGloballyDisabled; + + public final Logger log = LoggerFactory.getLogger(VatCheckDao.class); + + + + public Map checkVatNumberApi(String vatNumber) { + if (Boolean.TRUE.equals(Boolean.parseBoolean(isVatCheckGloballyDisabled))) { + return new HashMap<>(); + } + Map responseBody = new HashMap<>(); + try { + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set(GepafinConstant.AUTHORIZATION, "Bearer " + vatCheckNewToken); + headers.add(org.apache.http.HttpHeaders.USER_AGENT, + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"); + + URI baseUrl = URI.create(GepafinConstant.CHECK_VATNUMBER_V2_NEW_URL); + ResponseEntity> response = vatCheckService.checkVatNumber(baseUrl,vatNumber, headers); + + 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); + return data; + } + } + } catch (FeignException ex) { + log.error("Exception occurred while checking vat number: {0}", ex); + throw ex; + } + return responseBody; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/VatNumberResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/VatNumberResponseBean.java new file mode 100644 index 00000000..f98e60c5 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/VatNumberResponseBean.java @@ -0,0 +1,15 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; + +@Data +public class VatNumberResponseBean { + + private Boolean valid; + + private String validation; + + private String companyName; + + private String errorMessage; +} diff --git a/src/main/java/net/gepafin/tendermanagement/service/feignClient/VatCheckService.java b/src/main/java/net/gepafin/tendermanagement/service/feignClient/VatCheckService.java new file mode 100644 index 00000000..6665690d --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/service/feignClient/VatCheckService.java @@ -0,0 +1,23 @@ +package net.gepafin.tendermanagement.service.feignClient; + +import net.gepafin.tendermanagement.constants.GepafinConstant; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; + +import java.net.URI; +import java.util.Map; + +@FeignClient(value = "vat-check-service", url = GepafinConstant.VATNUMBER_V2) +public interface VatCheckService { + + + @GetMapping("/{vatNumber}") + ResponseEntity> checkVatNumber(URI baseUrl, + @PathVariable("vatNumber") String vatNumber, + @RequestHeader HttpHeaders headers + ); +} diff --git a/src/main/java/net/gepafin/tendermanagement/util/FieldValidator.java b/src/main/java/net/gepafin/tendermanagement/util/FieldValidator.java index 18a1f042..71c76f1a 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/FieldValidator.java +++ b/src/main/java/net/gepafin/tendermanagement/util/FieldValidator.java @@ -3,12 +3,17 @@ package net.gepafin.tendermanagement.util; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.dao.FormDao; +import net.gepafin.tendermanagement.dao.VatCheckDao; import net.gepafin.tendermanagement.web.rest.api.errors.Status; import net.gepafin.tendermanagement.web.rest.api.errors.ValidationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; public class FieldValidator { @@ -18,6 +23,10 @@ public class FieldValidator { return new FieldValidator(); } + @Autowired + private VatCheckDao vatCheckDao; + + public FieldValidator notNull(Object object, String fieldName) { if (Objects.isNull(object)) { errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.FIELD_NOT_NULL), fieldName)); @@ -65,4 +74,77 @@ public class FieldValidator { public static boolean isNullOrZero(Long value) { return value == null || value == 0L; } + + public FieldValidator validateCustom(String value, String customRule, String fieldId) { + if (customRule == null || value == null) { + return this; // No custom rule to validate + } + + switch (customRule) { + + case GepafinConstant.IS_PIVA: + // VAT number: max 11 digits, can start with 0 + if (!value.matches("^\\d{1,11}$")) { + errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.VALIDATION_PIVA), fieldId, customRule)); + } + break; + case GepafinConstant.IS_CODICE_FISCALE: + // 16 characters: 6 letters, 2 digits, 1 letter, 2 digits, 1 letter, 3 digits, 1 letter + if (!value.matches("^[A-Z]{6}[0-9]{2}[A-Z]{1}[0-9]{2}[A-Z]{1}[0-9]{3}[A-Z]{1}$")) { + errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.VALIDATION_CODICE_FISCALE), fieldId, customRule)); + } + break; + + case GepafinConstant.IS_CAP: + // 5 digits (can start with 0) + if (!value.matches("^[0-9]{5}$")) { + errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.VALIDATION_CAP), fieldId, customRule)); + } + break; + + case GepafinConstant.IS_IBAN: + // IBAN must be 27 characters + if (value.length() != 27) { + errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.VALIDATION_IBAN), fieldId, customRule)); + } + break; + + case GepafinConstant.IS_EMAIL: + case GepafinConstant.IS_EMAIL_PEC: + // Email validation (using a simple regex for email) + if (!value.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$")) { + errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.VALIDATION_EMAIL), fieldId, customRule)); + } + break; + + case GepafinConstant.IS_URL: + // URL validation (simple regex for URL) + if (!value.matches("^(https?|ftp)://[^\s/$.?#].[^\s]*$")) { + errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.VALIDATION_URL), fieldId, customRule)); + } + break; + + case GepafinConstant.IS_MARCA_DA_BOLLO: + // Length must be 14 digits (can start with 0) + if (!value.matches("^[0-9]{14}$")) { + errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.VALIDATION_MARCA_DA_BOLLO), fieldId, customRule)); + } + break; + + default: + // If the custom rule is unknown, just log or add an error (optional) + errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.VALIDATION_FIELD_CUSTOM), fieldId, customRule)); + break; + } + + return this; + } + public FieldValidator isRequired(String value,Boolean isRequired, String fieldName) { + if (Boolean.TRUE.equals(isRequired)) { // Only check if isRequired is true + if (Objects.isNull(value) || value.isEmpty()) { // Check if value is null or empty + errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.FIELD_NOT_NULL), fieldName)); + } + } + return this; + } } diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 72135766..19588213 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -5,4 +5,6 @@ spring.datasource.password=vs1pAc9vu07mMcdx93j6WiBS spring.datasource.driver-class-name=org.postgresql.Driver # JPA Configuration -spring.h2.console.enabled=true \ No newline at end of file +spring.h2.console.enabled=true + +isVatCheckGloballyDisabled = false diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 07fcf32f..a1893c1e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,7 +4,7 @@ spring.application.name=tendermanagement spring.servlet.multipart.max-file-size=50MB spring.servlet.multipart.max-request-size=50MB -spring.profiles.active=testing +spring.profiles.active=local # JPA Configuration @@ -39,3 +39,6 @@ security.authentication.jwt.token-validity-in-seconds=86400 spring.main.allow-circular-references=true +isVatCheckGloballyDisabled = true +vatCheckNewToken: 66026bd891a51044e90e08c4 + diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index b0449f1e..0f2025d5 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -168,4 +168,17 @@ call.not.published=Call is not published. application.form.not.found=Application form not found. updating.form.value.impact.on.flow=Updating this value of form {0} can make impact on flow. +validation.field.custom=The value for field {0} does not meet the custom validation rule. + +validation.codice.fiscale=The field {0} must be a valid Codice Fiscale with exactly 16 characters: 6 letters, 2 digits, 1 letter, 2 digits, 1 letter, 3 digits, and 1 letter. +validation.cap=The field {0} must be a valid CAP with exactly 5 digits. +validation.iban=The field {0} must be a valid IBAN with exactly 27 characters. +validation.email=The field {0} must be a valid email address. +validation.email.pec=The field {0} must be a valid PEC email address. +validation.url=The field {0} must be a valid URL. +validation.marca.da.bollo=The field {0} must be a valid Marca Da Bollo with exactly 14 digits. +validation.piva=The VAT number for {0} must be up to 11 digits. +valid.vat.number=The VAT number is not valid for field {0}. + + diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index 7b464832..95e4a1d9 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -162,4 +162,15 @@ action.required=Campo azione obbligatorio. call.not.published=La chiamata non č stata pubblicata. application.form.not.found=Modulo di domanda non trovato. -updating.form.value.impact.on.flow=L'aggiornamento di questo valore del modulo {0} puņ avere un impatto sul flusso. \ No newline at end of file +updating.form.value.impact.on.flow=L'aggiornamento di questo valore del modulo {0} puņ avere un impatto sul flusso. +validation.field.custom=Il valore per il campo {0} non soddisfa la regola di convalida personalizzata. + +validation.codice.fiscale=Il campo {0} deve essere un Codice Fiscale valido con esattamente 16 caratteri: 6 lettere, 2 cifre, 1 lettera, 2 cifre, 1 lettera, 3 cifre e 1 lettera. +validation.cap=Il campo {0} deve essere un CAP valido con esattamente 5 cifre. +validation.iban=Il campo {0} deve essere un IBAN valido con esattamente 27 caratteri. +validation.email=Il campo {0} deve essere un indirizzo email valido. +validation.email.pec=Il campo {0} deve essere un indirizzo email PEC valido. +validation.url=Il campo {0} deve essere un URL valido. +validation.marca.da.bollo=Il campo {0} deve essere una Marca Da Bollo valida con esattamente 14 cifre. +validation.piva=Il numero di partita IVA per {0} deve essere lungo fino a 11 cifre. +valid.vat.number=Il numero di partita IVA non č valido per il campo {0}. \ No newline at end of file