From e9c95dba3028e01934ac3fee85b5e35a02376931 Mon Sep 17 00:00:00 2001 From: "Marc B." <125284318+M-Busk@users.noreply.github.com> Date: Wed, 12 Feb 2025 16:31:01 +0100 Subject: [PATCH] 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 --- backend/build.gradle.kts | 1 + .../ParticipantRegistrationRestApi.java | 3 +- .../BoundaryExceptionHandler.java | 31 ++ .../ParticipantRegistrationRestApiMapper.java | 3 - .../entity/CreateRegistrationRequestTO.java | 8 + .../credentials/PojoCredentialSubject.java | 1 + .../credentials/gx/datatypes/GxVcard.java | 9 +- .../gx/datatypes/NodeKindIRITypeId.java | 2 +- .../GxLegalParticipantCredentialSubject.java | 11 +- ...alRegistrationNumberCredentialSubject.java | 2 + ...ParticipantExtensionCredentialSubject.java | 4 + .../serialization/IntegerDeserializer.java | 48 --- .../serialization/IntegerSerializer.java | 43 --- .../serialization/UriDeserializer.java | 47 --- .../serialization/UriSerializer.java | 43 --- .../AtLeastOneRegistrationNumberNotEmpty.java | 20 ++ ...neRegistrationNumberNotEmptyValidator.java | 25 ++ .../business/control/FhCatalogClientImpl.java | 51 ++- .../ParticipantRegistrationServiceImpl.java | 87 ++++-- .../ParticipantRegistrationServiceMapper.java | 7 - ...ndedLegalParticipantCredentialSubject.java | 4 + .../CatalogCommunicationException.java | 8 + .../exception/CatalogParsingException.java | 9 + .../RegistrationRequestConflictException.java | 5 - .../ParticipantRegistrationEntityMapper.java | 20 -- ...ParticipantRegistrationRequestDAOImpl.java | 144 +++++---- .../ParticipantEntityNotFoundException.java | 8 + ...icipantEntityStateTransitionException.java | 8 + .../utilities/ExceptionHandlingFilter.java | 50 --- .../portal/utilities/PossibleXException.java | 26 -- .../ParticipantRegistrationModuleTest.java | 200 ++++++++++++ .../ParticipantRegistrationRestApiTest.java | 130 +++++++- .../boundary/ParticipantShapeRestApiTest.java | 19 +- ...ticipantRegistrationRestApiMapperTest.java | 88 +++--- .../control/DidWebServiceApiClientFake.java | 19 +- .../business/control/FhCatalogClientFake.java | 16 +- .../business/control/FhCatalogClientTest.java | 120 +++++++ .../control/OmejdnConnectorApiClientFake.java | 10 +- .../ParticipantRegistrationServiceFake.java | 20 +- ...ticipantRegistrationServiceMapperTest.java | 80 ++--- .../ParticipantRegistrationServiceTest.java | 293 ++++++++++++++---- .../SdCreationWizardApiServiceTest.java | 14 +- .../control/TechnicalFhCatalogClientFake.java | 131 ++++++++ ...rticipantRegistrationEntityMapperTest.java | 239 ++++++++++++++ ...ParticipantRegistrationRequestDAOFake.java | 78 ++++- ...ParticipantRegistrationRequestDAOTest.java | 221 +++++++++---- .../portal/testutilities/ResultCaptor.java | 20 ++ backend/src/test/resources/application.yml | 10 +- frontend/package.json | 2 +- libs.versions.toml | 1 + 50 files changed, 1777 insertions(+), 662 deletions(-) delete mode 100644 backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerDeserializer.java delete mode 100644 backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerSerializer.java delete mode 100644 backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/UriDeserializer.java delete mode 100644 backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/UriSerializer.java create mode 100644 backend/src/main/java/eu/possiblex/portal/application/entity/credentials/validation/AtLeastOneRegistrationNumberNotEmpty.java create mode 100644 backend/src/main/java/eu/possiblex/portal/application/entity/credentials/validation/AtLeastOneRegistrationNumberNotEmptyValidator.java create mode 100644 backend/src/main/java/eu/possiblex/portal/business/entity/exception/CatalogCommunicationException.java create mode 100644 backend/src/main/java/eu/possiblex/portal/business/entity/exception/CatalogParsingException.java create mode 100644 backend/src/main/java/eu/possiblex/portal/persistence/entity/exception/ParticipantEntityNotFoundException.java create mode 100644 backend/src/main/java/eu/possiblex/portal/persistence/entity/exception/ParticipantEntityStateTransitionException.java delete mode 100644 backend/src/main/java/eu/possiblex/portal/utilities/ExceptionHandlingFilter.java delete mode 100644 backend/src/main/java/eu/possiblex/portal/utilities/PossibleXException.java create mode 100644 backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationModuleTest.java create mode 100644 backend/src/test/java/eu/possiblex/portal/business/control/FhCatalogClientTest.java create mode 100644 backend/src/test/java/eu/possiblex/portal/business/control/TechnicalFhCatalogClientFake.java create mode 100644 backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationEntityMapperTest.java create mode 100644 backend/src/test/java/eu/possiblex/portal/testutilities/ResultCaptor.java diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index f7090fa..6de1100 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { implementation(libs.springBootStarterWebflux) implementation(libs.springBootStarterDataJpa) implementation(libs.springBootStarterSecurity) + implementation(libs.springBootStarterValidation) implementation(libs.hibernateValidator) implementation(libs.openApi) implementation(libs.titaniumJsonLd) diff --git a/backend/src/main/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationRestApi.java b/backend/src/main/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationRestApi.java index 50a0c64..5f22cb2 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationRestApi.java +++ b/backend/src/main/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationRestApi.java @@ -2,6 +2,7 @@ import eu.possiblex.portal.application.entity.CreateRegistrationRequestTO; import eu.possiblex.portal.application.entity.RegistrationRequestEntryTO; +import jakarta.validation.Valid; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; @@ -16,7 +17,7 @@ public interface ParticipantRegistrationRestApi { * @param request participant registration request */ @PostMapping(value = "/request", produces = MediaType.APPLICATION_JSON_VALUE) - void registerParticipant(@RequestBody CreateRegistrationRequestTO request); + void registerParticipant(@Valid @RequestBody CreateRegistrationRequestTO request); /** * GET request for retrieving registration requests for the given pagination request. diff --git a/backend/src/main/java/eu/possiblex/portal/application/configuration/BoundaryExceptionHandler.java b/backend/src/main/java/eu/possiblex/portal/application/configuration/BoundaryExceptionHandler.java index ba3ed57..f1ad844 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/configuration/BoundaryExceptionHandler.java +++ b/backend/src/main/java/eu/possiblex/portal/application/configuration/BoundaryExceptionHandler.java @@ -5,11 +5,20 @@ import eu.possiblex.portal.business.entity.exception.RegistrationRequestConflictException; import eu.possiblex.portal.business.entity.exception.RegistrationRequestProcessingException; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import java.util.HashMap; +import java.util.Map; + import static org.springframework.http.HttpStatus.*; /** @@ -54,6 +63,28 @@ public ResponseEntity handleException(ParticipantComplianceExce UNPROCESSABLE_ENTITY); } + /** + * Handle Spring validation exceptions. + */ + @Override + public ResponseEntity handleMethodArgumentNotValid(@NonNull MethodArgumentNotValidException ex, + @NonNull HttpHeaders headers, @NonNull HttpStatusCode status, @NonNull WebRequest request) { + + logError(ex); + + Map errors = new HashMap<>(); + ex.getBindingResult().getAllErrors().forEach((error) -> { + String fieldName = ((FieldError) error).getField(); + String errorMessage = error.getDefaultMessage(); + errors.put(fieldName, errorMessage); + }); + StringBuilder message = new StringBuilder(); + errors.forEach((key, value) -> message.append(key).append(": ").append(value).append("; ")); + + return new ResponseEntity<>(new ErrorResponseTO("Request contained errors.", message.toString().strip()), + BAD_REQUEST); + } + /** * Handle all other exceptions. */ diff --git a/backend/src/main/java/eu/possiblex/portal/application/control/ParticipantRegistrationRestApiMapper.java b/backend/src/main/java/eu/possiblex/portal/application/control/ParticipantRegistrationRestApiMapper.java index 9f1a2c2..5f541d6 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/control/ParticipantRegistrationRestApiMapper.java +++ b/backend/src/main/java/eu/possiblex/portal/application/control/ParticipantRegistrationRestApiMapper.java @@ -19,8 +19,5 @@ public interface ParticipantRegistrationRestApiMapper { @Mapping(target = "id", ignore = true) PxExtendedLegalParticipantCredentialSubject credentialSubjectsToExtendedLegalParticipantCs( CreateRegistrationRequestTO request); - - GxNestedLegalRegistrationNumberCredentialSubject registrationNumberCsToNestedLegalRegistrationNumberCs( - GxLegalRegistrationNumberCredentialSubject request); } diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/CreateRegistrationRequestTO.java b/backend/src/main/java/eu/possiblex/portal/application/entity/CreateRegistrationRequestTO.java index 915964b..95e95bc 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/CreateRegistrationRequestTO.java +++ b/backend/src/main/java/eu/possiblex/portal/application/entity/CreateRegistrationRequestTO.java @@ -3,6 +3,8 @@ import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalParticipantCredentialSubject; import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalRegistrationNumberCredentialSubject; import eu.possiblex.portal.application.entity.credentials.px.participants.PxParticipantExtensionCredentialSubject; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -14,9 +16,15 @@ @AllArgsConstructor public class CreateRegistrationRequestTO { + @Valid + @NotNull(message = "Participant credential subject is required") private GxLegalParticipantCredentialSubject participantCs; + @Valid + @NotNull(message = "Registration number credential subject is required") private GxLegalRegistrationNumberCredentialSubject registrationNumberCs; + @Valid + @NotNull(message = "Participant extension credential subject is required") private PxParticipantExtensionCredentialSubject participantExtensionCs; } diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/PojoCredentialSubject.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/PojoCredentialSubject.java index 52bb41f..6be2e1a 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/PojoCredentialSubject.java +++ b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/PojoCredentialSubject.java @@ -35,6 +35,7 @@ @AllArgsConstructor public abstract class PojoCredentialSubject { // base fields + // no input validations as this will be set by the backend private String id; } diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/GxVcard.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/GxVcard.java index 9575f00..24fd2af 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/GxVcard.java +++ b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/GxVcard.java @@ -23,7 +23,8 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import eu.possiblex.portal.application.entity.credentials.serialization.StringDeserializer; import eu.possiblex.portal.application.entity.credentials.serialization.StringSerializer; -import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -38,13 +39,15 @@ public class GxVcard { @JsonProperty("gx:countryCode") @JsonSerialize(using = StringSerializer.class) @JsonDeserialize(using = StringDeserializer.class) - @NotNull + @NotBlank(message = "Country code is needed") + @Pattern(regexp = "^([A-Z]{2}|[A-Z]{3}|\\d{3})$", message = "An ISO 3166-1 alpha2, alpha-3 or numeric format value is expected.") private String countryCode; @JsonProperty("gx:countrySubdivisionCode") @JsonSerialize(using = StringSerializer.class) @JsonDeserialize(using = StringDeserializer.class) - @NotNull + @NotBlank(message = "Country subdivision code is needed") + @Pattern(regexp = "^[A-Z]{2}-[A-Z0-9]{1,3}$", message = "An ISO 3166-2 format value is expected.") private String countrySubdivisionCode; @JsonProperty("vcard:street-address") diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/NodeKindIRITypeId.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/NodeKindIRITypeId.java index b6839f0..67a216d 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/NodeKindIRITypeId.java +++ b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/datatypes/NodeKindIRITypeId.java @@ -33,7 +33,7 @@ @NoArgsConstructor public class NodeKindIRITypeId { - @NotNull + // no input validations as this will be set by the backend @JsonAlias("@id") private String id; diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalParticipantCredentialSubject.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalParticipantCredentialSubject.java index 20f075c..fe1001e 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalParticipantCredentialSubject.java +++ b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalParticipantCredentialSubject.java @@ -27,6 +27,8 @@ import eu.possiblex.portal.application.entity.credentials.gx.datatypes.NodeKindIRITypeId; import eu.possiblex.portal.application.entity.credentials.serialization.StringDeserializer; import eu.possiblex.portal.application.entity.credentials.serialization.StringSerializer; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.*; @@ -58,22 +60,25 @@ public class GxLegalParticipantCredentialSubject extends PojoCredentialSubject { "https://schema.org/"); // Tagus - @NotNull + // no input validations as this will be set by the backend @JsonProperty("gx:legalRegistrationNumber") @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) private List legalRegistrationNumber; // will be gx:registrationNumber in Loire // Loire - @NotNull + @Valid + @NotNull(message = "Legal address is needed") @JsonProperty("gx:legalAddress") private GxVcard legalAddress; // contains Tagus gx:countrySubdivisionCode // Loire - @NotNull + @Valid + @NotNull(message = "Headquarter address is needed") @JsonProperty("gx:headquarterAddress") private GxVcard headquarterAddress; // contains Tagus gx:countrySubdivisionCode // Loire + @NotBlank(message = "Name is needed") @JsonProperty("schema:name") @JsonSerialize(using = StringSerializer.class) @JsonDeserialize(using = StringDeserializer.class) diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalRegistrationNumberCredentialSubject.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalRegistrationNumberCredentialSubject.java index dc6d57b..af2cccb 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalRegistrationNumberCredentialSubject.java +++ b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/gx/participants/GxLegalRegistrationNumberCredentialSubject.java @@ -24,6 +24,7 @@ import eu.possiblex.portal.application.entity.credentials.PojoCredentialSubject; import eu.possiblex.portal.application.entity.credentials.serialization.StringDeserializer; import eu.possiblex.portal.application.entity.credentials.serialization.StringSerializer; +import eu.possiblex.portal.application.entity.credentials.validation.AtLeastOneRegistrationNumberNotEmpty; import lombok.*; import java.util.Map; @@ -35,6 +36,7 @@ @JsonIgnoreProperties(ignoreUnknown = true, value = { "type", "@context" }, allowGetters = true) @NoArgsConstructor @AllArgsConstructor +@AtLeastOneRegistrationNumberNotEmpty(message = "At least one registration number type must be provided") public class GxLegalRegistrationNumberCredentialSubject extends PojoCredentialSubject { @Getter(AccessLevel.NONE) diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/px/participants/PxParticipantExtensionCredentialSubject.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/px/participants/PxParticipantExtensionCredentialSubject.java index 8813475..e7363e4 100644 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/px/participants/PxParticipantExtensionCredentialSubject.java +++ b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/px/participants/PxParticipantExtensionCredentialSubject.java @@ -8,6 +8,8 @@ import eu.possiblex.portal.application.entity.credentials.PojoCredentialSubject; import eu.possiblex.portal.application.entity.credentials.serialization.StringDeserializer; import eu.possiblex.portal.application.entity.credentials.serialization.StringSerializer; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; import lombok.*; import java.util.Map; @@ -33,9 +35,11 @@ public class PxParticipantExtensionCredentialSubject extends PojoCredentialSubje public static final Map CONTEXT = Map.of(TYPE_NAMESPACE, "http://w3id.org/gaia-x/possible-x#", "vcard", "http://www.w3.org/2006/vcard/ns#", "xsd", "http://www.w3.org/2001/XMLSchema#"); + @NotBlank(message = "Mail address is needed") @JsonProperty("px:mailAddress") @JsonSerialize(using = StringSerializer.class) @JsonDeserialize(using = StringDeserializer.class) + @Pattern(regexp = "^((?!\\.)[\\w\\-_.]*[^.])(@\\w+)(\\.\\w+(\\.\\w+)?[^.\\W])$", message = "Mail address must be a valid email address") private String mailAddress; @JsonProperty("type") diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerDeserializer.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerDeserializer.java deleted file mode 100644 index 5a7b9df..0000000 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerDeserializer.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Dataport. All rights reserved. Developed as part of the MERLOT project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package eu.possiblex.portal.application.entity.credentials.serialization; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; - -import java.io.IOException; - -public class IntegerDeserializer extends StdDeserializer { - - public IntegerDeserializer() { - - this(null); - } - - public IntegerDeserializer(Class vc) { - - super(vc); - } - - @Override - public Integer deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) - throws IOException { - - JsonNode node = jsonParser.getCodec().readTree(jsonParser); - if (node.get("@type") != null && node.get("@type").textValue().equals("xsd:integer")) { - return node.get("@value").intValue(); - } - return node.intValue(); - } -} diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerSerializer.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerSerializer.java deleted file mode 100644 index 5d33a6c..0000000 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/IntegerSerializer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Dataport. All rights reserved. Developed as part of the MERLOT project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package eu.possiblex.portal.application.entity.credentials.serialization; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -import java.io.IOException; - -public class IntegerSerializer extends StdSerializer { - - public IntegerSerializer() { - - this(null); - } - - public IntegerSerializer(Class t) { - - super(t); - } - - @Override - public void serialize(Integer i, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) - throws IOException { - - jsonGenerator.writeNumber(i); - } -} diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/UriDeserializer.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/UriDeserializer.java deleted file mode 100644 index d4687a4..0000000 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/UriDeserializer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2024 Dataport. All rights reserved. Developed as part of the MERLOT project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package eu.possiblex.portal.application.entity.credentials.serialization; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; - -import java.io.IOException; - -public class UriDeserializer extends StdDeserializer { - - public UriDeserializer() { - - this(null); - } - - public UriDeserializer(Class vc) { - - super(vc); - } - - @Override - public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { - - JsonNode node = jsonParser.getCodec().readTree(jsonParser); - if (node.get("@type") != null && node.get("@type").textValue().equals("xsd:anyURI")) { - return node.get("@value").textValue(); - } - return node.textValue(); - } -} diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/UriSerializer.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/UriSerializer.java deleted file mode 100644 index 2b55672..0000000 --- a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/serialization/UriSerializer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Dataport. All rights reserved. Developed as part of the MERLOT project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package eu.possiblex.portal.application.entity.credentials.serialization; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -import java.io.IOException; - -public class UriSerializer extends StdSerializer { - - public UriSerializer() { - - this(null); - } - - public UriSerializer(Class t) { - - super(t); - } - - @Override - public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) - throws IOException { - - jsonGenerator.writeString(s); - } -} \ No newline at end of file diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/validation/AtLeastOneRegistrationNumberNotEmpty.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/validation/AtLeastOneRegistrationNumberNotEmpty.java new file mode 100644 index 0000000..5f70ae1 --- /dev/null +++ b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/validation/AtLeastOneRegistrationNumberNotEmpty.java @@ -0,0 +1,20 @@ +package eu.possiblex.portal.application.entity.credentials.validation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Constraint(validatedBy = AtLeastOneRegistrationNumberNotEmptyValidator.class) +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface AtLeastOneRegistrationNumberNotEmpty { + String message() default "At least one field must be not empty"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/validation/AtLeastOneRegistrationNumberNotEmptyValidator.java b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/validation/AtLeastOneRegistrationNumberNotEmptyValidator.java new file mode 100644 index 0000000..85e4712 --- /dev/null +++ b/backend/src/main/java/eu/possiblex/portal/application/entity/credentials/validation/AtLeastOneRegistrationNumberNotEmptyValidator.java @@ -0,0 +1,25 @@ +package eu.possiblex.portal.application.entity.credentials.validation; + +import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalRegistrationNumberCredentialSubject; +import io.netty.util.internal.StringUtil; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class AtLeastOneRegistrationNumberNotEmptyValidator + implements ConstraintValidator { + + @Override + public boolean isValid(GxLegalRegistrationNumberCredentialSubject cs, ConstraintValidatorContext context) { + + // object must be non-null + if (cs == null) { + return false; + } + + // at least one registration number field must be non-null and not empty + return !StringUtil.isNullOrEmpty(cs.getEori()) || !StringUtil.isNullOrEmpty(cs.getVatID()) + || !StringUtil.isNullOrEmpty(cs.getLeiCode()); + } +} \ No newline at end of file diff --git a/backend/src/main/java/eu/possiblex/portal/business/control/FhCatalogClientImpl.java b/backend/src/main/java/eu/possiblex/portal/business/control/FhCatalogClientImpl.java index 3ed4cd1..be7645a 100644 --- a/backend/src/main/java/eu/possiblex/portal/business/control/FhCatalogClientImpl.java +++ b/backend/src/main/java/eu/possiblex/portal/business/control/FhCatalogClientImpl.java @@ -1,11 +1,13 @@ package eu.possiblex.portal.business.control; import com.apicatalog.jsonld.JsonLd; -import com.apicatalog.jsonld.JsonLdError; import com.apicatalog.jsonld.document.JsonDocument; -import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; +import eu.possiblex.portal.business.entity.exception.CatalogCommunicationException; +import eu.possiblex.portal.business.entity.exception.CatalogParsingException; +import eu.possiblex.portal.business.entity.exception.ParticipantComplianceException; import eu.possiblex.portal.business.entity.exception.ParticipantNotFoundException; import eu.possiblex.portal.business.entity.fh.FhCatalogIdResponse; import eu.possiblex.portal.utilities.LogUtils; @@ -60,8 +62,7 @@ public FhCatalogIdResponse addParticipantToCatalog(PxExtendedLegalParticipantCre try { catalogParticipantId = technicalFhCatalogClient.addParticipantToFhCatalog(cs, verMethod); } catch (Exception e) { - log.error("error when trying to send participant to catalog!", e); - throw e; + throw buildCatalogCommunicationException(e); } log.info("got participant id: {}", catalogParticipantId.getId()); return catalogParticipantId; @@ -70,17 +71,14 @@ public FhCatalogIdResponse addParticipantToCatalog(PxExtendedLegalParticipantCre @Override public PxExtendedLegalParticipantCredentialSubject getParticipantFromCatalog(String participantId) { - log.info("fetching participant for fh catalog ID " + participantId); + log.info("fetching participant for fh catalog ID {}", participantId); String participantJsonContent; try { participantJsonContent = technicalFhCatalogClient.getParticipantFromCatalog(participantId); - } catch (WebClientResponseException e) { - if (e.getStatusCode().value() == 404) { - throw new ParticipantNotFoundException("no FH Catalog participant found with ID " + participantId); - } - throw e; + } catch (Exception e) { + throw buildCatalogCommunicationException(e); } - log.info("answer for fh catalog ID: " + participantJsonContent); + log.info("answer for fh catalog ID {}: {}", participantId, participantJsonContent); try { JsonDocument input = JsonDocument.of(new StringReader(participantJsonContent)); @@ -90,8 +88,9 @@ public PxExtendedLegalParticipantCredentialSubject getParticipantFromCatalog(Str return objectMapper.readValue(framedParticipant.toString(), PxExtendedLegalParticipantCredentialSubject.class); - } catch (JsonLdError | JsonProcessingException e) { - throw new RuntimeException("failed to parse fh catalog participant json: " + participantJsonContent, e); + } catch (Exception e) { + throw new CatalogParsingException("failed to parse fh catalog participant json: " + participantJsonContent, + e); } } @@ -101,11 +100,29 @@ public void deleteParticipantFromCatalog(String participantId) { log.info("deleting participant from fh catalog with ID {}", participantId); try { technicalFhCatalogClient.deleteParticipantFromCatalog(participantId); - } catch (WebClientResponseException e) { - if (e.getStatusCode().value() == 404) { - throw new ParticipantNotFoundException("no FH Catalog participant found with ID " + participantId); + } catch (Exception e) { + throw buildCatalogCommunicationException(e); + } + } + + private RuntimeException buildCatalogCommunicationException(Exception e) { + + log.warn("error during communication with catalog", e); + if (e instanceof WebClientResponseException responseException) { + if (responseException.getStatusCode().value() == 404) { + return new ParticipantNotFoundException("no FH Catalog participant found with given id"); } - throw e; + if (responseException.getStatusCode().value() == 422) { + JsonNode error = responseException.getResponseBodyAs(JsonNode.class); + if (error != null && error.get("error") != null) { + return new ParticipantComplianceException(error.get("error").textValue(), e); + } + return new ParticipantComplianceException("Unknown catalog processing exception", e); + } + return new CatalogCommunicationException("Catalog returned bad response", responseException); + } else { + return new CatalogCommunicationException("Unexpected error during catalog request", e); } + } } diff --git a/backend/src/main/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceImpl.java b/backend/src/main/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceImpl.java index 14f4c49..fdc1a32 100644 --- a/backend/src/main/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceImpl.java +++ b/backend/src/main/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceImpl.java @@ -1,6 +1,5 @@ package eu.possiblex.portal.business.control; -import com.fasterxml.jackson.databind.JsonNode; import eu.possiblex.portal.application.entity.RegistrationRequestEntryTO; import eu.possiblex.portal.business.entity.ParticipantRegistrationRequestBE; import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; @@ -10,17 +9,19 @@ import eu.possiblex.portal.business.entity.did.ParticipantDidCreateRequestBE; import eu.possiblex.portal.business.entity.did.ParticipantDidUpdateRequestBE; import eu.possiblex.portal.business.entity.exception.ParticipantComplianceException; +import eu.possiblex.portal.business.entity.exception.ParticipantNotFoundException; import eu.possiblex.portal.business.entity.exception.RegistrationRequestConflictException; import eu.possiblex.portal.business.entity.exception.RegistrationRequestProcessingException; import eu.possiblex.portal.business.entity.fh.FhCatalogIdResponse; import eu.possiblex.portal.persistence.dao.ParticipantRegistrationRequestDAO; +import eu.possiblex.portal.persistence.entity.exception.ParticipantEntityNotFoundException; +import eu.possiblex.portal.persistence.entity.exception.ParticipantEntityStateTransitionException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import org.springframework.web.reactive.function.client.WebClientResponseException; import java.util.List; @@ -69,7 +70,10 @@ public void registerParticipant(PxExtendedLegalParticipantCredentialSubject cs) log.info("Processing participant registration: {}", cs); - if (participantRegistrationRequestDAO.getRegistrationRequestByName(cs.getName()) != null) { + ParticipantRegistrationRequestBE be = participantRegistrationRequestDAO.getRegistrationRequestByName( + cs.getName()); + + if (be != null) { throw new RegistrationRequestConflictException( "A registration request has already been made under this organization name: " + cs.getName()); } @@ -97,8 +101,13 @@ public RegistrationRequestEntryTO getParticipantRegistrationRequestByDid(String log.info("Processing retrieval of participant registration requests with did {}", did); - return participantRegistrationServiceMapper.participantRegistrationRequestBEToRegistrationRequestEntryTO( - participantRegistrationRequestDAO.getRegistrationRequestByDid(did)); + ParticipantRegistrationRequestBE be = participantRegistrationRequestDAO.getRegistrationRequestByDid(did); + + if (be == null) { + throw new ParticipantNotFoundException("No registration request found for DID: " + did); + } + + return participantRegistrationServiceMapper.participantRegistrationRequestBEToRegistrationRequestEntryTO(be); } /** @@ -111,7 +120,12 @@ public void acceptRegistrationRequest(String id) { log.info("Processing acceptance of participant: {}", id); - participantRegistrationRequestDAO.acceptRegistrationRequest(id); + try { + participantRegistrationRequestDAO.acceptRegistrationRequest(id); + } catch (Exception e) { + handleEntityProcessingException(e); + } + completeRegistrationRequest(id); } @@ -169,7 +183,7 @@ private void completeRegistrationRequest(String id) { deleteDapsCertificate(certificate.getClientId()); deleteParticipantFromCatalog(idResponse.getId()); deleteDidWeb(didWeb.getDid()); - throw e; + handleEntityProcessingException(e); } log.info("Stored finalized registration request for participant: {}", id); } @@ -184,7 +198,12 @@ public void rejectRegistrationRequest(String id) { log.info("Processing rejection of participant: {}", id); - participantRegistrationRequestDAO.rejectRegistrationRequest(id); + try { + participantRegistrationRequestDAO.rejectRegistrationRequest(id); + } catch (Exception e) { + handleEntityProcessingException(e); + } + } /** @@ -196,15 +215,21 @@ public void rejectRegistrationRequest(String id) { public void deleteRegistrationRequest(String id) { log.info("Processing deletion of participant: {}", id); - participantRegistrationRequestDAO.deleteRegistrationRequest(id); + + try { + participantRegistrationRequestDAO.deleteRegistrationRequest(id); + } catch (Exception e) { + handleEntityProcessingException(e); + } + } private OmejdnConnectorCertificateBE requestDapsCertificate(String clientName, String did) { try { return omejdnConnectorApiClient.addConnector(new OmejdnConnectorCertificateRequest(clientName, did)); - } catch (WebClientResponseException e) { - log.error("Failed to request DAPS certificate: {}", e.getResponseBodyAsString()); + } catch (Exception e) { + log.error("Failed to request DAPS certificate {}", clientName, e); throw new RegistrationRequestProcessingException("Failed to request DAPS certificate", e); } } @@ -213,8 +238,8 @@ private void deleteDapsCertificate(String clientId) { try { omejdnConnectorApiClient.deleteConnector(clientId); - } catch (WebClientResponseException e) { - log.error("Failed to delete DAPS certificate", e); + } catch (Exception e) { + log.error("Failed to delete DAPS certificate {}", clientId, e); } } @@ -224,8 +249,8 @@ private ParticipantDidBE generateDidWeb(String id) { createRequestBE.setSubject(id); try { return didWebServiceApiClient.generateDidWeb(createRequestBE); - } catch (WebClientResponseException e) { - log.error("Failed to generate DID: {}", e.getResponseBodyAsString()); + } catch (Exception e) { + log.error("Failed to generate DID {}", id, e); throw new RegistrationRequestProcessingException("Failed to generate DID", e); } } @@ -235,8 +260,8 @@ private void updateDidWebWithAliases(String did, List aliases) { ParticipantDidUpdateRequestBE updateRequestBE = new ParticipantDidUpdateRequestBE(did, aliases); try { didWebServiceApiClient.updateDidWeb(updateRequestBE); - } catch (WebClientResponseException e) { - log.error("Failed to update DID: {}", e.getResponseBodyAsString()); + } catch (Exception e) { + log.error("Failed to update DID {}", did, e); throw new RegistrationRequestProcessingException("Failed to update DID document with DAPS ID", e); } } @@ -245,8 +270,8 @@ private void deleteDidWeb(String id) { try { didWebServiceApiClient.deleteDidWeb(id); - } catch (WebClientResponseException e) { - log.error("Failed to delete DID: {}", e.getResponseBodyAsString()); + } catch (Exception e) { + log.error("Failed to delete DID {}", id, e); } } @@ -262,12 +287,12 @@ private FhCatalogIdResponse enrollParticipantInCatalog(ParticipantRegistrationRe log.info("Stored CS for participant {} in catalog: {}", idResponse, cs); return idResponse; - } catch (WebClientResponseException.UnprocessableEntity e) { - JsonNode error = e.getResponseBodyAs(JsonNode.class); - if (error != null && error.get("error") != null) { - throw new ParticipantComplianceException(error.get("error").textValue(), e); - } - throw new ParticipantComplianceException("Unknown catalog processing exception", e); + } catch (ParticipantComplianceException e) { + log.error("Participant {} does not fulfill compliance", cs); + throw e; + } catch (Exception e) { + log.error("Failed to store participant in catalog", e); + throw new RegistrationRequestProcessingException("Failed to store participant in catalog", e); } } @@ -279,4 +304,16 @@ private void deleteParticipantFromCatalog(String id) { log.error("Failed to delete participant from catalog", e); } } + + private void handleEntityProcessingException(Exception e) { + + if (e instanceof ParticipantEntityNotFoundException notFoundException) { + throw new ParticipantNotFoundException(notFoundException.getMessage()); + } else if (e instanceof ParticipantEntityStateTransitionException transitionException) { + throw new RegistrationRequestProcessingException( + "Cannot transition participant registration request: " + transitionException.getMessage(), e); + } else { + throw new RegistrationRequestProcessingException("Unknown error during request processing", e); + } + } } diff --git a/backend/src/main/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceMapper.java b/backend/src/main/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceMapper.java index e643e9a..5bd791b 100644 --- a/backend/src/main/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceMapper.java +++ b/backend/src/main/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceMapper.java @@ -15,13 +15,6 @@ @Mapper(componentModel = "spring") public interface ParticipantRegistrationServiceMapper { - AddressTO gxVcardToAddressTO(GxVcard gxVcard); - - RegistrationNumberTO legalRegistrationNumberToRegistrationNumberTO( - GxLegalRegistrationNumberCredentialSubject legalRegistrationNumber); - - ParticipantDidDataTO didDataToDidDataTO(ParticipantDidBE didData); - RegistrationRequestEntryTO participantRegistrationRequestBEToRegistrationRequestEntryTO( ParticipantRegistrationRequestBE participantRegistrationRequestBE); diff --git a/backend/src/main/java/eu/possiblex/portal/business/entity/credentials/px/PxExtendedLegalParticipantCredentialSubject.java b/backend/src/main/java/eu/possiblex/portal/business/entity/credentials/px/PxExtendedLegalParticipantCredentialSubject.java index e51fba7..ca33e19 100644 --- a/backend/src/main/java/eu/possiblex/portal/business/entity/credentials/px/PxExtendedLegalParticipantCredentialSubject.java +++ b/backend/src/main/java/eu/possiblex/portal/business/entity/credentials/px/PxExtendedLegalParticipantCredentialSubject.java @@ -1,5 +1,7 @@ package eu.possiblex.portal.business.entity.credentials.px; +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import eu.possiblex.portal.application.entity.credentials.gx.datatypes.GxVcard; import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalParticipantCredentialSubject; @@ -14,6 +16,7 @@ @NoArgsConstructor @AllArgsConstructor @Builder +@JsonIgnoreProperties(ignoreUnknown = true, value = { "@type", "@context" }, allowGetters = true) public class PxExtendedLegalParticipantCredentialSubject { @Getter(AccessLevel.NONE) @@ -26,6 +29,7 @@ public class PxExtendedLegalParticipantCredentialSubject { "http://www.w3.org/2001/XMLSchema#", "px", "http://w3id.org/gaia-x/possible-x#", "schema", "https://schema.org/"); + @JsonAlias("@id") private String id; @NotNull diff --git a/backend/src/main/java/eu/possiblex/portal/business/entity/exception/CatalogCommunicationException.java b/backend/src/main/java/eu/possiblex/portal/business/entity/exception/CatalogCommunicationException.java new file mode 100644 index 0000000..cdd1383 --- /dev/null +++ b/backend/src/main/java/eu/possiblex/portal/business/entity/exception/CatalogCommunicationException.java @@ -0,0 +1,8 @@ +package eu.possiblex.portal.business.entity.exception; + +public class CatalogCommunicationException extends RuntimeException { + public CatalogCommunicationException(String message, Exception cause) { + + super(message, cause); + } +} diff --git a/backend/src/main/java/eu/possiblex/portal/business/entity/exception/CatalogParsingException.java b/backend/src/main/java/eu/possiblex/portal/business/entity/exception/CatalogParsingException.java new file mode 100644 index 0000000..3e237e6 --- /dev/null +++ b/backend/src/main/java/eu/possiblex/portal/business/entity/exception/CatalogParsingException.java @@ -0,0 +1,9 @@ +package eu.possiblex.portal.business.entity.exception; + +public class CatalogParsingException extends RuntimeException { + + public CatalogParsingException(String message, Exception cause) { + + super(message, cause); + } +} diff --git a/backend/src/main/java/eu/possiblex/portal/business/entity/exception/RegistrationRequestConflictException.java b/backend/src/main/java/eu/possiblex/portal/business/entity/exception/RegistrationRequestConflictException.java index 9e6d3f9..64d42c8 100644 --- a/backend/src/main/java/eu/possiblex/portal/business/entity/exception/RegistrationRequestConflictException.java +++ b/backend/src/main/java/eu/possiblex/portal/business/entity/exception/RegistrationRequestConflictException.java @@ -5,9 +5,4 @@ public RegistrationRequestConflictException(String message) { super(message); } - - public RegistrationRequestConflictException(String message, Exception e) { - - super(message, e); - } } \ No newline at end of file diff --git a/backend/src/main/java/eu/possiblex/portal/persistence/control/ParticipantRegistrationEntityMapper.java b/backend/src/main/java/eu/possiblex/portal/persistence/control/ParticipantRegistrationEntityMapper.java index abedfc2..273a6f5 100644 --- a/backend/src/main/java/eu/possiblex/portal/persistence/control/ParticipantRegistrationEntityMapper.java +++ b/backend/src/main/java/eu/possiblex/portal/persistence/control/ParticipantRegistrationEntityMapper.java @@ -1,15 +1,9 @@ package eu.possiblex.portal.persistence.control; -import eu.possiblex.portal.application.entity.credentials.gx.datatypes.GxVcard; -import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalRegistrationNumberCredentialSubject; import eu.possiblex.portal.business.entity.ParticipantRegistrationRequestBE; import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; import eu.possiblex.portal.business.entity.daps.OmejdnConnectorCertificateBE; -import eu.possiblex.portal.business.entity.did.ParticipantDidBE; -import eu.possiblex.portal.persistence.entity.DidDataEntity; import eu.possiblex.portal.persistence.entity.ParticipantRegistrationRequestEntity; -import eu.possiblex.portal.persistence.entity.RegistrationNumberEntity; -import eu.possiblex.portal.persistence.entity.VcardEntity; import eu.possiblex.portal.persistence.entity.daps.OmejdnConnectorCertificateEntity; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -17,26 +11,12 @@ @Mapper(componentModel = "spring") public interface ParticipantRegistrationEntityMapper { - @Mapping(target = "id", ignore = true) - VcardEntity gxVcardToEntity(GxVcard vcard); - - @Mapping(target = "id", ignore = true) - RegistrationNumberEntity gxLegalRegistrationNumberToEntity( - GxLegalRegistrationNumberCredentialSubject registrationNumberCs); - @Mapping(target = "id", ignore = true) @Mapping(target = "status", expression = "java(eu.possiblex.portal.persistence.entity.RequestStatus.NEW)") @Mapping(target = "emailAddress", source = "mailAddress") ParticipantRegistrationRequestEntity pxExtendedLegalParticipantCsToNewEntity( PxExtendedLegalParticipantCredentialSubject cs); - GxVcard entityToGxVcard(VcardEntity entity); - - @Mapping(target = "id", ignore = true) - GxLegalRegistrationNumberCredentialSubject entityToGxLegalRegistrationNumber(RegistrationNumberEntity entity); - - ParticipantDidBE entityToParticipantDidBe(DidDataEntity entity); - ParticipantRegistrationRequestBE entityToParticipantRegistrationRequestBe( ParticipantRegistrationRequestEntity entity); diff --git a/backend/src/main/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOImpl.java b/backend/src/main/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOImpl.java index bace321..d4f7d60 100644 --- a/backend/src/main/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOImpl.java +++ b/backend/src/main/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOImpl.java @@ -9,6 +9,8 @@ import eu.possiblex.portal.persistence.entity.ParticipantRegistrationRequestEntity; import eu.possiblex.portal.persistence.entity.RequestStatus; import eu.possiblex.portal.persistence.entity.daps.OmejdnConnectorCertificateEntity; +import eu.possiblex.portal.persistence.entity.exception.ParticipantEntityNotFoundException; +import eu.possiblex.portal.persistence.entity.exception.ParticipantEntityStateTransitionException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -79,8 +81,13 @@ public ParticipantRegistrationRequestBE getRegistrationRequestByDid(String did) log.info("Getting participant registration request by did"); - return participantRegistrationEntityMapper.entityToParticipantRegistrationRequestBe( - participantRegistrationRequestRepository.findByDidData_Did(did)); + ParticipantRegistrationRequestEntity entity = participantRegistrationRequestRepository.findByDidData_Did(did); + + if (entity == null) { + return null; + } + + return participantRegistrationEntityMapper.entityToParticipantRegistrationRequestBe(entity); } /** @@ -93,18 +100,22 @@ public ParticipantRegistrationRequestBE getRegistrationRequestByDid(String did) public void acceptRegistrationRequest(String id) { log.info("Accepting participant registration request: {}", id); - ParticipantRegistrationRequestEntity entity = participantRegistrationRequestRepository.findByName(id); - if (entity != null) { - if (entity.getStatus() == RequestStatus.COMPLETED) { - log.error("Cannot accept completed participant registration request: {}", id); - throw new RuntimeException("Cannot accept completed participant registration request: " + id); - } else { - entity.setStatus(RequestStatus.ACCEPTED); - } - } else { - log.error("(Accept) Participant not found: {}", id); - throw new RuntimeException("Participant not found: " + id); + + ParticipantRegistrationRequestEntity entity = findParticipantRegistrationRequestById(id); + + if (entity.getStatus() == RequestStatus.COMPLETED) { + log.error("Cannot accept completed participant registration request: {}", id); + throw new ParticipantEntityStateTransitionException( + "Cannot accept completed participant registration request: " + id); + } + + if (entity.getStatus() == RequestStatus.REJECTED) { + log.error("Cannot accept rejected participant registration request: {}", id); + throw new ParticipantEntityStateTransitionException( + "Cannot accept rejected participant registration request: " + id); } + + entity.setStatus(RequestStatus.ACCEPTED); } /** @@ -117,18 +128,16 @@ public void acceptRegistrationRequest(String id) { public void rejectRegistrationRequest(String id) { log.info("Rejecting participant registration request: {}", id); - ParticipantRegistrationRequestEntity entity = participantRegistrationRequestRepository.findByName(id); - if (entity != null) { - if (entity.getStatus() == RequestStatus.COMPLETED) { - log.error("Cannot reject completed participant registration request: {}", id); - throw new RuntimeException("Cannot reject completed participant registration request: " + id); - } else { - entity.setStatus(RequestStatus.REJECTED); - } - } else { - log.error("(Reject) Participant not found: {}", id); - throw new RuntimeException("Participant not found: " + id); + + ParticipantRegistrationRequestEntity entity = findParticipantRegistrationRequestById(id); + + if (entity.getStatus() == RequestStatus.COMPLETED) { + log.error("Cannot reject completed participant registration request: {}", id); + throw new ParticipantEntityStateTransitionException( + "Cannot reject completed participant registration request: " + id); } + + entity.setStatus(RequestStatus.REJECTED); } /** @@ -141,18 +150,16 @@ public void rejectRegistrationRequest(String id) { public void deleteRegistrationRequest(String id) { log.info("Deleting participant registration request: {}", id); - ParticipantRegistrationRequestEntity entity = participantRegistrationRequestRepository.findByName(id); - if (entity != null) { - if (entity.getStatus() == RequestStatus.COMPLETED) { - log.error("Cannot delete completed participant registration request: {}", id); - throw new RuntimeException("Cannot delete completed participant registration request: " + id); - } else { - participantRegistrationRequestRepository.delete(entity); - } - } else { - log.error("(Delete) Participant not found: {}", id); - throw new RuntimeException("Participant not found: " + id); + + ParticipantRegistrationRequestEntity entity = findParticipantRegistrationRequestById(id); + + if (entity.getStatus() == RequestStatus.COMPLETED) { + log.error("Cannot delete completed participant registration request: {}", id); + throw new ParticipantEntityStateTransitionException( + "Cannot delete completed participant registration request: " + id); } + + participantRegistrationRequestRepository.delete(entity); } /** @@ -165,40 +172,55 @@ public void deleteRegistrationRequest(String id) { public void completeRegistrationRequest(String id, ParticipantDidBE did, String vpLink, OmejdnConnectorCertificateBE certificate) { - ParticipantRegistrationRequestEntity entity = participantRegistrationRequestRepository.findByName(id); - if (entity != null) { - log.info("Completing participant registration request: {}", id); - - // set did data - DidDataEntity didData = new DidDataEntity(); - didData.setDid(did.getDid()); - didData.setVerificationMethod(did.getVerificationMethod()); - entity.setDidData(didData); - - // set daps data - OmejdnConnectorCertificateEntity certificateEntity = participantRegistrationEntityMapper.omejdnConnectorCertificateBEToOmejdnConnectorCertificateEntity( - certificate); - entity.setOmejdnConnectorCertificate(certificateEntity); - - // set vp link - entity.setVpLink(vpLink); - - // complete request - entity.setStatus(RequestStatus.COMPLETED); - } else { - log.error("(Complete) Participant not found: {}", id); - throw new RuntimeException("Participant not found: " + id); + log.info("Completing participant registration request: {}", id); + + ParticipantRegistrationRequestEntity entity = findParticipantRegistrationRequestById(id); + + if (entity.getStatus() != RequestStatus.ACCEPTED) { + log.error("Cannot complete non-accepted participant registration request: {}", id); + throw new ParticipantEntityStateTransitionException( + "Cannot complete non-accepted participant registration request: " + id); } + + // set did data + DidDataEntity didData = new DidDataEntity(); + didData.setDid(did.getDid()); + didData.setVerificationMethod(did.getVerificationMethod()); + entity.setDidData(didData); + + // set daps data + OmejdnConnectorCertificateEntity certificateEntity = participantRegistrationEntityMapper.omejdnConnectorCertificateBEToOmejdnConnectorCertificateEntity( + certificate); + entity.setOmejdnConnectorCertificate(certificateEntity); + + // set vp link + entity.setVpLink(vpLink); + + // complete request + entity.setStatus(RequestStatus.COMPLETED); } @Override public ParticipantRegistrationRequestBE getRegistrationRequestByName(String name) { ParticipantRegistrationRequestEntity entity = participantRegistrationRequestRepository.findByName(name); - if (entity != null) { - return participantRegistrationEntityMapper.entityToParticipantRegistrationRequestBe(entity); - } else { + + if (entity == null) { return null; } + + return participantRegistrationEntityMapper.entityToParticipantRegistrationRequestBe(entity); + } + + private ParticipantRegistrationRequestEntity findParticipantRegistrationRequestById(String id) { + + ParticipantRegistrationRequestEntity entity = participantRegistrationRequestRepository.findByName(id); + if (entity == null) { + log.error("Participant with id {} not found", id); + throw new ParticipantEntityNotFoundException("Participant not found: " + id); + } + + return entity; } + } diff --git a/backend/src/main/java/eu/possiblex/portal/persistence/entity/exception/ParticipantEntityNotFoundException.java b/backend/src/main/java/eu/possiblex/portal/persistence/entity/exception/ParticipantEntityNotFoundException.java new file mode 100644 index 0000000..d6b14c0 --- /dev/null +++ b/backend/src/main/java/eu/possiblex/portal/persistence/entity/exception/ParticipantEntityNotFoundException.java @@ -0,0 +1,8 @@ +package eu.possiblex.portal.persistence.entity.exception; + +public class ParticipantEntityNotFoundException extends RuntimeException { + public ParticipantEntityNotFoundException(String message) { + + super(message); + } +} diff --git a/backend/src/main/java/eu/possiblex/portal/persistence/entity/exception/ParticipantEntityStateTransitionException.java b/backend/src/main/java/eu/possiblex/portal/persistence/entity/exception/ParticipantEntityStateTransitionException.java new file mode 100644 index 0000000..d512514 --- /dev/null +++ b/backend/src/main/java/eu/possiblex/portal/persistence/entity/exception/ParticipantEntityStateTransitionException.java @@ -0,0 +1,8 @@ +package eu.possiblex.portal.persistence.entity.exception; + +public class ParticipantEntityStateTransitionException extends RuntimeException { + public ParticipantEntityStateTransitionException(String message) { + + super(message); + } +} \ No newline at end of file diff --git a/backend/src/main/java/eu/possiblex/portal/utilities/ExceptionHandlingFilter.java b/backend/src/main/java/eu/possiblex/portal/utilities/ExceptionHandlingFilter.java deleted file mode 100644 index b82a41c..0000000 --- a/backend/src/main/java/eu/possiblex/portal/utilities/ExceptionHandlingFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -package eu.possiblex.portal.utilities; - -import jakarta.servlet.*; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Component; - -import java.io.IOException; - -@Component -@Slf4j -public class ExceptionHandlingFilter implements Filter { - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - - try { - chain.doFilter(request, response); - } catch (Exception e) { - if (isPossibleXException(e)) { - PossibleXException ex = getPossibleXException(e); - ((HttpServletResponse) response).setStatus(ex.getStatus().value()); - log.error("PossibleXException: ", ex); - } else { - ((HttpServletResponse) response).setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); - log.error("Exception: ", e); - } - response.getWriter().write(e.getMessage()); - } - } - - public boolean isPossibleXException(Throwable e) { - - PossibleXException pe = getPossibleXException(e); - return pe != null; - } - - public PossibleXException getPossibleXException(Throwable e) { - - while (e != null) { - if (e instanceof PossibleXException) { - return (PossibleXException) e; - } - e = e.getCause(); - } - return null; - } -} \ No newline at end of file diff --git a/backend/src/main/java/eu/possiblex/portal/utilities/PossibleXException.java b/backend/src/main/java/eu/possiblex/portal/utilities/PossibleXException.java deleted file mode 100644 index c45c14b..0000000 --- a/backend/src/main/java/eu/possiblex/portal/utilities/PossibleXException.java +++ /dev/null @@ -1,26 +0,0 @@ -package eu.possiblex.portal.utilities; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.http.HttpStatus; - -@Setter -@Getter -public class PossibleXException extends RuntimeException { - - private HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; - - public PossibleXException(String message, HttpStatus status) { - super(message); - this.status = status; - } - - public PossibleXException(String message) { - super(message); - } - - public PossibleXException(String message, HttpStatus status, Throwable cause) { - super(message, cause); - this.status = status; - } -} \ No newline at end of file diff --git a/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationModuleTest.java b/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationModuleTest.java new file mode 100644 index 0000000..759d8f0 --- /dev/null +++ b/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationModuleTest.java @@ -0,0 +1,200 @@ +package eu.possiblex.portal.application.boundary; + +import eu.possiblex.portal.application.entity.CreateRegistrationRequestTO; +import eu.possiblex.portal.application.entity.credentials.gx.datatypes.GxVcard; +import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalParticipantCredentialSubject; +import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalRegistrationNumberCredentialSubject; +import eu.possiblex.portal.application.entity.credentials.px.participants.PxParticipantExtensionCredentialSubject; +import eu.possiblex.portal.business.control.*; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ContextConfiguration(classes = { ParticipantRegistrationModuleTest.TestConfig.class }) +@Transactional +@AutoConfigureMockMvc +class ParticipantRegistrationModuleTest { + + @Autowired + private MockMvc mockMvc; + + @Test + void registerParticipantSuccess() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + } + + @Test + void registerParticipantRepetitionShouldTriggerConflict() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isConflict()); + + } + + @WithMockUser(username = "admin") + @Test + void registrationsShouldShowInList() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + String anotherName = "anotherName"; + CreateRegistrationRequestTO anotherTo = buildValidRegistrationRequest(); + anotherTo.getParticipantCs().setName(anotherName); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(anotherTo)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + MvcResult result = this.mockMvc.perform(get("/registration/request")).andDo(print()).andExpect(status().isOk()) + .andReturn(); + + String content = result.getResponse().getContentAsString(); + assertTrue(content.contains(to.getParticipantCs().getName())); + assertTrue(content.contains(anotherTo.getParticipantCs().getName())); + + } + + @WithMockUser(username = "admin") + @Test + void registrationsDeletedEntriesShouldNotShowInList() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + String anotherName = "another name"; + CreateRegistrationRequestTO anotherTo = buildValidRegistrationRequest(); + anotherTo.getParticipantCs().setName(anotherName); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(anotherTo)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + this.mockMvc.perform(delete("/registration/request/" + anotherTo.getParticipantCs().getName()).contentType( + MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + MvcResult result = this.mockMvc.perform(get("/registration/request")).andDo(print()).andExpect(status().isOk()) + .andReturn(); + + String content = result.getResponse().getContentAsString(); + assertFalse(content.contains(anotherTo.getParticipantCs().getName())); + assertTrue(content.contains(to.getParticipantCs().getName())); + + } + + @WithMockUser(username = "admin") + @Test + void registrationStateTransitionsSuccess() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + this.mockMvc.perform(post("/registration/request/" + to.getParticipantCs().getName() + "/reject").contentType( + MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + this.mockMvc.perform( + delete("/registration/request/" + to.getParticipantCs().getName()).contentType(MediaType.APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + this.mockMvc.perform(post("/registration/request/" + to.getParticipantCs().getName() + "/accept").contentType( + MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); + + MvcResult result = this.mockMvc.perform(get("/registration/request")).andDo(print()).andExpect(status().isOk()) + .andReturn(); + + String content = result.getResponse().getContentAsString(); + assertTrue(content.contains("COMPLETED")); + + } + + private CreateRegistrationRequestTO buildValidRegistrationRequest() { + + GxVcard address = new GxVcard(); + address.setStreetAddress("Example Street 123"); + address.setLocality("Berlin"); + address.setPostalCode("12345"); + address.setCountryCode("DE"); + address.setCountrySubdivisionCode("DE-BE"); + GxLegalParticipantCredentialSubject participantCs = new GxLegalParticipantCredentialSubject(); + participantCs.setName("name"); + participantCs.setLegalAddress(address); + participantCs.setHeadquarterAddress(address); + GxLegalRegistrationNumberCredentialSubject registrationNumberCs = new GxLegalRegistrationNumberCredentialSubject(); + registrationNumberCs.setLeiCode("894500MQZ65CN32S9A66"); + PxParticipantExtensionCredentialSubject participantExtensionCs = new PxParticipantExtensionCredentialSubject(); + participantExtensionCs.setMailAddress("example@example.com"); + + CreateRegistrationRequestTO to = new CreateRegistrationRequestTO(); + to.setParticipantCs(participantCs); + to.setRegistrationNumberCs(registrationNumberCs); + to.setParticipantExtensionCs(participantExtensionCs); + + return to; + } + + @TestConfiguration + static class TestConfig { + + @Bean + @Primary + public TechnicalFhCatalogClient technicalFhCatalogClient() { + + return new TechnicalFhCatalogClientFake(); + } + + @Bean + @Primary + public DidWebServiceApiClient didWebServiceApiClient() { + + return new DidWebServiceApiClientFake(); + } + + @Bean + @Primary + public OmejdnConnectorApiClient omejdnConnectorApiClient() { + + return new OmejdnConnectorApiClientFake(); + } + + @Bean + @Primary + public SdCreationWizardApiClient sdCreationWizardApiClient() { + + return new SdCreationWizardApiClientFake(); + } + + } +} diff --git a/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationRestApiTest.java b/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationRestApiTest.java index 15648b7..d3e622b 100644 --- a/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationRestApiTest.java +++ b/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantRegistrationRestApiTest.java @@ -1,10 +1,16 @@ package eu.possiblex.portal.application.boundary; import eu.possiblex.portal.application.configuration.AppConfigurer; +import eu.possiblex.portal.application.configuration.BoundaryExceptionHandler; import eu.possiblex.portal.application.control.ParticipantRegistrationRestApiMapper; import eu.possiblex.portal.application.entity.CreateRegistrationRequestTO; +import eu.possiblex.portal.application.entity.credentials.gx.datatypes.GxVcard; +import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalParticipantCredentialSubject; +import eu.possiblex.portal.application.entity.credentials.gx.participants.GxLegalRegistrationNumberCredentialSubject; +import eu.possiblex.portal.application.entity.credentials.px.participants.PxParticipantExtensionCredentialSubject; import eu.possiblex.portal.business.control.ParticipantRegistrationService; import eu.possiblex.portal.business.control.ParticipantRegistrationServiceFake; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mapstruct.factory.Mappers; import org.mockito.Mockito; @@ -16,7 +22,9 @@ import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -26,7 +34,7 @@ @WebMvcTest(ParticipantRegistrationRestApiImpl.class) @ContextConfiguration(classes = { ParticipantRegistrationRestApiTest.TestConfig.class, - ParticipantRegistrationRestApiImpl.class, AppConfigurer.class }) + ParticipantRegistrationRestApiImpl.class, BoundaryExceptionHandler.class, AppConfigurer.class }) class ParticipantRegistrationRestApiTest { @Autowired @@ -35,22 +43,105 @@ class ParticipantRegistrationRestApiTest { @Autowired private ParticipantRegistrationService participantRegistrationService; - @Test - void registerParticipant() throws Exception { + @BeforeEach + void setUp() { - CreateRegistrationRequestTO to = new CreateRegistrationRequestTO(); reset(participantRegistrationService); + } + + @Test + void registerParticipantSuccess() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()); verify(participantRegistrationService).registerParticipant(any()); } + @Test + void registerParticipantBadMail() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + to.getParticipantExtensionCs().setMailAddress("garbage"); + + MvcResult result = this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + + String content = result.getResponse().getContentAsString(); + assertTrue(content.contains("mailAddress")); + + } + + @Test + void registerParticipantNoRegistrationNumber() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + to.getRegistrationNumberCs().setLeiCode(""); + to.getRegistrationNumberCs().setEori(""); + to.getRegistrationNumberCs().setVatID(""); + + MvcResult result = this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + + String content = result.getResponse().getContentAsString(); + assertTrue(content.contains("registration number")); + + } + + @Test + void registerParticipantBadAddress() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + GxVcard address = to.getParticipantCs().getLegalAddress(); + address.setCountryCode("garbage"); + address.setCountrySubdivisionCode("garbage"); + to.getParticipantCs().setHeadquarterAddress(address); + to.getParticipantCs().setLegalAddress(address); + + MvcResult result = this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isBadRequest()).andReturn(); + + String content = result.getResponse().getContentAsString(); + assertTrue(content.contains("countryCode")); + assertTrue(content.contains("countrySubdivisionCode")); + } + + @Test + void registerParticipantConflict() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + to.getParticipantCs().setName(ParticipantRegistrationServiceFake.CONFLICT_NAME); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isConflict()); + } + + @Test + void registerParticipantProcessingFailed() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + to.getParticipantCs().setName(ParticipantRegistrationServiceFake.PROCESSING_ERROR_NAME); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isUnprocessableEntity()); + } + + @Test + void registerParticipantNonCompliant() throws Exception { + + CreateRegistrationRequestTO to = buildValidRegistrationRequest(); + to.getParticipantCs().setName(ParticipantRegistrationServiceFake.BAD_COMPLIANCE_NAME); + + this.mockMvc.perform(post("/registration/request").content(RestApiHelper.asJsonString(to)) + .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isUnprocessableEntity()); + } + @WithMockUser(username = "admin") @Test void getRegistrationRequests() throws Exception { - reset(participantRegistrationService); this.mockMvc.perform(get("/registration/request")).andDo(print()).andExpect(status().isOk()); verify(participantRegistrationService).getParticipantRegistrationRequests(any()); @@ -60,7 +151,6 @@ void getRegistrationRequests() throws Exception { @Test void getRegistrationRequestByDid() throws Exception { - reset(participantRegistrationService); this.mockMvc.perform(get("/registration/request/someDid")).andDo(print()).andExpect(status().isOk()); verify(participantRegistrationService).getParticipantRegistrationRequestByDid("someDid"); @@ -70,7 +160,6 @@ void getRegistrationRequestByDid() throws Exception { @Test void acceptRegistrationRequest() throws Exception { - reset(participantRegistrationService); this.mockMvc.perform(post("/registration/request/validId/accept")).andDo(print()).andExpect(status().isOk()); verify(participantRegistrationService).acceptRegistrationRequest("validId"); @@ -80,7 +169,6 @@ void acceptRegistrationRequest() throws Exception { @Test void rejectRegistrationRequest() throws Exception { - reset(participantRegistrationService); this.mockMvc.perform(post("/registration/request/validId/reject")).andDo(print()).andExpect(status().isOk()); verify(participantRegistrationService).rejectRegistrationRequest("validId"); @@ -90,12 +178,36 @@ void rejectRegistrationRequest() throws Exception { @Test void deleteRegistrationRequest() throws Exception { - reset(participantRegistrationService); this.mockMvc.perform(delete("/registration/request/validId")).andDo(print()).andExpect(status().isOk()); verify(participantRegistrationService).deleteRegistrationRequest("validId"); } + private CreateRegistrationRequestTO buildValidRegistrationRequest() { + + GxVcard address = new GxVcard(); + address.setStreetAddress("Example Street 123"); + address.setLocality("Berlin"); + address.setPostalCode("12345"); + address.setCountryCode("DE"); + address.setCountrySubdivisionCode("DE-BE"); + GxLegalParticipantCredentialSubject participantCs = new GxLegalParticipantCredentialSubject(); + participantCs.setName("name"); + participantCs.setLegalAddress(address); + participantCs.setHeadquarterAddress(address); + GxLegalRegistrationNumberCredentialSubject registrationNumberCs = new GxLegalRegistrationNumberCredentialSubject(); + registrationNumberCs.setLeiCode("894500MQZ65CN32S9A66"); + PxParticipantExtensionCredentialSubject participantExtensionCs = new PxParticipantExtensionCredentialSubject(); + participantExtensionCs.setMailAddress("example@example.com"); + + CreateRegistrationRequestTO to = new CreateRegistrationRequestTO(); + to.setParticipantCs(participantCs); + to.setRegistrationNumberCs(registrationNumberCs); + to.setParticipantExtensionCs(participantExtensionCs); + + return to; + } + @TestConfiguration static class TestConfig { @Bean diff --git a/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantShapeRestApiTest.java b/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantShapeRestApiTest.java index e470288..fd030ec 100644 --- a/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantShapeRestApiTest.java +++ b/backend/src/test/java/eu/possiblex/portal/application/boundary/ParticipantShapeRestApiTest.java @@ -3,6 +3,7 @@ import eu.possiblex.portal.application.configuration.AppConfigurer; import eu.possiblex.portal.business.control.SdCreationWizardApiService; import eu.possiblex.portal.business.control.SdCreationWizardApiServiceFake; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; @@ -13,13 +14,15 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.web.servlet.MockMvc; +import static org.mockito.Mockito.reset; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(ParticipantShapeRestApiImpl.class) -@ContextConfiguration(classes = { ParticipantShapeRestApiTest.TestConfig.class, ParticipantShapeRestApiImpl.class, AppConfigurer.class }) +@ContextConfiguration(classes = { ParticipantShapeRestApiTest.TestConfig.class, ParticipantShapeRestApiImpl.class, + AppConfigurer.class }) class ParticipantShapeRestApiTest { @Autowired SdCreationWizardApiService sdCreationWizardApiService; @@ -27,6 +30,12 @@ class ParticipantShapeRestApiTest { @Autowired private MockMvc mockMvc; + @BeforeEach + void setUp() { + + reset(sdCreationWizardApiService); + } + @Test void getGxLegalParticipantShape() throws Exception { @@ -43,6 +52,14 @@ void getGxLegalRegistrationNumberShape() throws Exception { .andExpect(jsonPath("$.someKey").value("someValue")); } + @Test + void getPxParticipantExtensionShape() throws Exception { + + this.mockMvc.perform(get("/shapes/px/participantextension").contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk()) + .andExpect(jsonPath("$.someKey").value("someValue")); + } + @TestConfiguration static class TestConfig { @Bean diff --git a/backend/src/test/java/eu/possiblex/portal/application/control/ParticipantRegistrationRestApiMapperTest.java b/backend/src/test/java/eu/possiblex/portal/application/control/ParticipantRegistrationRestApiMapperTest.java index b9505bc..2846526 100644 --- a/backend/src/test/java/eu/possiblex/portal/application/control/ParticipantRegistrationRestApiMapperTest.java +++ b/backend/src/test/java/eu/possiblex/portal/application/control/ParticipantRegistrationRestApiMapperTest.java @@ -12,37 +12,15 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; import static org.junit.jupiter.api.Assertions.assertEquals; @SpringBootTest -@ContextConfiguration(classes = { ParticipantRegistrationRestApiMapperTest.TestConfig.class, - ParticipantRegistrationRestApiMapper.class }) +@ContextConfiguration(classes = { ParticipantRegistrationRestApiMapperTest.TestConfig.class }) class ParticipantRegistrationRestApiMapperTest { - private final String participantId = "1234"; - - private final String participantName = "SomeOrga Inc."; - - private final String participantDescription = "This is an organization."; - - private final String participantAddrCountryCode = "DE"; - - private final String participantAddrCountrySubdivisionCode = "DE-BE"; - - private final String participantAddrCountryStreetAddress = "Some Street 123"; - - private final String participantAddrCountryLocality = "Berlin"; - - private final String participantAddrPostalCode = "12345"; - - private final String participantRegNumEori = "1234"; - - private final String participantRegNumVatID = "5678"; - - private final String participantRegNumLeiCode = "9012"; - @Autowired private ParticipantRegistrationRestApiMapper participantRegistrationRestApiMapper; @@ -62,35 +40,42 @@ void mapCredentialSubjectsToExtendedLegalParticipantCs() { request); // then - assertEquals(participantName, participantCs.getName()); - assertEquals(participantDescription, participantCs.getDescription()); + assertEquals(participant.getName(), participantCs.getName()); + assertEquals(participant.getDescription(), participantCs.getDescription()); - assertEquals(participantAddrCountryCode, participantCs.getHeadquarterAddress().getCountryCode()); - assertEquals(participantAddrCountrySubdivisionCode, + assertEquals(participant.getHeadquarterAddress().getCountryCode(), + participantCs.getHeadquarterAddress().getCountryCode()); + assertEquals(participant.getHeadquarterAddress().getCountrySubdivisionCode(), participantCs.getHeadquarterAddress().getCountrySubdivisionCode()); - assertEquals(participantAddrCountryStreetAddress, participantCs.getHeadquarterAddress().getStreetAddress()); - assertEquals(participantAddrCountryLocality, participantCs.getHeadquarterAddress().getLocality()); - assertEquals(participantAddrPostalCode, participantCs.getHeadquarterAddress().getPostalCode()); - - assertEquals(participantAddrCountryCode, participantCs.getLegalAddress().getCountryCode()); - assertEquals(participantAddrCountrySubdivisionCode, + assertEquals(participant.getHeadquarterAddress().getStreetAddress(), + participantCs.getHeadquarterAddress().getStreetAddress()); + assertEquals(participant.getHeadquarterAddress().getLocality(), + participantCs.getHeadquarterAddress().getLocality()); + assertEquals(participant.getHeadquarterAddress().getPostalCode(), + participantCs.getHeadquarterAddress().getPostalCode()); + + assertEquals(participant.getLegalAddress().getCountryCode(), participantCs.getLegalAddress().getCountryCode()); + assertEquals(participant.getLegalAddress().getCountrySubdivisionCode(), participantCs.getLegalAddress().getCountrySubdivisionCode()); - assertEquals(participantAddrCountryStreetAddress, participantCs.getLegalAddress().getStreetAddress()); - assertEquals(participantAddrCountryLocality, participantCs.getLegalAddress().getLocality()); - assertEquals(participantAddrPostalCode, participantCs.getLegalAddress().getPostalCode()); + assertEquals(participant.getLegalAddress().getStreetAddress(), + participantCs.getLegalAddress().getStreetAddress()); + assertEquals(participant.getLegalAddress().getLocality(), participantCs.getLegalAddress().getLocality()); + assertEquals(participant.getLegalAddress().getPostalCode(), participantCs.getLegalAddress().getPostalCode()); + + assertEquals(registrationNumber.getEori(), participantCs.getLegalRegistrationNumber().getEori()); + assertEquals(registrationNumber.getVatID(), participantCs.getLegalRegistrationNumber().getVatID()); + assertEquals(registrationNumber.getLeiCode(), participantCs.getLegalRegistrationNumber().getLeiCode()); - assertEquals(participantRegNumEori, participantCs.getLegalRegistrationNumber().getEori()); - assertEquals(participantRegNumVatID, participantCs.getLegalRegistrationNumber().getVatID()); - assertEquals(participantRegNumLeiCode, participantCs.getLegalRegistrationNumber().getLeiCode()); + assertEquals(extensionCs.getMailAddress(), participantCs.getMailAddress()); } private GxLegalRegistrationNumberCredentialSubject getGxLegalRegistrationNumberCredentialSubjectExample() { GxLegalRegistrationNumberCredentialSubject registrationNumber = new GxLegalRegistrationNumberCredentialSubject(); - registrationNumber.setEori(participantRegNumEori); - registrationNumber.setVatID(participantRegNumVatID); - registrationNumber.setLeiCode(participantRegNumLeiCode); + registrationNumber.setEori("1234"); + registrationNumber.setVatID("5678"); + registrationNumber.setLeiCode("9012"); return registrationNumber; } @@ -104,16 +89,16 @@ private PxParticipantExtensionCredentialSubject getPxParticipantExtensionCredent private GxLegalParticipantCredentialSubject getGxLegalParticipantCredentialSubjectExample() { GxVcard vcard = new GxVcard(); - vcard.setCountryCode(participantAddrCountryCode); - vcard.setCountrySubdivisionCode(participantAddrCountrySubdivisionCode); - vcard.setStreetAddress(participantAddrCountryStreetAddress); - vcard.setLocality(participantAddrCountryLocality); - vcard.setPostalCode(participantAddrPostalCode); + vcard.setCountryCode("DE"); + vcard.setCountrySubdivisionCode("DE-BE"); + vcard.setStreetAddress("Some Street 123"); + vcard.setLocality("Berlin"); + vcard.setPostalCode("12345"); GxLegalParticipantCredentialSubject participant = new GxLegalParticipantCredentialSubject(); - participant.setId(participantId); - participant.setName(participantName); - participant.setDescription(participantDescription); + participant.setId("1234"); + participant.setName("SomeOrga Inc."); + participant.setDescription("This is an organization."); participant.setHeadquarterAddress(vcard); participant.setLegalAddress(vcard); return participant; @@ -123,6 +108,7 @@ private GxLegalParticipantCredentialSubject getGxLegalParticipantCredentialSubje static class TestConfig { @Bean + @Primary public ParticipantRegistrationRestApiMapper participantCredentialMapper() { return Mappers.getMapper(ParticipantRegistrationRestApiMapper.class); diff --git a/backend/src/test/java/eu/possiblex/portal/business/control/DidWebServiceApiClientFake.java b/backend/src/test/java/eu/possiblex/portal/business/control/DidWebServiceApiClientFake.java index dec438f..1727a1b 100644 --- a/backend/src/test/java/eu/possiblex/portal/business/control/DidWebServiceApiClientFake.java +++ b/backend/src/test/java/eu/possiblex/portal/business/control/DidWebServiceApiClientFake.java @@ -3,6 +3,7 @@ import eu.possiblex.portal.business.entity.did.ParticipantDidBE; import eu.possiblex.portal.business.entity.did.ParticipantDidCreateRequestBE; import eu.possiblex.portal.business.entity.did.ParticipantDidUpdateRequestBE; +import org.springframework.web.reactive.function.client.WebClientResponseException; public class DidWebServiceApiClientFake implements DidWebServiceApiClient { @@ -10,12 +11,21 @@ public class DidWebServiceApiClientFake implements DidWebServiceApiClient { public static final String EXAMPLE_VERIFICATION_METHOD = "did:web:example.com:participant:1234#JWK2020-Example"; + public static final String FAILING_REQUEST_SUBJECT = "failDid"; + + public static final String FAILING_UPDATE_DID = "failUpdateDid"; + @Override public ParticipantDidBE generateDidWeb(ParticipantDidCreateRequestBE request) { + if (request.getSubject().equals(FAILING_REQUEST_SUBJECT)) { + throw WebClientResponseException.create(500, "did creation failed", null, null, null); + } + ParticipantDidBE be = new ParticipantDidBE(); - be.setDid(EXAMPLE_DID); + be.setDid(request.getSubject()); be.setVerificationMethod(EXAMPLE_VERIFICATION_METHOD); + return be; } @@ -26,6 +36,10 @@ public void deleteDidWeb(String did) { @Override public void updateDidWeb(ParticipantDidUpdateRequestBE request) { + + if (request.getDid().equals(FAILING_UPDATE_DID)) { + throw WebClientResponseException.create(500, "did update failed", null, null, null); + } // request worked } @@ -38,7 +52,8 @@ public String getDidDocument(String id) { "https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1" ], - "id": "did:web:example.com:participant:1234", + "id": \"""" + id + """ + ", "verificationMethod": [ { "@context": [ diff --git a/backend/src/test/java/eu/possiblex/portal/business/control/FhCatalogClientFake.java b/backend/src/test/java/eu/possiblex/portal/business/control/FhCatalogClientFake.java index c1bf9d1..df62b32 100644 --- a/backend/src/test/java/eu/possiblex/portal/business/control/FhCatalogClientFake.java +++ b/backend/src/test/java/eu/possiblex/portal/business/control/FhCatalogClientFake.java @@ -1,15 +1,29 @@ package eu.possiblex.portal.business.control; import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; +import eu.possiblex.portal.business.entity.exception.CatalogCommunicationException; +import eu.possiblex.portal.business.entity.exception.ParticipantComplianceException; import eu.possiblex.portal.business.entity.exception.ParticipantNotFoundException; import eu.possiblex.portal.business.entity.fh.FhCatalogIdResponse; public class FhCatalogClientFake implements FhCatalogClient { + public static final String FAILING_PARTICIPANT_ID = "failingParticipantId"; + + public static final String BAD_COMPLIANCE_PARTICIPANT_ID = "badComplianceParticipantId"; + @Override public FhCatalogIdResponse addParticipantToCatalog(PxExtendedLegalParticipantCredentialSubject cs) { - return new FhCatalogIdResponse("id"); + if (cs.getId().equals(FAILING_PARTICIPANT_ID)) { + throw new CatalogCommunicationException("Failing participant", new Exception()); + } + + if (cs.getId().equals(BAD_COMPLIANCE_PARTICIPANT_ID)) { + throw new ParticipantComplianceException("Bad compliance participant", new Exception()); + } + + return new FhCatalogIdResponse(cs.getId()); } @Override diff --git a/backend/src/test/java/eu/possiblex/portal/business/control/FhCatalogClientTest.java b/backend/src/test/java/eu/possiblex/portal/business/control/FhCatalogClientTest.java new file mode 100644 index 0000000..b702523 --- /dev/null +++ b/backend/src/test/java/eu/possiblex/portal/business/control/FhCatalogClientTest.java @@ -0,0 +1,120 @@ +package eu.possiblex.portal.business.control; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; +import eu.possiblex.portal.business.entity.exception.CatalogCommunicationException; +import eu.possiblex.portal.business.entity.exception.CatalogParsingException; +import eu.possiblex.portal.business.entity.exception.ParticipantComplianceException; +import eu.possiblex.portal.business.entity.exception.ParticipantNotFoundException; +import eu.possiblex.portal.business.entity.fh.FhCatalogIdResponse; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ContextConfiguration; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@ContextConfiguration(classes = { FhCatalogClientTest.TestConfig.class, FhCatalogClientImpl.class }) +class FhCatalogClientTest { + + @Autowired + private FhCatalogClient sut; + + @Test + void addParticipantToCatalogSuccess() { + + PxExtendedLegalParticipantCredentialSubject cs = new PxExtendedLegalParticipantCredentialSubject(); + cs.setId(TechnicalFhCatalogClientFake.VALID_ID); + FhCatalogIdResponse idResponse = sut.addParticipantToCatalog(cs); + assertNotNull(idResponse); + } + + @Test + void addParticipantToCatalogCommunicationFailed() { + + PxExtendedLegalParticipantCredentialSubject cs = new PxExtendedLegalParticipantCredentialSubject(); + cs.setId(TechnicalFhCatalogClientFake.BAD_COMMUNICATION_ID); + + assertThrows(CatalogCommunicationException.class, () -> sut.addParticipantToCatalog(cs)); + } + + @Test + void addParticipantToCatalogComplianceFailed() { + + PxExtendedLegalParticipantCredentialSubject cs = new PxExtendedLegalParticipantCredentialSubject(); + cs.setId(TechnicalFhCatalogClientFake.BAD_COMPLIANCE_ID); + + assertThrows(ParticipantComplianceException.class, () -> sut.addParticipantToCatalog(cs)); + } + + @Test + void getParticipantFromCatalogSuccess() { + + PxExtendedLegalParticipantCredentialSubject cs = sut.getParticipantFromCatalog( + TechnicalFhCatalogClientFake.VALID_ID); + assertNotNull(cs); + assertEquals(TechnicalFhCatalogClientFake.VALID_ID, cs.getId()); + } + + @Test + void getParticipantFromCatalogNotFound() { + + assertThrows(ParticipantNotFoundException.class, + () -> sut.getParticipantFromCatalog(TechnicalFhCatalogClientFake.MISSING_PARTICIPANT_ID)); + } + + @Test + void getParticipantFromCatalogParsingFailed() { + + assertThrows(CatalogParsingException.class, + () -> sut.getParticipantFromCatalog(TechnicalFhCatalogClientFake.BAD_PARSING_ID)); + } + + @Test + void getParticipantFromCatalogCommunicationFailed() { + + assertThrows(CatalogCommunicationException.class, + () -> sut.getParticipantFromCatalog(TechnicalFhCatalogClientFake.BAD_COMMUNICATION_ID)); + } + + @Test + void deleteParticipantFromCatalog() { + + assertDoesNotThrow(() -> sut.deleteParticipantFromCatalog(TechnicalFhCatalogClientFake.VALID_ID)); + } + + @Test + void deleteParticipantFromCatalogNotFound() { + + assertThrows(ParticipantNotFoundException.class, + () -> sut.deleteParticipantFromCatalog(TechnicalFhCatalogClientFake.MISSING_PARTICIPANT_ID)); + } + + @Test + void deleteParticipantFromCatalogCommunicationFailed() { + + assertThrows(CatalogCommunicationException.class, + () -> sut.deleteParticipantFromCatalog(TechnicalFhCatalogClientFake.BAD_COMMUNICATION_ID)); + } + + // Test-specific configuration to provide mocks + @TestConfiguration + static class TestConfig { + @Bean + public TechnicalFhCatalogClient technicalFhCatalogClient() { + + return Mockito.spy(new TechnicalFhCatalogClientFake()); + } + + @Bean + public ObjectMapper objectMapper() { + + return new ObjectMapper(); + } + + } +} diff --git a/backend/src/test/java/eu/possiblex/portal/business/control/OmejdnConnectorApiClientFake.java b/backend/src/test/java/eu/possiblex/portal/business/control/OmejdnConnectorApiClientFake.java index 3e80867..0533fb9 100644 --- a/backend/src/test/java/eu/possiblex/portal/business/control/OmejdnConnectorApiClientFake.java +++ b/backend/src/test/java/eu/possiblex/portal/business/control/OmejdnConnectorApiClientFake.java @@ -19,16 +19,24 @@ import eu.possiblex.portal.business.entity.daps.OmejdnConnectorCertificateBE; import eu.possiblex.portal.business.entity.daps.OmejdnConnectorCertificateRequest; import io.netty.util.internal.StringUtil; +import org.springframework.web.reactive.function.client.WebClientResponseException; import java.util.UUID; public class OmejdnConnectorApiClientFake implements OmejdnConnectorApiClient { + + public static final String FAILING_NAME = "failDaps"; + @Override public OmejdnConnectorCertificateBE addConnector(OmejdnConnectorCertificateRequest request) { + if (request.getClientName().equals(FAILING_NAME)) { + throw WebClientResponseException.create(500, "daps creation failed", null, null, null); + } + OmejdnConnectorCertificateBE dto = new OmejdnConnectorCertificateBE(); dto.setClientId("12:34:56"); - dto.setClientName((request == null || StringUtil.isNullOrEmpty(request.getClientName())) + dto.setClientName((StringUtil.isNullOrEmpty(request.getClientName())) ? UUID.randomUUID().toString() : request.getClientName()); dto.setKeystore("keystore123"); diff --git a/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceFake.java b/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceFake.java index 723eb4d..c1231f7 100644 --- a/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceFake.java +++ b/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceFake.java @@ -2,6 +2,9 @@ import eu.possiblex.portal.application.entity.RegistrationRequestEntryTO; import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; +import eu.possiblex.portal.business.entity.exception.ParticipantComplianceException; +import eu.possiblex.portal.business.entity.exception.RegistrationRequestConflictException; +import eu.possiblex.portal.business.entity.exception.RegistrationRequestProcessingException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -9,9 +12,24 @@ import java.util.Collections; public class ParticipantRegistrationServiceFake implements ParticipantRegistrationService { + + public static final String CONFLICT_NAME = "conflict"; + public static final String PROCESSING_ERROR_NAME = "processing-error"; + public static final String BAD_COMPLIANCE_NAME = "bad-compliance"; + + + @Override public void registerParticipant(PxExtendedLegalParticipantCredentialSubject cs) { - // request worked + + switch (cs.getName()) { + case CONFLICT_NAME -> throw new RegistrationRequestConflictException("Conflict"); + case PROCESSING_ERROR_NAME -> throw new RegistrationRequestProcessingException("Processing error"); + case BAD_COMPLIANCE_NAME -> throw new ParticipantComplianceException("Bad compliance"); + default -> { + // request worked + } + } } @Override diff --git a/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceMapperTest.java b/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceMapperTest.java index 6833587..64dc17b 100644 --- a/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceMapperTest.java +++ b/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceMapperTest.java @@ -20,30 +20,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; @SpringBootTest -@ContextConfiguration(classes = { ParticipantRegistrationServiceMapperTest.TestConfig.class, - ParticipantRegistrationServiceMapper.class }) +@ContextConfiguration(classes = { ParticipantRegistrationServiceMapperTest.TestConfig.class }) class ParticipantRegistrationServiceMapperTest { - private final String participantName = "SomeOrga Inc."; - - private final String participantDescription = "This is an organization."; - - private final String participantAddrCountryCode = "DE"; - - private final String participantAddrCountrySubdivisionCode = "DE-BE"; - - private final String participantAddrCountryStreetAddress = "Some Street 123"; - - private final String participantAddrCountryLocality = "Berlin"; - - private final String participantAddrPostalCode = "12345"; - - private final String participantRegNumEori = "1234"; - - private final String participantRegNumVatID = "5678"; - - private final String participantRegNumLeiCode = "9012"; - @Autowired private ParticipantRegistrationServiceMapper participantRegistrationServiceMapper; @@ -59,24 +38,33 @@ void possibleParticipantCsToRegistrationRequestListTO() { // then assertNotNull(to); - assertEquals(participantName, to.getName()); - assertEquals(participantDescription, to.getDescription()); + assertEquals(possibleParticipant.getName(), to.getName()); + assertEquals(possibleParticipant.getDescription(), to.getDescription()); - assertEquals(participantAddrCountryCode, to.getHeadquarterAddress().getCountryCode()); - assertEquals(participantAddrCountrySubdivisionCode, to.getHeadquarterAddress().getCountrySubdivisionCode()); - assertEquals(participantAddrCountryStreetAddress, to.getHeadquarterAddress().getStreetAddress()); - assertEquals(participantAddrCountryLocality, to.getHeadquarterAddress().getLocality()); - assertEquals(participantAddrPostalCode, to.getHeadquarterAddress().getPostalCode()); + assertEquals(possibleParticipant.getHeadquarterAddress().getCountryCode(), + to.getHeadquarterAddress().getCountryCode()); + assertEquals(possibleParticipant.getHeadquarterAddress().getCountrySubdivisionCode(), + to.getHeadquarterAddress().getCountrySubdivisionCode()); + assertEquals(possibleParticipant.getHeadquarterAddress().getStreetAddress(), + to.getHeadquarterAddress().getStreetAddress()); + assertEquals(possibleParticipant.getHeadquarterAddress().getLocality(), + to.getHeadquarterAddress().getLocality()); + assertEquals(possibleParticipant.getHeadquarterAddress().getPostalCode(), + to.getHeadquarterAddress().getPostalCode()); - assertEquals(participantAddrCountryCode, to.getLegalAddress().getCountryCode()); - assertEquals(participantAddrCountrySubdivisionCode, to.getLegalAddress().getCountrySubdivisionCode()); - assertEquals(participantAddrCountryStreetAddress, to.getLegalAddress().getStreetAddress()); - assertEquals(participantAddrCountryLocality, to.getLegalAddress().getLocality()); - assertEquals(participantAddrPostalCode, to.getLegalAddress().getPostalCode()); + assertEquals(possibleParticipant.getLegalAddress().getCountryCode(), to.getLegalAddress().getCountryCode()); + assertEquals(possibleParticipant.getLegalAddress().getCountrySubdivisionCode(), + to.getLegalAddress().getCountrySubdivisionCode()); + assertEquals(possibleParticipant.getLegalAddress().getStreetAddress(), to.getLegalAddress().getStreetAddress()); + assertEquals(possibleParticipant.getLegalAddress().getLocality(), to.getLegalAddress().getLocality()); + assertEquals(possibleParticipant.getLegalAddress().getPostalCode(), to.getLegalAddress().getPostalCode()); - assertEquals(participantRegNumEori, to.getLegalRegistrationNumber().getEori()); - assertEquals(participantRegNumVatID, to.getLegalRegistrationNumber().getVatID()); - assertEquals(participantRegNumLeiCode, to.getLegalRegistrationNumber().getLeiCode()); + assertEquals(possibleParticipant.getLegalRegistrationNumber().getEori(), + to.getLegalRegistrationNumber().getEori()); + assertEquals(possibleParticipant.getLegalRegistrationNumber().getVatID(), + to.getLegalRegistrationNumber().getVatID()); + assertEquals(possibleParticipant.getLegalRegistrationNumber().getLeiCode(), + to.getLegalRegistrationNumber().getLeiCode()); } @Test @@ -119,24 +107,24 @@ void registrationRequestToCredentialSubject() { private GxLegalRegistrationNumberCredentialSubject getGxLegalRegistrationNumberCredentialSubjectExample() { GxLegalRegistrationNumberCredentialSubject registrationNumber = new GxLegalRegistrationNumberCredentialSubject(); - registrationNumber.setEori(participantRegNumEori); - registrationNumber.setVatID(participantRegNumVatID); - registrationNumber.setLeiCode(participantRegNumLeiCode); + registrationNumber.setEori("1234"); + registrationNumber.setVatID("5678"); + registrationNumber.setLeiCode("9012"); return registrationNumber; } private ParticipantRegistrationRequestBE getParticipantRegistrationRequestExample() { GxVcard vcard = new GxVcard(); - vcard.setCountryCode(participantAddrCountryCode); - vcard.setCountrySubdivisionCode(participantAddrCountrySubdivisionCode); - vcard.setStreetAddress(participantAddrCountryStreetAddress); - vcard.setLocality(participantAddrCountryLocality); - vcard.setPostalCode(participantAddrPostalCode); + vcard.setCountryCode("DE"); + vcard.setCountrySubdivisionCode("DE-BE"); + vcard.setStreetAddress("Some Street 123"); + vcard.setLocality("Berlin"); + vcard.setPostalCode("12345"); return ParticipantRegistrationRequestBE.builder() .legalRegistrationNumber(getGxLegalRegistrationNumberCredentialSubjectExample()).legalAddress(vcard) - .headquarterAddress(vcard).description(participantDescription).name(participantName).build(); + .headquarterAddress(vcard).description("This is an organization.").name("SomeOrga Inc.").build(); } private ParticipantRegistrationRequestBE getParticipantRegistrationRequestCompletedExample() { diff --git a/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceTest.java b/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceTest.java index af221fb..d404d3a 100644 --- a/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceTest.java +++ b/backend/src/test/java/eu/possiblex/portal/business/control/ParticipantRegistrationServiceTest.java @@ -6,13 +6,17 @@ import eu.possiblex.portal.business.entity.credentials.px.GxNestedLegalRegistrationNumberCredentialSubject; import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; import eu.possiblex.portal.business.entity.daps.OmejdnConnectorCertificateBE; -import eu.possiblex.portal.business.entity.daps.OmejdnConnectorCertificateRequest; -import eu.possiblex.portal.business.entity.did.ParticipantDidCreateRequestBE; +import eu.possiblex.portal.business.entity.did.ParticipantDidBE; import eu.possiblex.portal.business.entity.exception.ParticipantComplianceException; +import eu.possiblex.portal.business.entity.exception.ParticipantNotFoundException; import eu.possiblex.portal.business.entity.exception.RegistrationRequestConflictException; +import eu.possiblex.portal.business.entity.exception.RegistrationRequestProcessingException; +import eu.possiblex.portal.business.entity.fh.FhCatalogIdResponse; import eu.possiblex.portal.persistence.control.ParticipantRegistrationEntityMapper; import eu.possiblex.portal.persistence.dao.ParticipantRegistrationRequestDAO; import eu.possiblex.portal.persistence.dao.ParticipantRegistrationRequestDAOFake; +import eu.possiblex.portal.testutilities.ResultCaptor; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mapstruct.factory.Mappers; import org.mockito.ArgumentCaptor; @@ -46,35 +50,32 @@ class ParticipantRegistrationServiceTest { private FhCatalogClient fhCatalogClient; @Autowired - private ParticipantRegistrationService participantRegistrationService; + private ParticipantRegistrationService sut; - @Test - void registerParticipant() { + @BeforeEach + void setUp() { reset(participantRegistrationRequestDao); reset(omejdnConnectorApiClient); reset(didWebServiceApiClient); reset(fhCatalogClient); + } + + @Test + void registerParticipantSuccess() { PxExtendedLegalParticipantCredentialSubject participant = getParticipantCs(); - participantRegistrationService.registerParticipant(participant); + sut.registerParticipant(participant); verify(participantRegistrationRequestDao).saveParticipantRegistrationRequest(any()); } @Test void registerParticipantAlreadyExists() { - reset(participantRegistrationRequestDao); - reset(omejdnConnectorApiClient); - reset(didWebServiceApiClient); - reset(fhCatalogClient); - PxExtendedLegalParticipantCredentialSubject participant = getParticipantCs(); participant.setName(ParticipantRegistrationRequestDAOFake.EXISTING_NAME); - assertThrows(RegistrationRequestConflictException.class, () -> { - participantRegistrationService.registerParticipant(participant); - }); + assertThrows(RegistrationRequestConflictException.class, () -> sut.registerParticipant(participant)); verify(participantRegistrationRequestDao).getRegistrationRequestByName( ParticipantRegistrationRequestDAOFake.EXISTING_NAME); @@ -82,90 +83,252 @@ void registerParticipantAlreadyExists() { } @Test - void getParticipantRegistrationRequests() { + void getParticipantRegistrationRequestsSingleItem() { - reset(participantRegistrationRequestDao); - reset(omejdnConnectorApiClient); - reset(didWebServiceApiClient); - reset(fhCatalogClient); - - Page to = participantRegistrationService.getParticipantRegistrationRequests( - PageRequest.of(0, 1)); + Page to = sut.getParticipantRegistrationRequests(PageRequest.of(0, 1)); assertEquals(1, to.getContent().size()); verify(participantRegistrationRequestDao).getRegistrationRequests(any()); } @Test - void getParticipantRegistrationRequestByDid() { + void getParticipantRegistrationRequestsFullPage() { - reset(participantRegistrationRequestDao); - reset(omejdnConnectorApiClient); - reset(didWebServiceApiClient); - reset(fhCatalogClient); + Page to = sut.getParticipantRegistrationRequests(PageRequest.of(0, 10)); + assertTrue(to.getContent().size() > 1); + + verify(participantRegistrationRequestDao).getRegistrationRequests(any()); + } + + @Test + void getParticipantRegistrationRequestByDidSuccess() { - RegistrationRequestEntryTO entry = participantRegistrationService.getParticipantRegistrationRequestByDid( - "validDid"); + RegistrationRequestEntryTO entry = sut.getParticipantRegistrationRequestByDid( + ParticipantRegistrationRequestDAOFake.EXISTING_DID); assertNotNull(entry); - verify(participantRegistrationRequestDao).getRegistrationRequestByDid("validDid"); + verify(participantRegistrationRequestDao).getRegistrationRequestByDid( + ParticipantRegistrationRequestDAOFake.EXISTING_DID); } @Test - void acceptRegistrationRequest() throws ParticipantComplianceException { + void getParticipantRegistrationRequestByDidUnknown() { - reset(participantRegistrationRequestDao); - reset(omejdnConnectorApiClient); - reset(didWebServiceApiClient); - reset(fhCatalogClient); + assertThrows(ParticipantNotFoundException.class, + () -> sut.getParticipantRegistrationRequestByDid(ParticipantRegistrationRequestDAOFake.NON_EXISTING_DID)); + verify(participantRegistrationRequestDao).getRegistrationRequestByDid( + ParticipantRegistrationRequestDAOFake.NON_EXISTING_DID); + } - PxExtendedLegalParticipantCredentialSubject participant = getParticipantCs(); - participantRegistrationService.registerParticipant(participant); + @Test + void acceptRegistrationRequestSuccess() { ArgumentCaptor certificateCaptor = ArgumentCaptor.forClass( OmejdnConnectorCertificateBE.class); - participantRegistrationService.acceptRegistrationRequest(ParticipantRegistrationRequestDAOFake.EXISTING_NAME); - verify(participantRegistrationRequestDao).acceptRegistrationRequest( - eq(ParticipantRegistrationRequestDAOFake.EXISTING_NAME)); - verify(participantRegistrationRequestDao).completeRegistrationRequest( - eq(ParticipantRegistrationRequestDAOFake.EXISTING_NAME), any(), any(), certificateCaptor.capture()); - verify(didWebServiceApiClient).generateDidWeb( - new ParticipantDidCreateRequestBE(ParticipantRegistrationRequestDAOFake.EXISTING_NAME)); + + sut.acceptRegistrationRequest(ParticipantRegistrationRequestDAOFake.EXISTING_NAME); + verify(participantRegistrationRequestDao).acceptRegistrationRequest(any()); + verify(didWebServiceApiClient).generateDidWeb(any()); verify(fhCatalogClient).addParticipantToCatalog(any()); - verify(omejdnConnectorApiClient).addConnector( - new OmejdnConnectorCertificateRequest(DidWebServiceApiClientFake.EXAMPLE_DID, - DidWebServiceApiClientFake.EXAMPLE_DID)); + verify(omejdnConnectorApiClient).addConnector(any()); verify(didWebServiceApiClient).updateDidWeb(any()); + verify(participantRegistrationRequestDao).completeRegistrationRequest(any(), any(), any(), + certificateCaptor.capture()); OmejdnConnectorCertificateBE certificate = certificateCaptor.getValue(); - assertEquals(DidWebServiceApiClientFake.EXAMPLE_DID, certificate.getClientName()); assertNotNull(certificate.getKeystore()); assertNotNull(certificate.getClientId()); assertNotNull(certificate.getPassword()); } @Test - void rejectRegistrationRequest() { + void acceptRegistrationRequestDidCreationFails() { - reset(participantRegistrationRequestDao); - reset(omejdnConnectorApiClient); - reset(didWebServiceApiClient); - reset(fhCatalogClient); + ResultCaptor didBEResultCaptor = new ResultCaptor<>(); + doAnswer(didBEResultCaptor).when(didWebServiceApiClient).generateDidWeb(any()); + + assertThrows(RegistrationRequestProcessingException.class, + () -> sut.acceptRegistrationRequest(DidWebServiceApiClientFake.FAILING_REQUEST_SUBJECT)); + + verify(participantRegistrationRequestDao).acceptRegistrationRequest(any()); + verify(didWebServiceApiClient).generateDidWeb(any()); + + verify(fhCatalogClient, times(0)).addParticipantToCatalog(any()); + verify(omejdnConnectorApiClient, times(0)).addConnector(any()); + verify(didWebServiceApiClient, times(0)).updateDidWeb(any()); + verify(participantRegistrationRequestDao, times(0)).completeRegistrationRequest(any(), any(), any(), any()); + } + + @Test + void acceptRegistrationRequestCatalogFails() { + + ResultCaptor didBEResultCaptor = new ResultCaptor<>(); + doAnswer(didBEResultCaptor).when(didWebServiceApiClient).generateDidWeb(any()); + + assertThrows(RegistrationRequestProcessingException.class, + () -> sut.acceptRegistrationRequest(FhCatalogClientFake.FAILING_PARTICIPANT_ID)); + + verify(participantRegistrationRequestDao).acceptRegistrationRequest(any()); + verify(didWebServiceApiClient).generateDidWeb(any()); + verify(fhCatalogClient).addParticipantToCatalog(any()); + + verify(omejdnConnectorApiClient, times(0)).addConnector(any()); + verify(didWebServiceApiClient, times(0)).updateDidWeb(any()); + verify(participantRegistrationRequestDao, times(0)).completeRegistrationRequest(any(), any(), any(), any()); + + verify(didWebServiceApiClient).deleteDidWeb(didBEResultCaptor.getResult().getDid()); + } + + @Test + void acceptRegistrationRequestBadCompliance() { + + ResultCaptor didBEResultCaptor = new ResultCaptor<>(); + doAnswer(didBEResultCaptor).when(didWebServiceApiClient).generateDidWeb(any()); + + assertThrows(ParticipantComplianceException.class, + () -> sut.acceptRegistrationRequest(FhCatalogClientFake.BAD_COMPLIANCE_PARTICIPANT_ID)); + + verify(participantRegistrationRequestDao).acceptRegistrationRequest(any()); + verify(didWebServiceApiClient).generateDidWeb(any()); + verify(fhCatalogClient).addParticipantToCatalog(any()); + + verify(omejdnConnectorApiClient, times(0)).addConnector(any()); + verify(didWebServiceApiClient, times(0)).updateDidWeb(any()); + verify(participantRegistrationRequestDao, times(0)).completeRegistrationRequest(any(), any(), any(), any()); + + verify(didWebServiceApiClient).deleteDidWeb(didBEResultCaptor.getResult().getDid()); + } + + @Test + void acceptRegistrationRequestDapsFails() { + + ResultCaptor didBEResultCaptor = new ResultCaptor<>(); + doAnswer(didBEResultCaptor).when(didWebServiceApiClient).generateDidWeb(any()); + ResultCaptor catalogIdResponseResultCaptor = new ResultCaptor<>(); + doAnswer(catalogIdResponseResultCaptor).when(fhCatalogClient).addParticipantToCatalog(any()); + + assertThrows(RegistrationRequestProcessingException.class, + () -> sut.acceptRegistrationRequest(OmejdnConnectorApiClientFake.FAILING_NAME)); + + verify(participantRegistrationRequestDao).acceptRegistrationRequest(any()); + verify(didWebServiceApiClient).generateDidWeb(any()); + verify(fhCatalogClient).addParticipantToCatalog(any()); + verify(omejdnConnectorApiClient).addConnector(any()); + + verify(didWebServiceApiClient, times(0)).updateDidWeb(any()); + verify(participantRegistrationRequestDao, times(0)).completeRegistrationRequest(any(), any(), any(), any()); + + verify(didWebServiceApiClient).deleteDidWeb(didBEResultCaptor.getResult().getDid()); + verify(fhCatalogClient).deleteParticipantFromCatalog(catalogIdResponseResultCaptor.getResult().getId()); + } + + @Test + void acceptRegistrationRequestDidUpdateFails() { + + ResultCaptor didBEResultCaptor = new ResultCaptor<>(); + doAnswer(didBEResultCaptor).when(didWebServiceApiClient).generateDidWeb(any()); + ResultCaptor catalogIdResponseResultCaptor = new ResultCaptor<>(); + doAnswer(catalogIdResponseResultCaptor).when(fhCatalogClient).addParticipantToCatalog(any()); + ResultCaptor certificateBEResultCaptor = new ResultCaptor<>(); + doAnswer(certificateBEResultCaptor).when(omejdnConnectorApiClient).addConnector(any()); + + assertThrows(RegistrationRequestProcessingException.class, + () -> sut.acceptRegistrationRequest(DidWebServiceApiClientFake.FAILING_UPDATE_DID)); + + verify(participantRegistrationRequestDao).acceptRegistrationRequest(any()); + verify(didWebServiceApiClient).generateDidWeb(any()); + verify(fhCatalogClient).addParticipantToCatalog(any()); + verify(omejdnConnectorApiClient).addConnector(any()); + verify(didWebServiceApiClient).updateDidWeb(any()); + + verify(participantRegistrationRequestDao, times(0)).completeRegistrationRequest(any(), any(), any(), any()); + + verify(didWebServiceApiClient).deleteDidWeb(didBEResultCaptor.getResult().getDid()); + verify(fhCatalogClient).deleteParticipantFromCatalog(catalogIdResponseResultCaptor.getResult().getId()); + verify(omejdnConnectorApiClient).deleteConnector(certificateBEResultCaptor.getResult().getClientId()); + } + + @Test + void acceptRegistrationRequestCompletionFails() { + + ResultCaptor didBEResultCaptor = new ResultCaptor<>(); + doAnswer(didBEResultCaptor).when(didWebServiceApiClient).generateDidWeb(any()); + ResultCaptor catalogIdResponseResultCaptor = new ResultCaptor<>(); + doAnswer(catalogIdResponseResultCaptor).when(fhCatalogClient).addParticipantToCatalog(any()); + ResultCaptor certificateBEResultCaptor = new ResultCaptor<>(); + doAnswer(certificateBEResultCaptor).when(omejdnConnectorApiClient).addConnector(any()); + + assertThrows(RegistrationRequestProcessingException.class, + () -> sut.acceptRegistrationRequest(ParticipantRegistrationRequestDAOFake.BAD_COMPLETION_NAME)); + + verify(participantRegistrationRequestDao).acceptRegistrationRequest(any()); + verify(didWebServiceApiClient).generateDidWeb(any()); + verify(fhCatalogClient).addParticipantToCatalog(any()); + verify(omejdnConnectorApiClient).addConnector(any()); + verify(didWebServiceApiClient).updateDidWeb(any()); + verify(participantRegistrationRequestDao).completeRegistrationRequest(any(), any(), any(), any()); + + verify(didWebServiceApiClient).deleteDidWeb(didBEResultCaptor.getResult().getDid()); + verify(fhCatalogClient).deleteParticipantFromCatalog(catalogIdResponseResultCaptor.getResult().getId()); + verify(omejdnConnectorApiClient).deleteConnector(certificateBEResultCaptor.getResult().getClientId()); + } + + @Test + void acceptRegistrationRequestNotFound() { - participantRegistrationService.rejectRegistrationRequest("validId"); - verify(participantRegistrationRequestDao).rejectRegistrationRequest("validId"); + assertThrows(ParticipantNotFoundException.class, + () -> sut.acceptRegistrationRequest(ParticipantRegistrationRequestDAOFake.NON_EXISTING_NAME)); + } + + @Test + void acceptRegistrationRequestBadTransition() { + + assertThrows(RegistrationRequestProcessingException.class, + () -> sut.acceptRegistrationRequest(ParticipantRegistrationRequestDAOFake.BAD_TRANSITION_NAME)); + } + + @Test + void rejectRegistrationRequestSuccess() { + + sut.rejectRegistrationRequest(ParticipantRegistrationRequestDAOFake.EXISTING_NAME); + verify(participantRegistrationRequestDao).rejectRegistrationRequest( + ParticipantRegistrationRequestDAOFake.EXISTING_NAME); + } + + @Test + void rejectRegistrationRequestNotFound() { + + assertThrows(ParticipantNotFoundException.class, + () -> sut.rejectRegistrationRequest(ParticipantRegistrationRequestDAOFake.NON_EXISTING_NAME)); + } + + @Test + void rejectRegistrationRequestBadTransition() { + + assertThrows(RegistrationRequestProcessingException.class, + () -> sut.rejectRegistrationRequest(ParticipantRegistrationRequestDAOFake.BAD_TRANSITION_NAME)); } @Test void deleteRegistrationRequest() { - reset(participantRegistrationRequestDao); - reset(omejdnConnectorApiClient); - reset(didWebServiceApiClient); - reset(fhCatalogClient); + sut.deleteRegistrationRequest(ParticipantRegistrationRequestDAOFake.EXISTING_NAME); + verify(participantRegistrationRequestDao).deleteRegistrationRequest( + ParticipantRegistrationRequestDAOFake.EXISTING_NAME); + } + + @Test + void deleteRegistrationRequestNotFound() { + + assertThrows(ParticipantNotFoundException.class, + () -> sut.deleteRegistrationRequest(ParticipantRegistrationRequestDAOFake.NON_EXISTING_NAME)); + } + + @Test + void deleteRegistrationRequestBadTransition() { - participantRegistrationService.deleteRegistrationRequest("validId"); - verify(participantRegistrationRequestDao).deleteRegistrationRequest("validId"); + assertThrows(RegistrationRequestProcessingException.class, + () -> sut.deleteRegistrationRequest(ParticipantRegistrationRequestDAOFake.BAD_TRANSITION_NAME)); } private PxExtendedLegalParticipantCredentialSubject getParticipantCs() { @@ -173,10 +336,12 @@ private PxExtendedLegalParticipantCredentialSubject getParticipantCs() { ParticipantRegistrationRequestBE be = ParticipantRegistrationRequestDAOFake.getExampleParticipant(); GxLegalRegistrationNumberCredentialSubject regNum = be.getLegalRegistrationNumber(); - return PxExtendedLegalParticipantCredentialSubject.builder().id("validId").legalRegistrationNumber( + return PxExtendedLegalParticipantCredentialSubject.builder() + .id(ParticipantRegistrationRequestDAOFake.NON_EXISTING_NAME).legalRegistrationNumber( new GxNestedLegalRegistrationNumberCredentialSubject(regNum.getEori(), regNum.getVatID(), - regNum.getLeiCode())).headquarterAddress(be.getHeadquarterAddress()).legalAddress(be.getLegalAddress()) - .name(be.getName()).description(be.getDescription()).mailAddress("example@address.com").build(); + regNum.getLeiCode())).headquarterAddress(be.getHeadquarterAddress()) + .legalAddress(be.getLegalAddress()).name(ParticipantRegistrationRequestDAOFake.NON_EXISTING_NAME) + .description(be.getDescription()).mailAddress("example@address.com").build(); } // Test-specific configuration to provide mocks diff --git a/backend/src/test/java/eu/possiblex/portal/business/control/SdCreationWizardApiServiceTest.java b/backend/src/test/java/eu/possiblex/portal/business/control/SdCreationWizardApiServiceTest.java index 356bd80..38ac7dd 100644 --- a/backend/src/test/java/eu/possiblex/portal/business/control/SdCreationWizardApiServiceTest.java +++ b/backend/src/test/java/eu/possiblex/portal/business/control/SdCreationWizardApiServiceTest.java @@ -20,12 +20,12 @@ SdCreationWizardApiServiceImpl.class }) class SdCreationWizardApiServiceTest { @Autowired - private SdCreationWizardApiService sdCreationWizardApiService; + private SdCreationWizardApiService sut; @Test void getShapesByExistingEcosystem() { - Map> shapes = sdCreationWizardApiService.getShapesByEcosystem("ecosystem1"); + Map> shapes = sut.getShapesByEcosystem("ecosystem1"); assertNotNull(shapes); } @@ -33,14 +33,14 @@ void getShapesByExistingEcosystem() { void getShapesByNonExistentEcosystem() { WebClientResponseException e = assertThrows(WebClientResponseException.class, - () -> sdCreationWizardApiService.getShapesByEcosystem("missing")); + () -> sut.getShapesByEcosystem("missing")); assertEquals(HttpStatus.NOT_FOUND, e.getStatusCode()); } @Test void getParticipantShapesByExistingEcosystem() { - List shapes = sdCreationWizardApiService.getParticipantShapesByEcosystem("ecosystem1"); + List shapes = sut.getParticipantShapesByEcosystem("ecosystem1"); assertNotNull(shapes); assertEquals(2, shapes.size()); } @@ -49,14 +49,14 @@ void getParticipantShapesByExistingEcosystem() { void getParticipantShapesByNonExistentEcosystem() { WebClientResponseException e = assertThrows(WebClientResponseException.class, - () -> sdCreationWizardApiService.getParticipantShapesByEcosystem("missing")); + () -> sut.getParticipantShapesByEcosystem("missing")); assertEquals(HttpStatus.NOT_FOUND, e.getStatusCode()); } @Test void getShapeFileExisting() { - String json = sdCreationWizardApiService.getShapeByName("ecosystem1", "Legalparticipant1.json"); + String json = sut.getShapeByName("ecosystem1", "Legalparticipant1.json"); assertNotNull(json); } @@ -64,7 +64,7 @@ void getShapeFileExisting() { void getShapeFileNonExistent() { WebClientResponseException e = assertThrows(WebClientResponseException.class, - () -> sdCreationWizardApiService.getShapeByName("ecosystem1", "missing")); + () -> sut.getShapeByName("ecosystem1", "missing")); assertEquals(HttpStatus.NOT_FOUND, e.getStatusCode()); } diff --git a/backend/src/test/java/eu/possiblex/portal/business/control/TechnicalFhCatalogClientFake.java b/backend/src/test/java/eu/possiblex/portal/business/control/TechnicalFhCatalogClientFake.java new file mode 100644 index 0000000..3474068 --- /dev/null +++ b/backend/src/test/java/eu/possiblex/portal/business/control/TechnicalFhCatalogClientFake.java @@ -0,0 +1,131 @@ +package eu.possiblex.portal.business.control; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; +import eu.possiblex.portal.business.entity.fh.FhCatalogIdResponse; +import org.springframework.http.HttpHeaders; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +public class TechnicalFhCatalogClientFake implements TechnicalFhCatalogClient { + + public static final String VALID_ID = "some-id"; + + public static final String MISSING_PARTICIPANT_ID = "missing"; + + public static final String BAD_COMMUNICATION_ID = "badcommunication"; + + public static final String BAD_COMPLIANCE_ID = "badcompliance"; + + public static final String BAD_PARSING_ID = "badparsing"; + + @Override + public FhCatalogIdResponse addParticipantToFhCatalog(PxExtendedLegalParticipantCredentialSubject participantCs, + String verificationMethod) { + + if (participantCs.getId().equals(BAD_COMMUNICATION_ID)) { + throw WebClientResponseException.create(500, "Some other error", null, null, null); + } + if (participantCs.getId().equals(BAD_COMPLIANCE_ID)) { + String jsonBody = "{\"error\":\"compliance error\"}"; + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", "application/json"); + WebClientResponseException responseException = WebClientResponseException.create(422, "compliance error", + headers, jsonBody.getBytes(), null); + + responseException.setBodyDecodeFunction(bytes -> { + try { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readTree(jsonBody); + } catch (Exception e) { + throw new RuntimeException("Failed to decode JSON", e); + } + }); + + throw responseException; + } + + return new FhCatalogIdResponse(participantCs.getId()); + } + + @Override + public String getParticipantFromCatalog(String participantId) { + + switch (participantId) { + case MISSING_PARTICIPANT_ID -> + throw WebClientResponseException.create(404, "Participant not found", null, null, null); + case BAD_COMMUNICATION_ID -> + throw WebClientResponseException.create(500, "Some other error", null, null, null); + case BAD_PARSING_ID -> { + return "invalid json"; + } + default -> { + return """ + { + "@graph": [ + { + "@id": "_:b0", + "gx:countrySubdivisionCode": "DE-BE", + "vcard:locality": "Berlin", + "vcard:street-address": "Example Street 123", + "vcard:postal-code": "12345", + "gx:countryCode": "DE" + }, + { + "@id": "_:b1", + "gx:leiCode": "894500MQZ65CN32S9A66" + }, + { + "@id": \"""" + participantId + """ + ", + "http://w3id.org/gaia-x/possible-x#mailAddress": "example@example.com", + "gx:legalRegistrationNumber": { + "@id": "_:b1" + }, + "@type": [ + "http://w3id.org/gaia-x/possible-x#PossibleXLegalParticipantExtension", + "gx:LegalParticipant" + ], + "schema:name": "Example Org", + "gx:legalAddress": { + "@id": "_:b0" + }, + "gx:headquarterAddress": { + "@id": "_:b2" + }, + "schema:description": "Some description" + }, + { + "@id": "_:b2", + "gx:countrySubdivisionCode": "DE-BE", + "vcard:locality": "Berlin", + "vcard:street-address": "Example Street 123", + "vcard:postal-code": "12345", + "gx:countryCode": "DE" + } + ], + "@context": { + "schema": "https://schema.org/", + "gx": "https://w3id.org/gaia-x/development#", + "vcard": "http://www.w3.org/2006/vcard/ns#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + } + } + """; + } + } + } + + @Override + public void deleteParticipantFromCatalog(String participantId) { + + if (participantId.equals(MISSING_PARTICIPANT_ID)) { + throw WebClientResponseException.create(404, "Participant not found", null, null, null); + } + + if (participantId.equals(BAD_COMMUNICATION_ID)) { + throw WebClientResponseException.create(500, "Some other error", null, null, null); + } + + // success + } +} diff --git a/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationEntityMapperTest.java b/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationEntityMapperTest.java new file mode 100644 index 0000000..fd9bab8 --- /dev/null +++ b/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationEntityMapperTest.java @@ -0,0 +1,239 @@ +package eu.possiblex.portal.persistence.dao; + +import eu.possiblex.portal.application.entity.credentials.gx.datatypes.GxVcard; +import eu.possiblex.portal.business.entity.ParticipantRegistrationRequestBE; +import eu.possiblex.portal.business.entity.credentials.px.GxNestedLegalRegistrationNumberCredentialSubject; +import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; +import eu.possiblex.portal.business.entity.daps.OmejdnConnectorCertificateBE; +import eu.possiblex.portal.persistence.control.ParticipantRegistrationEntityMapper; +import eu.possiblex.portal.persistence.entity.*; +import eu.possiblex.portal.persistence.entity.daps.OmejdnConnectorCertificateEntity; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ContextConfiguration; + +import java.time.Instant; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@ContextConfiguration(classes = { ParticipantRegistrationEntityMapperTest.TestConfig.class, + ParticipantRegistrationEntityMapper.class }) +class ParticipantRegistrationEntityMapperTest { + + @Autowired + private ParticipantRegistrationEntityMapper participantRegistrationServiceMapper; + + @Test + void mapParticipantCredentialSubjectToEntity() { + // given + PxExtendedLegalParticipantCredentialSubject cs = getExampleParticipantCs(); + // when + ParticipantRegistrationRequestEntity entity = participantRegistrationServiceMapper.pxExtendedLegalParticipantCsToNewEntity( + cs); + // then + assertNotNull(entity); + assertNull(entity.getId()); + assertNull(entity.getCreatedAt()); + assertNull(entity.getUpdatedAt()); + assertNull(entity.getOmejdnConnectorCertificate()); + assertNull(entity.getDidData()); + assertNull(entity.getVpLink()); + assertNull(entity.getLegalRegistrationNumber().getId()); + assertNull(entity.getHeadquarterAddress().getId()); + assertNull(entity.getLegalAddress().getId()); + + assertEquals(RequestStatus.NEW, entity.getStatus()); + assertEquals(cs.getName(), entity.getName()); + assertEquals(cs.getDescription(), entity.getDescription()); + assertEquals(cs.getMailAddress(), entity.getEmailAddress()); + + assertEquals(cs.getLegalAddress().getCountryCode(), entity.getLegalAddress().getCountryCode()); + assertEquals(cs.getLegalAddress().getCountrySubdivisionCode(), + entity.getLegalAddress().getCountrySubdivisionCode()); + assertEquals(cs.getLegalAddress().getStreetAddress(), entity.getLegalAddress().getStreetAddress()); + assertEquals(cs.getLegalAddress().getLocality(), entity.getLegalAddress().getLocality()); + assertEquals(cs.getLegalAddress().getPostalCode(), entity.getLegalAddress().getPostalCode()); + + assertEquals(cs.getHeadquarterAddress().getCountryCode(), entity.getHeadquarterAddress().getCountryCode()); + assertEquals(cs.getHeadquarterAddress().getCountrySubdivisionCode(), + entity.getHeadquarterAddress().getCountrySubdivisionCode()); + assertEquals(cs.getHeadquarterAddress().getStreetAddress(), entity.getHeadquarterAddress().getStreetAddress()); + assertEquals(cs.getHeadquarterAddress().getLocality(), entity.getHeadquarterAddress().getLocality()); + assertEquals(cs.getHeadquarterAddress().getPostalCode(), entity.getHeadquarterAddress().getPostalCode()); + + assertEquals(cs.getLegalRegistrationNumber().getEori(), entity.getLegalRegistrationNumber().getEori()); + assertEquals(cs.getLegalRegistrationNumber().getVatID(), entity.getLegalRegistrationNumber().getVatID()); + assertEquals(cs.getLegalRegistrationNumber().getLeiCode(), entity.getLegalRegistrationNumber().getLeiCode()); + } + + @Test + void mapParticipantEntityToParticipantRegistrationRequestBe() { + + // given + ParticipantRegistrationRequestEntity entity = getExampleParticipantEntity(); + + // when + ParticipantRegistrationRequestBE be = participantRegistrationServiceMapper.entityToParticipantRegistrationRequestBe( + entity); + + // then + assertNotNull(be); + assertEquals(entity.getName(), be.getName()); + assertEquals(entity.getDescription(), be.getDescription()); + assertEquals(entity.getEmailAddress(), be.getEmailAddress()); + assertEquals(entity.getStatus().name(), be.getStatus().name()); + assertEquals(entity.getVpLink(), be.getVpLink()); + + assertEquals(entity.getLegalRegistrationNumber().getEori(), be.getLegalRegistrationNumber().getEori()); + assertEquals(entity.getLegalRegistrationNumber().getVatID(), be.getLegalRegistrationNumber().getVatID()); + assertEquals(entity.getLegalRegistrationNumber().getLeiCode(), be.getLegalRegistrationNumber().getLeiCode()); + + assertEquals(entity.getLegalAddress().getCountryCode(), be.getLegalAddress().getCountryCode()); + assertEquals(entity.getLegalAddress().getCountrySubdivisionCode(), + be.getLegalAddress().getCountrySubdivisionCode()); + assertEquals(entity.getLegalAddress().getStreetAddress(), be.getLegalAddress().getStreetAddress()); + assertEquals(entity.getLegalAddress().getLocality(), be.getLegalAddress().getLocality()); + assertEquals(entity.getLegalAddress().getPostalCode(), be.getLegalAddress().getPostalCode()); + + assertEquals(entity.getHeadquarterAddress().getCountryCode(), be.getHeadquarterAddress().getCountryCode()); + assertEquals(entity.getHeadquarterAddress().getCountrySubdivisionCode(), + be.getHeadquarterAddress().getCountrySubdivisionCode()); + assertEquals(entity.getHeadquarterAddress().getStreetAddress(), be.getHeadquarterAddress().getStreetAddress()); + assertEquals(entity.getHeadquarterAddress().getLocality(), be.getHeadquarterAddress().getLocality()); + assertEquals(entity.getHeadquarterAddress().getPostalCode(), be.getHeadquarterAddress().getPostalCode()); + + assertEquals(entity.getOmejdnConnectorCertificate().getClientId(), + be.getOmejdnConnectorCertificate().getClientId()); + assertEquals(entity.getOmejdnConnectorCertificate().getClientName(), + be.getOmejdnConnectorCertificate().getClientName()); + assertEquals(entity.getOmejdnConnectorCertificate().getKeystore(), + be.getOmejdnConnectorCertificate().getKeystore()); + assertEquals(entity.getOmejdnConnectorCertificate().getPassword(), + be.getOmejdnConnectorCertificate().getPassword()); + assertEquals(entity.getOmejdnConnectorCertificate().getScope(), be.getOmejdnConnectorCertificate().getScope()); + + assertEquals(entity.getDidData().getDid(), be.getDidData().getDid()); + assertEquals(entity.getDidData().getVerificationMethod(), be.getDidData().getVerificationMethod()); + + } + + @Test + void mapCertificateBeToEntity() { + // given + OmejdnConnectorCertificateBE certificate = getExampleCertificateBe(); + + // when + OmejdnConnectorCertificateEntity entity = participantRegistrationServiceMapper.omejdnConnectorCertificateBEToOmejdnConnectorCertificateEntity( + certificate); + + // then + assertNotNull(entity); + assertNull(entity.getId()); + assertEquals(certificate.getClientId(), entity.getClientId()); + assertEquals(certificate.getClientName(), entity.getClientName()); + assertEquals(certificate.getScope(), entity.getScope()); + assertEquals(certificate.getKeystore(), entity.getKeystore()); + assertEquals(certificate.getPassword(), entity.getPassword()); + } + + private PxExtendedLegalParticipantCredentialSubject getExampleParticipantCs() { + + GxVcard address = new GxVcard(); + address.setStreetAddress("Example Street 123"); + address.setLocality("Berlin"); + address.setPostalCode("12345"); + address.setCountryCode("DE"); + address.setCountrySubdivisionCode("DE-BE"); + + GxNestedLegalRegistrationNumberCredentialSubject registrationNumber = new GxNestedLegalRegistrationNumberCredentialSubject(); + registrationNumber.setEori("EORI"); + registrationNumber.setVatID("VATID"); + registrationNumber.setLeiCode("LEI"); + + PxExtendedLegalParticipantCredentialSubject cs = new PxExtendedLegalParticipantCredentialSubject(); + cs.setId("id"); + cs.setName("name"); + cs.setDescription("description"); + cs.setMailAddress("mailAddress"); + cs.setLegalRegistrationNumber(registrationNumber); + cs.setLegalAddress(address); + cs.setHeadquarterAddress(address); + + return cs; + } + + private ParticipantRegistrationRequestEntity getExampleParticipantEntity() { + + VcardEntity address = new VcardEntity(); + address.setStreetAddress("Example Street 123"); + address.setLocality("Berlin"); + address.setPostalCode("12345"); + address.setCountryCode("DE"); + address.setCountrySubdivisionCode("DE-BE"); + + RegistrationNumberEntity registrationNumber = new RegistrationNumberEntity(); + registrationNumber.setId(111L); + registrationNumber.setEori("EORI"); + registrationNumber.setVatID("VATID"); + registrationNumber.setLeiCode("LEI"); + + OmejdnConnectorCertificateEntity certificate = new OmejdnConnectorCertificateEntity(); + certificate.setId(222L); + certificate.setClientId("11:22:33"); + certificate.setClientName("clientName"); + certificate.setScope("scope"); + certificate.setKeystore("keystore"); + certificate.setPassword("password"); + + DidDataEntity didData = new DidDataEntity(); + didData.setId(333L); + didData.setDid("did"); + didData.setVerificationMethod("verificationMethod"); + + ParticipantRegistrationRequestEntity entity = new ParticipantRegistrationRequestEntity(); + entity.setId(1234L); + entity.setCreatedAt(Instant.now()); + entity.setUpdatedAt(Instant.now()); + entity.setName("name"); + entity.setDescription("description"); + entity.setEmailAddress("mailAddress"); + entity.setLegalRegistrationNumber(registrationNumber); + entity.setLegalAddress(address); + entity.setHeadquarterAddress(address); + + entity.setOmejdnConnectorCertificate(certificate); + entity.setDidData(didData); + + entity.setStatus(RequestStatus.COMPLETED); + entity.setVpLink("vpLink"); + + return entity; + } + + private OmejdnConnectorCertificateBE getExampleCertificateBe() { + + OmejdnConnectorCertificateBE certificate = new OmejdnConnectorCertificateBE(); + certificate.setClientId("11:22:33"); + certificate.setClientName("clientName"); + certificate.setScope("scope"); + certificate.setKeystore("keystore"); + certificate.setPassword("password"); + return certificate; + } + + @TestConfiguration + static class TestConfig { + + @Bean + public ParticipantRegistrationEntityMapper participantRegistrationEntityMapper() { + + return Mappers.getMapper(ParticipantRegistrationEntityMapper.class); + } + + } +} \ No newline at end of file diff --git a/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOFake.java b/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOFake.java index cbb906e..56a32dc 100644 --- a/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOFake.java +++ b/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOFake.java @@ -7,16 +7,29 @@ import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; import eu.possiblex.portal.business.entity.daps.OmejdnConnectorCertificateBE; import eu.possiblex.portal.business.entity.did.ParticipantDidBE; +import eu.possiblex.portal.persistence.entity.exception.ParticipantEntityNotFoundException; +import eu.possiblex.portal.persistence.entity.exception.ParticipantEntityStateTransitionException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import java.util.ArrayList; import java.util.List; public class ParticipantRegistrationRequestDAOFake implements ParticipantRegistrationRequestDAO { public static final String EXISTING_NAME = "existingName"; + public static final String EXISTING_DID = "did:web:existingDid.me"; + + public static final String NON_EXISTING_DID = "nonExistingDid"; + + public static final String NON_EXISTING_NAME = "nonExistingName"; + + public static final String BAD_TRANSITION_NAME = "badTransitionName"; + + public static final String BAD_COMPLETION_NAME = "badCompletionName"; + public static ParticipantRegistrationRequestBE getExampleParticipant() { GxVcard vcard = new GxVcard(); @@ -46,13 +59,31 @@ public static ParticipantRegistrationRequestBE getExampleParticipant() { @Override public Page getRegistrationRequests(Pageable pageable) { - return new PageImpl<>(List.of(getExampleParticipant())); + ParticipantRegistrationRequestBE participant1 = getExampleParticipant(); + ParticipantRegistrationRequestBE participant2 = getExampleParticipant(); + participant2.setName("anotherName"); + + List beList = List.of(participant1, participant2); + List responseList = new ArrayList<>(); + + for (int i = 0; i < Math.min(pageable.getPageSize(), beList.size()); i++) { + responseList.add(beList.get(i)); + } + + return new PageImpl<>(responseList); } @Override public ParticipantRegistrationRequestBE getRegistrationRequestByDid(String did) { - return getExampleParticipant(); + if (did.equals(NON_EXISTING_DID)) { + return null; + } + + ParticipantRegistrationRequestBE be = getExampleParticipant(); + be.getDidData().setDid(did); + + return be; } @Override @@ -62,32 +93,69 @@ public void saveParticipantRegistrationRequest(PxExtendedLegalParticipantCredent @Override public void acceptRegistrationRequest(String id) { + + if (id.equals(NON_EXISTING_NAME)) { + throw new ParticipantEntityNotFoundException("Participant not found"); + } + + if (id.equals(BAD_TRANSITION_NAME)) { + throw new ParticipantEntityStateTransitionException("Bad Transition"); + } + // request worked } @Override public void rejectRegistrationRequest(String id) { + + if (id.equals(NON_EXISTING_NAME)) { + throw new ParticipantEntityNotFoundException("Participant not found"); + } + + if (id.equals(BAD_TRANSITION_NAME)) { + throw new ParticipantEntityStateTransitionException("Bad Transition"); + } + // request worked } @Override public void deleteRegistrationRequest(String id) { + + if (id.equals(NON_EXISTING_NAME)) { + throw new ParticipantEntityNotFoundException("Participant not found"); + } + + if (id.equals(BAD_TRANSITION_NAME)) { + throw new ParticipantEntityStateTransitionException("Bad Transition"); + } + // request worked } @Override public void completeRegistrationRequest(String id, ParticipantDidBE did, String vpLink, OmejdnConnectorCertificateBE certificate) { + + if (id.equals(NON_EXISTING_NAME)) { + throw new ParticipantEntityNotFoundException("Participant not found"); + } + + if (id.equals(BAD_TRANSITION_NAME) || id.equals(BAD_COMPLETION_NAME)) { + throw new ParticipantEntityStateTransitionException("Bad Transition"); + } + // request worked } @Override public ParticipantRegistrationRequestBE getRegistrationRequestByName(String name) { - if (name.equals(EXISTING_NAME)) { - return getExampleParticipant(); - } else { + if (name.equals(NON_EXISTING_NAME)) { return null; } + + return getExampleParticipant(); + } } diff --git a/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOTest.java b/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOTest.java index 587428f..cfbca21 100644 --- a/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOTest.java +++ b/backend/src/test/java/eu/possiblex/portal/persistence/dao/ParticipantRegistrationRequestDAOTest.java @@ -1,5 +1,6 @@ package eu.possiblex.portal.persistence.dao; +import eu.possiblex.portal.PortalApplication; import eu.possiblex.portal.application.entity.credentials.gx.datatypes.GxVcard; import eu.possiblex.portal.business.entity.ParticipantRegistrationRequestBE; import eu.possiblex.portal.business.entity.RequestStatus; @@ -7,31 +8,48 @@ import eu.possiblex.portal.business.entity.credentials.px.PxExtendedLegalParticipantCredentialSubject; import eu.possiblex.portal.business.entity.daps.OmejdnConnectorCertificateBE; import eu.possiblex.portal.business.entity.did.ParticipantDidBE; +import eu.possiblex.portal.persistence.entity.exception.ParticipantEntityNotFoundException; +import eu.possiblex.portal.persistence.entity.exception.ParticipantEntityStateTransitionException; import jakarta.transaction.Transactional; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @SpringBootTest @TestPropertySource(properties = { "version.no = thisistheversion", "version.date = 21.03.2022" }) +@ContextConfiguration(classes = { ParticipantRegistrationRequestDAOImpl.class, PortalApplication.class }) @Transactional class ParticipantRegistrationRequestDAOTest { + private static final String EXAMPLE_PARTICIPANT_NAME = "exampleParticipantName"; + @SpyBean private ParticipantRegistrationRequestRepository participantRegistrationRequestRepository; @Autowired private ParticipantRegistrationRequestDAO participantRegistrationRequestDAO; + @BeforeEach + void setUp() { + + participantRegistrationRequestRepository.deleteAll(); + + PxExtendedLegalParticipantCredentialSubject participant = getParticipant(); + participantRegistrationRequestDAO.saveParticipantRegistrationRequest(participant); + + reset(participantRegistrationRequestRepository); + } + @Test void saveParticipantRegistrationRequest() { @@ -42,99 +60,166 @@ void saveParticipantRegistrationRequest() { } @Test - void getAllParticipantRegistrationRequests() { + void acceptRegistrationRequestSuccess() { - participantRegistrationRequestDAO.getRegistrationRequests(PageRequest.of(0, 1)); - verify(participantRegistrationRequestRepository).findAll(any(Pageable.class)); + ParticipantRegistrationRequestBE repoParticipant = participantRegistrationRequestDAO.getRegistrationRequestByName( + EXAMPLE_PARTICIPANT_NAME); + assertEquals(RequestStatus.NEW, repoParticipant.getStatus()); + + participantRegistrationRequestDAO.acceptRegistrationRequest(EXAMPLE_PARTICIPANT_NAME); + repoParticipant = participantRegistrationRequestDAO.getRegistrationRequestByName(EXAMPLE_PARTICIPANT_NAME); + assertEquals(RequestStatus.ACCEPTED, repoParticipant.getStatus()); } @Test - void getParticipantRegistrationByDid() { + void acceptRegistrationRequestNotExistingParticipant() { - participantRegistrationRequestDAO.getRegistrationRequestByDid("did:web:1234"); - verify(participantRegistrationRequestRepository).findByDidData_Did("did:web:1234"); + assertThrows(ParticipantEntityNotFoundException.class, + () -> participantRegistrationRequestDAO.acceptRegistrationRequest("notExistingParticipant")); } @Test - void getParticipantRegistrationByName() { + void acceptRegistrationRequestCompletedParticipant() { - participantRegistrationRequestDAO.getRegistrationRequestByName("name"); - verify(participantRegistrationRequestRepository).findByName("name"); + participantRegistrationRequestDAO.acceptRegistrationRequest(EXAMPLE_PARTICIPANT_NAME); + participantRegistrationRequestDAO.completeRegistrationRequest(EXAMPLE_PARTICIPANT_NAME, + new ParticipantDidBE("validDid", "validVerificationMethod"), "validVpLink", + new OmejdnConnectorCertificateBE("validClientId", "validPassword", "validKeystore", "123", "1234")); + + assertThrows(ParticipantEntityStateTransitionException.class, + () -> participantRegistrationRequestDAO.acceptRegistrationRequest(EXAMPLE_PARTICIPANT_NAME)); } @Test - void acceptRegistrationRequest() { + void acceptRegistrationRequestRejectedParticipant() { - PxExtendedLegalParticipantCredentialSubject participant = getParticipant(); + participantRegistrationRequestDAO.rejectRegistrationRequest(EXAMPLE_PARTICIPANT_NAME); - participantRegistrationRequestDAO.saveParticipantRegistrationRequest(participant); - participantRegistrationRequestDAO.acceptRegistrationRequest(participant.getName()); - verify(participantRegistrationRequestRepository, times(1)).save(any()); - - Page listBe = participantRegistrationRequestDAO.getRegistrationRequests( - PageRequest.of(0, 1)); - assertEquals(1, listBe.getContent().size()); - ParticipantRegistrationRequestBE repoParticipant = listBe.getContent().get(0); - assertEquals(participant.getName(), repoParticipant.getName()); - assertEquals(participant.getDescription(), repoParticipant.getDescription()); - - assertEquals(participant.getHeadquarterAddress().getCountryCode(), - repoParticipant.getHeadquarterAddress().getCountryCode()); - assertEquals(participant.getHeadquarterAddress().getCountrySubdivisionCode(), - repoParticipant.getHeadquarterAddress().getCountrySubdivisionCode()); - assertEquals(participant.getHeadquarterAddress().getStreetAddress(), - repoParticipant.getHeadquarterAddress().getStreetAddress()); - assertEquals(participant.getHeadquarterAddress().getLocality(), - repoParticipant.getHeadquarterAddress().getLocality()); - assertEquals(participant.getHeadquarterAddress().getPostalCode(), - repoParticipant.getHeadquarterAddress().getPostalCode()); - - assertEquals(participant.getLegalAddress().getCountryCode(), - repoParticipant.getLegalAddress().getCountryCode()); - assertEquals(participant.getLegalAddress().getCountrySubdivisionCode(), - repoParticipant.getLegalAddress().getCountrySubdivisionCode()); - assertEquals(participant.getLegalAddress().getStreetAddress(), - repoParticipant.getLegalAddress().getStreetAddress()); - assertEquals(participant.getLegalAddress().getLocality(), repoParticipant.getLegalAddress().getLocality()); - assertEquals(participant.getLegalAddress().getPostalCode(), repoParticipant.getLegalAddress().getPostalCode()); - - assertEquals(participant.getLegalRegistrationNumber().getEori(), - repoParticipant.getLegalRegistrationNumber().getEori()); - assertEquals(participant.getLegalRegistrationNumber().getVatID(), - repoParticipant.getLegalRegistrationNumber().getVatID()); - assertEquals(participant.getLegalRegistrationNumber().getLeiCode(), - repoParticipant.getLegalRegistrationNumber().getLeiCode()); + assertThrows(ParticipantEntityStateTransitionException.class, + () -> participantRegistrationRequestDAO.acceptRegistrationRequest(EXAMPLE_PARTICIPANT_NAME)); + } + + @Test + void completeRegistrationRequestSuccess() { + participantRegistrationRequestDAO.acceptRegistrationRequest(EXAMPLE_PARTICIPANT_NAME); + ParticipantRegistrationRequestBE repoParticipant = participantRegistrationRequestDAO.getRegistrationRequestByName( + EXAMPLE_PARTICIPANT_NAME); assertEquals(RequestStatus.ACCEPTED, repoParticipant.getStatus()); + + participantRegistrationRequestDAO.completeRegistrationRequest(EXAMPLE_PARTICIPANT_NAME, + new ParticipantDidBE("validDid", "validVerificationMethod"), "validVpLink", + new OmejdnConnectorCertificateBE("validClientId", "validPassword", "validKeystore", "123", "1234")); + repoParticipant = participantRegistrationRequestDAO.getRegistrationRequestByName(EXAMPLE_PARTICIPANT_NAME); + assertEquals(RequestStatus.COMPLETED, repoParticipant.getStatus()); } @Test - void completeRegistrationRequest() { + void completeRegistrationRequestNotExistingParticipant() { - PxExtendedLegalParticipantCredentialSubject participant = getParticipant(); + assertThrows(ParticipantEntityNotFoundException.class, + () -> participantRegistrationRequestDAO.completeRegistrationRequest("notExistingParticipant", + new ParticipantDidBE("validDid", "validVerificationMethod"), "validVpLink", + new OmejdnConnectorCertificateBE("validClientId", "validPassword", "validKeystore", "123", "1234"))); + } - participantRegistrationRequestDAO.saveParticipantRegistrationRequest(participant); - participantRegistrationRequestDAO.acceptRegistrationRequest(participant.getName()); - participantRegistrationRequestDAO.completeRegistrationRequest(participant.getName(), + @Test + void completeRegistrationRequestNotAcceptedParticipant() { + + assertThrows(ParticipantEntityStateTransitionException.class, + () -> participantRegistrationRequestDAO.completeRegistrationRequest(EXAMPLE_PARTICIPANT_NAME, + new ParticipantDidBE("validDid", "validVerificationMethod"), "validVpLink", + new OmejdnConnectorCertificateBE("validClientId", "validPassword", "validKeystore", "123", "1234"))); + } + + @Test + void rejectRegistrationRequestSuccess() { + + participantRegistrationRequestDAO.rejectRegistrationRequest(EXAMPLE_PARTICIPANT_NAME); + ParticipantRegistrationRequestBE repoParticipant = participantRegistrationRequestDAO.getRegistrationRequestByName( + EXAMPLE_PARTICIPANT_NAME); + assertEquals(RequestStatus.REJECTED, repoParticipant.getStatus()); + + } + + @Test + void rejectRegistrationRequestNotExistingParticipant() { + + assertThrows(ParticipantEntityNotFoundException.class, + () -> participantRegistrationRequestDAO.rejectRegistrationRequest("notExistingParticipant")); + } + + @Test + void rejectRegistrationRequestCompletedParticipant() { + + participantRegistrationRequestDAO.acceptRegistrationRequest(EXAMPLE_PARTICIPANT_NAME); + participantRegistrationRequestDAO.completeRegistrationRequest(EXAMPLE_PARTICIPANT_NAME, new ParticipantDidBE("validDid", "validVerificationMethod"), "validVpLink", new OmejdnConnectorCertificateBE("validClientId", "validPassword", "validKeystore", "123", "1234")); - verify(participantRegistrationRequestRepository, times(1)).save(any()); - assertNotNull( - participantRegistrationRequestRepository.findByName(participant.getName()).getOmejdnConnectorCertificate()); + + assertThrows(ParticipantEntityStateTransitionException.class, + () -> participantRegistrationRequestDAO.rejectRegistrationRequest(EXAMPLE_PARTICIPANT_NAME)); } @Test - void rejectAndDeleteRegistrationRequest() { + void deleteRegistrationRequestSuccess() { - PxExtendedLegalParticipantCredentialSubject participant = getParticipant(); + participantRegistrationRequestDAO.deleteRegistrationRequest(EXAMPLE_PARTICIPANT_NAME); + ParticipantRegistrationRequestBE repoParticipant = participantRegistrationRequestDAO.getRegistrationRequestByName( + EXAMPLE_PARTICIPANT_NAME); + assertNull(repoParticipant); + } - participantRegistrationRequestDAO.saveParticipantRegistrationRequest(participant); - participantRegistrationRequestDAO.rejectRegistrationRequest("validName"); - participantRegistrationRequestDAO.deleteRegistrationRequest("validName"); - verify(participantRegistrationRequestRepository, times(1)).save(any()); + @Test + void deleteRegistrationRequestNotExistingParticipant() { + + assertThrows(ParticipantEntityNotFoundException.class, + () -> participantRegistrationRequestDAO.deleteRegistrationRequest("notExistingParticipant")); + } + + @Test + void deleteRegistrationRequestCompletedParticipant() { + + participantRegistrationRequestDAO.acceptRegistrationRequest(EXAMPLE_PARTICIPANT_NAME); + participantRegistrationRequestDAO.completeRegistrationRequest(EXAMPLE_PARTICIPANT_NAME, + new ParticipantDidBE("validDid", "validVerificationMethod"), "validVpLink", + new OmejdnConnectorCertificateBE("validClientId", "validPassword", "validKeystore", "123", "1234")); + + assertThrows(ParticipantEntityStateTransitionException.class, + () -> participantRegistrationRequestDAO.deleteRegistrationRequest(EXAMPLE_PARTICIPANT_NAME)); + } + + @Test + void getAllParticipantRegistrationRequests() { + + participantRegistrationRequestDAO.getRegistrationRequests(PageRequest.of(0, 1)); + verify(participantRegistrationRequestRepository).findAll(any(Pageable.class)); + } + + @Test + void getParticipantRegistrationByDidSuccess() { + + participantRegistrationRequestDAO.getRegistrationRequestByDid("did:web:1234"); + verify(participantRegistrationRequestRepository).findByDidData_Did("did:web:1234"); + } + + @Test + void getParticipantRegistrationByDidNotExistingParticipant() { + + assertNull(participantRegistrationRequestDAO.getRegistrationRequestByDid("notExistingParticipant")); + } + + @Test + void getParticipantRegistrationByNameSuccess() { + + participantRegistrationRequestDAO.getRegistrationRequestByName("name"); + verify(participantRegistrationRequestRepository).findByName("name"); + } + + @Test + void getParticipantRegistrationByNameNotExistingParticipant() { - verify(participantRegistrationRequestRepository).delete(any()); - assertTrue(participantRegistrationRequestRepository.findAll().isEmpty()); + assertNull(participantRegistrationRequestDAO.getRegistrationRequestByName("notExistingParticipant")); } private PxExtendedLegalParticipantCredentialSubject getParticipant() { @@ -148,7 +233,7 @@ private PxExtendedLegalParticipantCredentialSubject getParticipant() { return PxExtendedLegalParticipantCredentialSubject.builder().id("validId").legalRegistrationNumber( new GxNestedLegalRegistrationNumberCredentialSubject("validEori", "validVatId", "validLeiCode")) - .headquarterAddress(vcard).legalAddress(vcard).name("validName").description("validDescription") - .mailAddress("example@address.com").build(); + .headquarterAddress(vcard).legalAddress(vcard).name(EXAMPLE_PARTICIPANT_NAME) + .description("validDescription").mailAddress("example@address.com").build(); } } diff --git a/backend/src/test/java/eu/possiblex/portal/testutilities/ResultCaptor.java b/backend/src/test/java/eu/possiblex/portal/testutilities/ResultCaptor.java new file mode 100644 index 0000000..21efd13 --- /dev/null +++ b/backend/src/test/java/eu/possiblex/portal/testutilities/ResultCaptor.java @@ -0,0 +1,20 @@ +package eu.possiblex.portal.testutilities; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class ResultCaptor implements Answer { + private T result = null; + + public T getResult() { + + return result; + } + + @Override + public T answer(InvocationOnMock invocationOnMock) throws Throwable { + + result = (T) invocationOnMock.callRealMethod(); + return result; + } +} \ No newline at end of file diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml index 74a3c9f..f2ee0c4 100644 --- a/backend/src/test/resources/application.yml +++ b/backend/src/test/resources/application.yml @@ -1,4 +1,6 @@ spring: + main: + allow-bean-definition-overriding: true datasource: driver-class-name: "org.h2.Driver" url: "jdbc:h2:mem:db;DB_CLOSE_DELAY=-1" @@ -29,6 +31,10 @@ did-web-service: fh: catalog: - ui-url: https://example.com/ + ui-url: test url: url - secret-key: secret \ No newline at end of file + secret-key: secret + +version.no: "1.0.0" + +version.date: "2024-12-31" \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 06484e1..1347e13 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "scripts": { "ng": "ng", - "start": "ng serve --port 4300", + "start": "ng serve", "build": "ng build", "watch": "ng build --watch --configuration local", "test": "ng test" diff --git a/libs.versions.toml b/libs.versions.toml index 3078873..5d2d173 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -22,6 +22,7 @@ springBootStarterWeb = { module = "org.springframework.boot:spring-boot-starter- springBootStarterWebflux = { module = "org.springframework.boot:spring-boot-starter-webflux", version.ref = "springBoot" } springBootStarterTest = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "springBoot" } springBootStarterDataJpa = { module = "org.springframework.boot:spring-boot-starter-data-jpa", version.ref = "springBoot" } +springBootStarterValidation= { module = "org.springframework.boot:spring-boot-starter-validation", version.ref = "springBoot" } springBootDevtools = { module = "org.springframework.boot:spring-boot-devtools", version.ref = "springBoot" } springBootStarterSecurity = { module = "org.springframework.boot:spring-boot-starter-security", version.ref = "springBoot" } springSecurityTest = { module = "org.springframework.security:spring-security-test", version.ref = "springSecurityTest" }