Merge pull request #23 from Kitzanos/feature/GEPAFINBE-18

Implemented Custom Field Validation
This commit is contained in:
nishainnogent
2024-09-20 19:41:06 +05:30
committed by GitHub
12 changed files with 290 additions and 11 deletions

View File

@@ -140,6 +140,12 @@
<version>0.23.0</version> <version>0.23.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>4.1.3</version>
</dependency>
</dependencies> </dependencies>

View File

@@ -2,12 +2,14 @@ package net.gepafin.tendermanagement;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@EnableScheduling @EnableScheduling
@EnableFeignClients
@SpringBootApplication @SpringBootApplication
public class TendermanagementApplication { public class TendermanagementApplication {

View File

@@ -133,5 +133,27 @@ public class GepafinConstant {
public static final String INITAL_AND_FINAL_FORM_CANNOT_NULL="initial.and.final.form.cannot.null"; 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 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 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";
public static final String FAILED_RETAIN_FIELD="failed.retain.field"; public static final String FAILED_RETAIN_FIELD="failed.retain.field";
} }

View File

@@ -6,6 +6,7 @@ import net.gepafin.tendermanagement.entities.*;
import net.gepafin.tendermanagement.model.request.*; import net.gepafin.tendermanagement.model.request.*;
import net.gepafin.tendermanagement.model.response.ContentResponseBean; import net.gepafin.tendermanagement.model.response.ContentResponseBean;
import net.gepafin.tendermanagement.model.response.FormResponseBean; import net.gepafin.tendermanagement.model.response.FormResponseBean;
import net.gepafin.tendermanagement.model.response.VatNumberResponseBean;
import net.gepafin.tendermanagement.repositories.*; import net.gepafin.tendermanagement.repositories.*;
import net.gepafin.tendermanagement.service.CallService; import net.gepafin.tendermanagement.service.CallService;
import net.gepafin.tendermanagement.util.DateTimeUtil; 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 net.gepafin.tendermanagement.web.rest.api.errors.Status;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -45,6 +47,9 @@ public class FormDao {
@Autowired @Autowired
private FlowEdgesRepository flowEdgesRepository; private FlowEdgesRepository flowEdgesRepository;
@Autowired
private VatCheckDao vatCheckDao;
@Autowired @Autowired
private CallRepository callRepository; private CallRepository callRepository;
@@ -190,7 +195,7 @@ public class FormDao {
} }
public void validateFormField(List<ApplicationFormFieldRequestBean> applicationFormFieldRequestList, ApplicationEntity applicationEntity, FormEntity formEntity) { public void validateFormField(List<ApplicationFormFieldRequestBean> applicationFormFieldRequestList, ApplicationEntity applicationEntity, FormEntity formEntity) {
Map<String, String> formFieldMap = new LinkedHashMap<String, String>(); Map<String, Object> formFieldMap = new LinkedHashMap<String, Object>();
for(ApplicationFormFieldRequestBean applicationFormFieldRequestBean:applicationFormFieldRequestList) { for(ApplicationFormFieldRequestBean applicationFormFieldRequestBean:applicationFormFieldRequestList) {
formFieldMap.put(applicationFormFieldRequestBean.getFieldId(),applicationFormFieldRequestBean.getFieldValue()); formFieldMap.put(applicationFormFieldRequestBean.getFieldId(),applicationFormFieldRequestBean.getFieldValue());
} }
@@ -201,20 +206,25 @@ public class FormDao {
FieldValidator validator = FieldValidator.create(); FieldValidator validator = FieldValidator.create();
formResponseBean.getContent().forEach(contentResponseBean -> { formResponseBean.getContent().forEach(contentResponseBean -> {
String fieldId = contentResponseBean.getId(); String fieldId = contentResponseBean.getId();
String value = formFieldMap.get(fieldId); String value = String.valueOf(formFieldMap.get(fieldId));
if(value == null && isApplicationFormExist) { if(value == null && isApplicationFormExist) {
return; return;
} }
FieldValidatorBean fieldValidatorBean = Utils.convertSourceObjectToDestinationObject(contentResponseBean, FieldValidatorBean.class); FieldValidatorBean fieldValidatorBean = Utils.convertSourceObjectToDestinationObject(contentResponseBean.getValidators(), FieldValidatorBean.class);
validator validator
.notNull(value, fieldId) .notNull(value, fieldId)
.isRequired(value,fieldValidatorBean.getIsRequired(),fieldId)
.minLength(value, fieldValidatorBean.getMinLength(), fieldId) // Only applies if minLength is not null .minLength(value, fieldValidatorBean.getMinLength(), fieldId) // Only applies if minLength is not null
.maxLength(value, fieldValidatorBean.getMaxLength(), fieldId) // Only applies if maxLength 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(); validator.validate();
} }
private Boolean getApplicationFormExist(ApplicationFormEntity applicationFormEntity) { private Boolean getApplicationFormExist(ApplicationFormEntity applicationFormEntity) {
@@ -223,4 +233,20 @@ public class FormDao {
} }
return false; return false;
} }
public String validateVatNumber(String value,String customRule,String fieldId){
String error=null;
if (value.matches("^\\d{1,11}$")) {
Map<String, Object> customData=null;
try {
Map<String, Object> 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;
}
} }

View File

@@ -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<String, Object> checkVatNumberApi(String vatNumber) {
if (Boolean.TRUE.equals(Boolean.parseBoolean(isVatCheckGloballyDisabled))) {
return new HashMap<>();
}
Map<String, Object> 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<Map<String, Object>> response = vatCheckService.checkVatNumber(baseUrl,vatNumber, headers);
if (response.getStatusCode() == HttpStatus.OK && response.hasBody()) {
log.info("Successfully checked vat number");
Map<String, Object> responseMap = response.getBody();
if (responseMap != null && responseMap.containsKey("data")) {
responseBody = (Map<String, Object>) responseMap.get("data");
responseBody.remove("timestamp_creation");
responseBody.remove("timestamp_last_update");
responseBody.remove("data_iscrizione");
responseBody.remove("id");
Map<String, Object> 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;
}
}

View File

@@ -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;
}

View File

@@ -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<Map<String, Object>> checkVatNumber(URI baseUrl,
@PathVariable("vatNumber") String vatNumber,
@RequestHeader HttpHeaders headers
);
}

View File

@@ -3,12 +3,17 @@ package net.gepafin.tendermanagement.util;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.constants.GepafinConstant; 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.Status;
import net.gepafin.tendermanagement.web.rest.api.errors.ValidationException; import net.gepafin.tendermanagement.web.rest.api.errors.ValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
public class FieldValidator { public class FieldValidator {
@@ -18,6 +23,10 @@ public class FieldValidator {
return new FieldValidator(); return new FieldValidator();
} }
@Autowired
private VatCheckDao vatCheckDao;
public FieldValidator notNull(Object object, String fieldName) { public FieldValidator notNull(Object object, String fieldName) {
if (Objects.isNull(object)) { if (Objects.isNull(object)) {
errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.FIELD_NOT_NULL), fieldName)); errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.FIELD_NOT_NULL), fieldName));
@@ -65,4 +74,77 @@ public class FieldValidator {
public static boolean isNullOrZero(Long value) { public static boolean isNullOrZero(Long value) {
return value == null || value == 0L; 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;
}
} }

View File

@@ -5,4 +5,6 @@ spring.datasource.password=vs1pAc9vu07mMcdx93j6WiBS
spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.driver-class-name=org.postgresql.Driver
# JPA Configuration # JPA Configuration
spring.h2.console.enabled=true spring.h2.console.enabled=true
isVatCheckGloballyDisabled = false

View File

@@ -39,3 +39,6 @@ security.authentication.jwt.token-validity-in-seconds=86400
spring.main.allow-circular-references=true spring.main.allow-circular-references=true
isVatCheckGloballyDisabled = true
vatCheckNewToken: 66026bd891a51044e90e08c4

View File

@@ -168,5 +168,18 @@ call.not.published=Call is not published.
application.form.not.found=Application form not found. 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. 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}.
failed.retain.field=Failed to retain specific fields. failed.retain.field=Failed to retain specific fields.

View File

@@ -146,7 +146,7 @@ application.not.found=Applicazione non trovata con l'ID fornito.
application.form.field.not.found=Campo del modulo di domanda non trovato. application.form.field.not.found=Campo del modulo di domanda non trovato.
Form.not.matches.to.call.initial.form=L'ID del modulo non corrisponde all'ID del modulo iniziale della chiamata. Form.not.matches.to.call.initial.form=L'ID del modulo non corrisponde all'ID del modulo iniziale della chiamata.
application.already.exists=L'applicazione esiste gi<67> per questa chiamata. application.already.exists=L'applicazione esiste gi<67> per questa chiamata.
application.already.submitted=La domanda è già stata inviata. application.already.submitted=La domanda <EFBFBD> gi<EFBFBD> stata inviata.
#Validation related messages #Validation related messages
validation.field.required=Il campo {0} <20> obbligatorio. validation.field.required=Il campo {0} <20> obbligatorio.
@@ -159,8 +159,19 @@ current.form.incomplete=il modulo corrente non <20> compilato
flow.not.found=Flow not found. flow.not.found=Flow not found.
validation.message=Messaggi di convalida. validation.message=Messaggi di convalida.
action.required=Campo azione obbligatorio. action.required=Campo azione obbligatorio.
call.not.published=La chiamata non è stata pubblicata. call.not.published=La chiamata non <EFBFBD> stata pubblicata.
application.form.not.found=Modulo di domanda non trovato. 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. updating.form.value.impact.on.flow=L'aggiornamento di questo valore del modulo {0} pu<EFBFBD> avere un impatto sul flusso.
failed.retain.field=Impossibile conservare campi specifici. 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 <20> valido per il campo {0}.
failed.retain.field=Impossibile conservare campi specifici.