Skip to content

Commit e9c95db

Browse files
authored
PI-870: validate overall code coverage (#59)
* add validations to request bodies * extend tests * adapt fh catalog client tests * increase dao coverage * remove validators for id fields as they are set by backend * strip error message * move validator to its own package, add comments * replace email annotation with same regex that is used in frontend * fix log * remove default port from package.json * add some module tests for registration API * cleanup * simplify registrationNumber validator * add missing quotes * cleanup * reset wizard api on tests * cleanup
1 parent 7d0d8f2 commit e9c95db

File tree

50 files changed

+1777
-662
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1777
-662
lines changed

backend/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies {
3636
implementation(libs.springBootStarterWebflux)
3737
implementation(libs.springBootStarterDataJpa)
3838
implementation(libs.springBootStarterSecurity)
39+
implementation(libs.springBootStarterValidation)
3940
implementation(libs.hibernateValidator)
4041
implementation(libs.openApi)
4142
implementation(libs.titaniumJsonLd)

backend/src/main/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationRestApi.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import eu.possiblex.portal.application.entity.CreateRegistrationRequestTO;
44
import eu.possiblex.portal.application.entity.RegistrationRequestEntryTO;
5+
import jakarta.validation.Valid;
56
import org.springframework.data.domain.Page;
67
import org.springframework.data.domain.Pageable;
78
import org.springframework.data.web.PageableDefault;
@@ -16,7 +17,7 @@ public interface ParticipantRegistrationRestApi {
1617
* @param request participant registration request
1718
*/
1819
@PostMapping(value = "/request", produces = MediaType.APPLICATION_JSON_VALUE)
19-
void registerParticipant(@RequestBody CreateRegistrationRequestTO request);
20+
void registerParticipant(@Valid @RequestBody CreateRegistrationRequestTO request);
2021

2122
/**
2223
* GET request for retrieving registration requests for the given pagination request.

backend/src/main/java/eu/possiblex/portal/application/configuration/BoundaryExceptionHandler.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,20 @@
55
import eu.possiblex.portal.business.entity.exception.RegistrationRequestConflictException;
66
import eu.possiblex.portal.business.entity.exception.RegistrationRequestProcessingException;
77
import lombok.extern.slf4j.Slf4j;
8+
import org.springframework.http.HttpHeaders;
9+
import org.springframework.http.HttpStatusCode;
810
import org.springframework.http.ResponseEntity;
11+
import org.springframework.lang.NonNull;
12+
import org.springframework.validation.FieldError;
13+
import org.springframework.web.bind.MethodArgumentNotValidException;
914
import org.springframework.web.bind.annotation.ExceptionHandler;
1015
import org.springframework.web.bind.annotation.RestControllerAdvice;
16+
import org.springframework.web.context.request.WebRequest;
1117
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
1218

19+
import java.util.HashMap;
20+
import java.util.Map;
21+
1322
import static org.springframework.http.HttpStatus.*;
1423

1524
/**
@@ -54,6 +63,28 @@ public ResponseEntity<ErrorResponseTO> handleException(ParticipantComplianceExce
5463
UNPROCESSABLE_ENTITY);
5564
}
5665

66+
/**
67+
* Handle Spring validation exceptions.
68+
*/
69+
@Override
70+
public ResponseEntity<Object> handleMethodArgumentNotValid(@NonNull MethodArgumentNotValidException ex,
71+
@NonNull HttpHeaders headers, @NonNull HttpStatusCode status, @NonNull WebRequest request) {
72+
73+
logError(ex);
74+
75+
Map<String, String> errors = new HashMap<>();
76+
ex.getBindingResult().getAllErrors().forEach((error) -> {
77+
String fieldName = ((FieldError) error).getField();
78+
String errorMessage = error.getDefaultMessage();
79+
errors.put(fieldName, errorMessage);
80+
});
81+
StringBuilder message = new StringBuilder();
82+
errors.forEach((key, value) -> message.append(key).append(": ").append(value).append("; "));
83+
84+
return new ResponseEntity<>(new ErrorResponseTO("Request contained errors.", message.toString().strip()),
85+
BAD_REQUEST);
86+
}
87+
5788
/**
5889
* Handle all other exceptions.
5990
*/

backend/src/main/java/eu/possiblex/portal/application/control/ParticipantRegistrationRestApiMapper.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,5 @@ public interface ParticipantRegistrationRestApiMapper {
1919
@Mapping(target = "id", ignore = true)
2020
PxExtendedLegalParticipantCredentialSubject credentialSubjectsToExtendedLegalParticipantCs(
2121
CreateRegistrationRequestTO request);
22-
23-
GxNestedLegalRegistrationNumberCredentialSubject registrationNumberCsToNestedLegalRegistrationNumberCs(
24-
GxLegalRegistrationNumberCredentialSubject request);
2522
}
2623

backend/src/main/java/eu/possiblex/portal/application/entity/CreateRegistrationRequestTO.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalParticipantCredentialSubject;
44
import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalRegistrationNumberCredentialSubject;
55
import eu.possiblex.portal.application.entity.credentials.px.participants.PxParticipantExtensionCredentialSubject;
6+
import jakarta.validation.Valid;
7+
import jakarta.validation.constraints.NotNull;
68
import lombok.AllArgsConstructor;
79
import lombok.Builder;
810
import lombok.Data;
@@ -14,9 +16,15 @@
1416
@AllArgsConstructor
1517
public class CreateRegistrationRequestTO {
1618

19+
@Valid
20+
@NotNull(message = "Participant credential subject is required")
1721
private GxLegalParticipantCredentialSubject participantCs;
1822

23+
@Valid
24+
@NotNull(message = "Registration number credential subject is required")
1925
private GxLegalRegistrationNumberCredentialSubject registrationNumberCs;
2026

27+
@Valid
28+
@NotNull(message = "Participant extension credential subject is required")
2129
private PxParticipantExtensionCredentialSubject participantExtensionCs;
2230
}

backend/src/main/java/eu/possiblex/portal/application/entity/credentials/PojoCredentialSubject.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
@AllArgsConstructor
3636
public abstract class PojoCredentialSubject {
3737
// base fields
38+
// no input validations as this will be set by the backend
3839
private String id;
3940
}
4041

backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/GxVcard.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
2424
import eu.possiblex.portal.application.entity.credentials.serialization.StringDeserializer;
2525
import eu.possiblex.portal.application.entity.credentials.serialization.StringSerializer;
26-
import jakarta.validation.constraints.NotNull;
26+
import jakarta.validation.constraints.NotBlank;
27+
import jakarta.validation.constraints.Pattern;
2728
import lombok.Getter;
2829
import lombok.Setter;
2930
import lombok.ToString;
@@ -38,13 +39,15 @@ public class GxVcard {
3839
@JsonProperty("gx:countryCode")
3940
@JsonSerialize(using = StringSerializer.class)
4041
@JsonDeserialize(using = StringDeserializer.class)
41-
@NotNull
42+
@NotBlank(message = "Country code is needed")
43+
@Pattern(regexp = "^([A-Z]{2}|[A-Z]{3}|\\d{3})$", message = "An ISO 3166-1 alpha2, alpha-3 or numeric format value is expected.")
4244
private String countryCode;
4345

4446
@JsonProperty("gx:countrySubdivisionCode")
4547
@JsonSerialize(using = StringSerializer.class)
4648
@JsonDeserialize(using = StringDeserializer.class)
47-
@NotNull
49+
@NotBlank(message = "Country subdivision code is needed")
50+
@Pattern(regexp = "^[A-Z]{2}-[A-Z0-9]{1,3}$", message = "An ISO 3166-2 format value is expected.")
4851
private String countrySubdivisionCode;
4952

5053
@JsonProperty("vcard:street-address")

backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/NodeKindIRITypeId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
@NoArgsConstructor
3434
public class NodeKindIRITypeId {
3535

36-
@NotNull
36+
// no input validations as this will be set by the backend
3737
@JsonAlias("@id")
3838
private String id;
3939

backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalParticipantCredentialSubject.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import eu.possiblex.portal.application.entity.credentials.gx.datatypes.NodeKindIRITypeId;
2828
import eu.possiblex.portal.application.entity.credentials.serialization.StringDeserializer;
2929
import eu.possiblex.portal.application.entity.credentials.serialization.StringSerializer;
30+
import jakarta.validation.Valid;
31+
import jakarta.validation.constraints.NotBlank;
3032
import jakarta.validation.constraints.NotNull;
3133
import lombok.*;
3234

@@ -58,22 +60,25 @@ public class GxLegalParticipantCredentialSubject extends PojoCredentialSubject {
5860
"https://schema.org/");
5961

6062
// Tagus
61-
@NotNull
63+
// no input validations as this will be set by the backend
6264
@JsonProperty("gx:legalRegistrationNumber")
6365
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
6466
private List<NodeKindIRITypeId> legalRegistrationNumber; // will be gx:registrationNumber in Loire
6567

6668
// Loire
67-
@NotNull
69+
@Valid
70+
@NotNull(message = "Legal address is needed")
6871
@JsonProperty("gx:legalAddress")
6972
private GxVcard legalAddress; // contains Tagus gx:countrySubdivisionCode
7073

7174
// Loire
72-
@NotNull
75+
@Valid
76+
@NotNull(message = "Headquarter address is needed")
7377
@JsonProperty("gx:headquarterAddress")
7478
private GxVcard headquarterAddress; // contains Tagus gx:countrySubdivisionCode
7579

7680
// Loire
81+
@NotBlank(message = "Name is needed")
7782
@JsonProperty("schema:name")
7883
@JsonSerialize(using = StringSerializer.class)
7984
@JsonDeserialize(using = StringDeserializer.class)

backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalRegistrationNumberCredentialSubject.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import eu.possiblex.portal.application.entity.credentials.PojoCredentialSubject;
2525
import eu.possiblex.portal.application.entity.credentials.serialization.StringDeserializer;
2626
import eu.possiblex.portal.application.entity.credentials.serialization.StringSerializer;
27+
import eu.possiblex.portal.application.entity.credentials.validation.AtLeastOneRegistrationNumberNotEmpty;
2728
import lombok.*;
2829

2930
import java.util.Map;
@@ -35,6 +36,7 @@
3536
@JsonIgnoreProperties(ignoreUnknown = true, value = { "type", "@context" }, allowGetters = true)
3637
@NoArgsConstructor
3738
@AllArgsConstructor
39+
@AtLeastOneRegistrationNumberNotEmpty(message = "At least one registration number type must be provided")
3840
public class GxLegalRegistrationNumberCredentialSubject extends PojoCredentialSubject {
3941

4042
@Getter(AccessLevel.NONE)

backend/src/main/java/eu/possiblex/portal/application/entity/credentials/px/participants/PxParticipantExtensionCredentialSubject.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import eu.possiblex.portal.application.entity.credentials.PojoCredentialSubject;
99
import eu.possiblex.portal.application.entity.credentials.serialization.StringDeserializer;
1010
import eu.possiblex.portal.application.entity.credentials.serialization.StringSerializer;
11+
import jakarta.validation.constraints.NotBlank;
12+
import jakarta.validation.constraints.Pattern;
1113
import lombok.*;
1214

1315
import java.util.Map;
@@ -33,9 +35,11 @@ public class PxParticipantExtensionCredentialSubject extends PojoCredentialSubje
3335
public static final Map<String, String> CONTEXT = Map.of(TYPE_NAMESPACE, "http://w3id.org/gaia-x/possible-x#",
3436
"vcard", "http://www.w3.org/2006/vcard/ns#", "xsd", "http://www.w3.org/2001/XMLSchema#");
3537

38+
@NotBlank(message = "Mail address is needed")
3639
@JsonProperty("px:mailAddress")
3740
@JsonSerialize(using = StringSerializer.class)
3841
@JsonDeserialize(using = StringDeserializer.class)
42+
@Pattern(regexp = "^((?!\\.)[\\w\\-_.]*[^.])(@\\w+)(\\.\\w+(\\.\\w+)?[^.\\W])$", message = "Mail address must be a valid email address")
3943
private String mailAddress;
4044

4145
@JsonProperty("type")

backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerDeserializer.java

Lines changed: 0 additions & 48 deletions
This file was deleted.

backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerSerializer.java

Lines changed: 0 additions & 43 deletions
This file was deleted.

backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/UriDeserializer.java

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)