From 14f0f4ea186840f101f761f59e910cc7c316421f Mon Sep 17 00:00:00 2001 From: Manuel Rafeli Date: Fri, 19 Jan 2024 11:46:34 +0100 Subject: [PATCH] [SELC-3493] feat: Approve Endpoint for Enhanced Onboarding Process Approval (#95) * endpoint for approve * fix unit test onboarding get * unify endpoint approve * fix error message --- .../controller/OnboardingController.java | 16 ++- .../onboarding/service/OnboardingService.java | 2 + .../service/OnboardingServiceDefault.java | 36 ++++++- .../onboarding/util/GenericError.java | 1 + .../controller/OnboardingControllerTest.java | 18 +++- .../service/OnboardingServiceDefaultTest.java | 101 +++++++++++++++--- 6 files changed, 155 insertions(+), 19 deletions(-) diff --git a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/controller/OnboardingController.java b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/controller/OnboardingController.java index ad15324ac..1d74c4dd9 100644 --- a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/controller/OnboardingController.java +++ b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/controller/OnboardingController.java @@ -105,8 +105,7 @@ private Uni readUserIdFromToken(SecurityContext ctx) { }); } - - @Operation(summary = "Perform complete operation of an onboarding request receiving onboarding id and contract signed by hte institution." + + @Operation(summary = "Perform complete operation of an onboarding request receiving onboarding id and contract signed by the institution." + "It checks the contract's signature and upload the contract on an azure storage" + "At the end, function triggers async activities related to complete onboarding " + "that consist of create the institution, activate the onboarding and sending data to notification queue.") @@ -121,6 +120,19 @@ public Uni complete(@PathParam(value = "onboardingId") String onboardi .build()); } + @Operation(summary = "Perform approve operation of an onboarding request receiving onboarding id." + + "Function triggers async activities related to onboarding based on institution type or completing onboarding. " ) + + @PUT + @Path("/{onboardingId}/approve") + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Uni approve(@PathParam(value = "onboardingId") String onboardingId) { + return onboardingService.approve(onboardingId) + .map(ignore -> Response + .status(HttpStatus.SC_OK) + .build()); + } + @Operation(summary = "The API retrieves paged onboarding using optional filter, order by descending creation date") @GET @Produces(MediaType.APPLICATION_JSON) diff --git a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingService.java b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingService.java index 18ff9afc7..776e6825f 100644 --- a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingService.java +++ b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingService.java @@ -22,6 +22,8 @@ public interface OnboardingService { Uni onboardingPa(OnboardingPaRequest onboardingRequest); + Uni approve(String onboardingId); + Uni complete(String tokenId, File contract); Uni completeWithoutSignatureVerification(String tokenId, File contract); diff --git a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceDefault.java b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceDefault.java index 44baaf67a..d84bf6e78 100644 --- a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceDefault.java +++ b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceDefault.java @@ -46,6 +46,7 @@ import org.jboss.resteasy.reactive.ClientWebApplicationException; import org.openapi.quarkus.core_json.api.OnboardingApi; import org.openapi.quarkus.onboarding_functions_json.api.OrchestrationApi; +import org.openapi.quarkus.onboarding_functions_json.model.OrchestrationResponse; import org.openapi.quarkus.party_registry_proxy_json.api.AooApi; import org.openapi.quarkus.party_registry_proxy_json.api.UoApi; import org.openapi.quarkus.user_registry_json.api.UserApi; @@ -58,12 +59,12 @@ import java.time.OffsetDateTime; import java.util.*; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import static it.pagopa.selfcare.onboarding.common.ProductId.PROD_INTEROP; import static it.pagopa.selfcare.onboarding.constants.CustomError.*; -import static it.pagopa.selfcare.onboarding.util.GenericError.GENERIC_ERROR; -import static it.pagopa.selfcare.onboarding.util.GenericError.ONBOARDING_EXPIRED; +import static it.pagopa.selfcare.onboarding.util.GenericError.*; @ApplicationScoped public class OnboardingServiceDefault implements OnboardingService { @@ -418,6 +419,28 @@ private static boolean isFieldToUpdate(CertifiableFieldResourceOfstring certifie return isToUpdate; } + @Override + public Uni approve(String onboardingId) { + + // If approve if for PT it must complete onboarding, otherwise continue to onboarding process + Function>> approveFunction = + workflowType -> WorkflowType.FOR_APPROVE_PT.equals(workflowType) + ? () -> orchestrationApi.apiStartOnboardingCompletionOrchestrationGet(onboardingId) + : () -> orchestrationApi.apiStartOnboardingOrchestrationGet(onboardingId); + + return retrieveOnboardingAndCheckIfExpired(onboardingId) + .onItem().transformToUni(this::checkIfToBeValidated) + //Fail if onboarding exists for a product + .onItem().transformToUni(onboarding -> product(onboarding.getProductId()) + .onItem().transformToUni(product -> verifyAlreadyOnboardingForProductAndProductParent(onboarding, product)) + ) + .onItem().transformToUni(onboarding -> onboardingOrchestrationEnabled + ? approveFunction.apply(onboarding.getWorkflowType()) + .get().map(ignore -> onboarding) + : Uni.createFrom().item(onboarding)) + .map(onboardingMapper::toGetResponse); + } + @Override public Uni complete(String onboardingId, File contract) { @@ -498,6 +521,14 @@ private Uni retrieveOnboardingAndCheckIfExpired(String onboardingId) onboardingId, ONBOARDING_EXPIRED.getCode()))))); } + + private Uni checkIfToBeValidated(Onboarding onboarding) { + return OnboardingStatus.TO_BE_VALIDATED.equals(onboarding.getStatus()) + ? Uni.createFrom().item(onboarding) + : Uni.createFrom().failure(new InvalidRequestException(String.format(ONBOARDING_NOT_TO_BE_VALIDATED.getMessage(), + onboarding.getId(), ONBOARDING_NOT_TO_BE_VALIDATED.getCode()))); + } + public static boolean isOnboardingExpired(LocalDateTime dateTime) { LocalDateTime now = LocalDateTime.now(); return Objects.nonNull(dateTime) && (now.isEqual(dateTime) || now.isAfter(dateTime)); @@ -574,6 +605,7 @@ public Uni deleteOnboarding(String onboardingId) { public Uni onboardingPending(String onboardingId) { return onboardingGet(onboardingId) .flatMap(onboardingGet -> OnboardingStatus.PENDING.name().equals(onboardingGet.getStatus()) + || OnboardingStatus.TO_BE_VALIDATED.name().equals(onboardingGet.getStatus()) ? Uni.createFrom().item(onboardingGet) : Uni.createFrom().failure(new ResourceNotFoundException(String.format("Onboarding with id %s not found or not in PENDING status!",onboardingId)))); } diff --git a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/util/GenericError.java b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/util/GenericError.java index 8819a034e..b3747bb0a 100644 --- a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/util/GenericError.java +++ b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/util/GenericError.java @@ -54,6 +54,7 @@ public enum GenericError { GET_INSTITUTION_ATTRIBUTES_ERROR("0022", "Error while getting party attributes"), ONBOARDING_EXPIRED("0040", "Onboarding with id %s not found or it is expired!"), + ONBOARDING_NOT_TO_BE_VALIDATED("0043", "Onboarding with id %s has not TO_BE_VALIDATED status!"), GET_INSTITUTION_BY_GEOTAXONOMY_ERROR("0053", "Error while searching institutions related to given geoTaxonomies"), GET_INSTITUTION_BY_PRODUCTID_ERROR("0053", "Error while searching institutions related to given productId"), GET_INSTITUTIONS_REQUEST_ERROR("0054", "Invalid request parameters sent. Allowed filters combinations taxCode and subunit or origin and originId"), diff --git a/apps/onboarding-ms/src/test/java/it/pagopa/selfcare/onboarding/controller/OnboardingControllerTest.java b/apps/onboarding-ms/src/test/java/it/pagopa/selfcare/onboarding/controller/OnboardingControllerTest.java index 42288e018..3d49d134a 100644 --- a/apps/onboarding-ms/src/test/java/it/pagopa/selfcare/onboarding/controller/OnboardingControllerTest.java +++ b/apps/onboarding-ms/src/test/java/it/pagopa/selfcare/onboarding/controller/OnboardingControllerTest.java @@ -323,7 +323,6 @@ void getOnboardingByIdWithUserInfo(){ .onboardingGetWithUserInfo(onboardingGet.getId()); } - @Test @TestSecurity(user = "userJwt") void getOnboardingPending(){ @@ -340,6 +339,23 @@ void getOnboardingPending(){ verify(onboardingService, times(1)) .onboardingPending(onboardingGet.getId()); } + @Test + @TestSecurity(user = "userJwt") + void approve(){ + OnboardingGet onboardingGet = dummyOnboardingGet(); + when(onboardingService.approve(onboardingGet.getId())) + .thenReturn(Uni.createFrom().item(onboardingGet)); + + given() + .when() + .put("/{onboardingId}/approve", onboardingGet.getId()) + .then() + .statusCode(200); + + verify(onboardingService, times(1)) + .approve(onboardingGet.getId()); + } + private static Map getStringStringMap() { Map queryParameterMap = new HashMap<>(); queryParameterMap.put("productId","prod-io"); diff --git a/apps/onboarding-ms/src/test/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceDefaultTest.java b/apps/onboarding-ms/src/test/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceDefaultTest.java index 7cae44fd1..fd8e31ca9 100644 --- a/apps/onboarding-ms/src/test/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceDefaultTest.java +++ b/apps/onboarding-ms/src/test/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceDefaultTest.java @@ -16,7 +16,6 @@ import it.pagopa.selfcare.onboarding.common.OnboardingStatus; import it.pagopa.selfcare.onboarding.common.PartyRole; import it.pagopa.selfcare.onboarding.controller.request.*; -import it.pagopa.selfcare.onboarding.controller.response.InstitutionResponse; import it.pagopa.selfcare.onboarding.controller.response.OnboardingGet; import it.pagopa.selfcare.onboarding.controller.response.OnboardingGetResponse; import it.pagopa.selfcare.onboarding.controller.response.UserResponse; @@ -27,6 +26,8 @@ import it.pagopa.selfcare.onboarding.exception.InvalidRequestException; import it.pagopa.selfcare.onboarding.exception.OnboardingNotAllowedException; import it.pagopa.selfcare.onboarding.exception.ResourceNotFoundException; +import it.pagopa.selfcare.onboarding.mapper.OnboardingMapper; +import it.pagopa.selfcare.onboarding.mapper.OnboardingMapperImpl; import it.pagopa.selfcare.onboarding.util.InstitutionPaSubunitType; import it.pagopa.selfcare.product.entity.Product; import it.pagopa.selfcare.product.entity.ProductRole; @@ -42,6 +43,7 @@ import org.jboss.resteasy.reactive.ClientWebApplicationException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.Spy; import org.openapi.quarkus.core_json.api.OnboardingApi; import org.openapi.quarkus.onboarding_functions_json.api.OrchestrationApi; import org.openapi.quarkus.onboarding_functions_json.model.OrchestrationResponse; @@ -102,6 +104,9 @@ class OnboardingServiceDefaultTest { @RestClient OrchestrationApi orchestrationApi; + @Spy + OnboardingMapper onboardingMapper = new OnboardingMapperImpl(); + final static UserRequest manager = UserRequest.builder() .name("name") .surname("surname") @@ -931,7 +936,7 @@ void testOnboardingGet() { int page = 0, size = 3; Onboarding onboarding = createDummyOnboarding(); mockFindOnboarding(onboarding); - OnboardingGetResponse getResponse = getOnboardingGetResponse(onboarding.getId()); + OnboardingGetResponse getResponse = getOnboardingGetResponse(onboarding); UniAssertSubscriber subscriber = onboardingService .onboardingGet("prod-io", null, null, "2023-11-10", "2021-12-10", page,size) .subscribe() @@ -940,19 +945,11 @@ void testOnboardingGet() { subscriber.assertCompleted().assertItem(getResponse); } - private static OnboardingGetResponse getOnboardingGetResponse(ObjectId id) { - OnboardingGet onboarding = new OnboardingGet(); - onboarding.setId(id.toString()); - onboarding.setProductId("prod-id"); - UserResponse user = new UserResponse(); - user.setId("actual-user-id"); - user.setRole(PartyRole.MANAGER); - onboarding.setUsers(List.of(user)); - InstitutionResponse institutionResponse = new InstitutionResponse(); - onboarding.setInstitution(institutionResponse); + private OnboardingGetResponse getOnboardingGetResponse(Onboarding onboarding) { + OnboardingGet onboardingGet = onboardingMapper.toGetResponse(onboarding); OnboardingGetResponse response = new OnboardingGetResponse(); response.setCount(1L); - response.setItems(List.of(onboarding)); + response.setItems(List.of(onboardingGet)); return response; } @@ -992,6 +989,8 @@ private Onboarding createDummyOnboarding() { onboarding.setProductId("prod-id"); Institution institution = new Institution(); + institution.setTaxCode("taxCode"); + institution.setSubunitCode("subunitCode"); onboarding.setInstitution(institution); User user = new User(); @@ -1100,7 +1099,7 @@ void onboardingPending_shouldResourceNotFound() { when(Onboarding.findByIdOptional(any())) .thenReturn(Uni.createFrom().item(Optional.of(onboarding))); - UniAssertSubscriber subscriber = onboardingService + onboardingService .onboardingPending(ObjectId.get().toHexString()) .subscribe() .withSubscriber(UniAssertSubscriber.create()) @@ -1132,4 +1131,78 @@ void onboardingGetWithUserInfo() { Assertions.assertEquals(actualUser.getSurname(), managerResource.getFamilyName().getValue()); } + + + @Test + void approve() { + Onboarding onboarding = createDummyOnboarding(); + onboarding.setStatus(OnboardingStatus.TO_BE_VALIDATED); + PanacheMock.mock(Onboarding.class); + when(Onboarding.findByIdOptional(any())) + .thenReturn(Uni.createFrom().item(Optional.of(onboarding))); + + when(productService.getProductIsValid(onboarding.getProductId())) + .thenReturn(createDummyProduct(onboarding.getProductId(), false)); + + when(onboardingApi.verifyOnboardingInfoUsingHEAD(onboarding.getInstitution().getTaxCode(), onboarding.getProductId(), + onboarding.getInstitution().getSubunitCode())) + .thenReturn(Uni.createFrom().failure(new ClientWebApplicationException(404))); + + when(orchestrationApi.apiStartOnboardingOrchestrationGet(onboarding.getId().toHexString())) + .thenReturn(Uni.createFrom().item(new OrchestrationResponse())); + + UniAssertSubscriber subscriber = onboardingService + .approve(onboarding.getId().toHexString()) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()); + + OnboardingGet actual = subscriber.awaitItem().getItem(); + Assertions.assertNotNull(actual); + Assertions.assertEquals(onboarding.getId().toHexString(), actual.getId()); + } + + @Test + void approve_throwExceptionIfOnboardingIsNotToBeValidated() { + Onboarding onboarding = createDummyOnboarding(); + PanacheMock.mock(Onboarding.class); + when(Onboarding.findByIdOptional(any())) + .thenReturn(Uni.createFrom().item(Optional.of(onboarding))); + + when(productService.getProductIsValid(onboarding.getProductId())) + .thenReturn(createDummyProduct(onboarding.getProductId(), false)); + + onboardingService + .approve(onboarding.getId().toHexString()) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(InvalidRequestException.class); + } + + @Test + void approveCompletion() { + Onboarding onboarding = createDummyOnboarding(); + onboarding.setStatus(OnboardingStatus.TO_BE_VALIDATED); + PanacheMock.mock(Onboarding.class); + when(Onboarding.findByIdOptional(any())) + .thenReturn(Uni.createFrom().item(Optional.of(onboarding))); + + when(productService.getProductIsValid(onboarding.getProductId())) + .thenReturn(createDummyProduct(onboarding.getProductId(), false)); + + when(onboardingApi.verifyOnboardingInfoUsingHEAD(onboarding.getInstitution().getTaxCode(), onboarding.getProductId(), + onboarding.getInstitution().getSubunitCode())) + .thenReturn(Uni.createFrom().failure(new ClientWebApplicationException(404))); + + when(orchestrationApi.apiStartOnboardingCompletionOrchestrationGet(onboarding.getId().toHexString())) + .thenReturn(Uni.createFrom().item(new OrchestrationResponse())); + + UniAssertSubscriber subscriber = onboardingService + .approve(onboarding.getId().toHexString()) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()); + + OnboardingGet actual = subscriber.awaitItem().getItem(); + Assertions.assertNotNull(actual); + Assertions.assertEquals(onboarding.getId().toHexString(), actual.getId()); + } }