From 385ed1ce77f03f6bab5888d83f6c2493b464c652 Mon Sep 17 00:00:00 2001 From: Giulia Tremolada <124147597+giulia-tremolada@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:02:23 +0200 Subject: [PATCH 1/8] [SELC-5764] feat: modify link text for csv aggregates for IO (#548) --- .../it/pagopa/selfcare/onboarding/utils/PdfMapper.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/utils/PdfMapper.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/utils/PdfMapper.java index ac6ac1d58..959a646a2 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/utils/PdfMapper.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/utils/PdfMapper.java @@ -31,7 +31,9 @@ public class PdfMapper { public static final String INSTITUTION_REGISTER_LABEL_VALUE = "institutionRegisterLabelValue"; public static final String CSV_AGGREGATES_LABEL_VALUE = "aggregatesCsvLink"; public static final String ORIGIN_ID_LABEL = "
  • codice di iscrizione all’Indice delle Pubbliche Amministrazioni e dei gestori di pubblici servizi (I.P.A.) ${originId}
  • "; - public static final String CSV_AGGREGATES_LABEL = " - Dati di Enti Aggregati"; + public static final String CSV_AGGREGATES_LABEL = " - %s"; + public static final String CSV_AGGREGATES_TEXT_PAGOPA = "Dati di Enti Aggregati"; + public static final String CSV_AGGREGATES_TEXT_IO = "Dati degli Enti Aggregati_IO"; public static final String INSTITUTION_RECIPIENT_CODE = "institutionRecipientCode"; private PdfMapper() { @@ -214,7 +216,8 @@ private static void addAggregatesCsvLink(Onboarding onboarding, Map Date: Thu, 17 Oct 2024 15:29:45 +0200 Subject: [PATCH 2/8] [PNPG-244] feat: added user apis and implementation of onboardingUserPg method (#543) --- apps/onboarding-ms/README.md | 1 + .../onboarding/service/OnboardingService.java | 2 + .../service/OnboardingServiceDefault.java | 239 ++- apps/onboarding-ms/src/main/openapi/user.json | 1908 +++++++++++++++++ .../src/main/resources/application.properties | 8 + .../service/OnboardingServiceDefaultTest.java | 175 +- 6 files changed, 2324 insertions(+), 9 deletions(-) create mode 100644 apps/onboarding-ms/src/main/openapi/user.json diff --git a/apps/onboarding-ms/README.md b/apps/onboarding-ms/README.md index 9b5a2250d..76b3ebe67 100644 --- a/apps/onboarding-ms/README.md +++ b/apps/onboarding-ms/README.md @@ -39,6 +39,7 @@ Before running you must set these properties as environment variables. | quarkus.rest-client."**.UoApi".url
    | MS_PARTY_REGISTRY_URL | | yes | | quarkus.rest-client."**.OrchestrationApi".url
    | ONBOARDING_FUNCTIONS_URL | | yes | | quarkus.rest-client."**.OrchestrationApi".api-key
    | ONBOARDING-FUNCTIONS-API-KEY | | yes | +| quarkus.rest-client."**.InstitutionApi".url
    | USER_URL | | yes | | onboarding.institutions-allowed-list
    | ONBOARDING_ALLOWED_INSTITUTIONS_PRODUCTS | | no | > **_NOTE:_** properties that contains secret must have the same name of its secret as uppercase. 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 5289f901d..5935d0882 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 @@ -27,6 +27,8 @@ public interface OnboardingService { Uni onboardingAggregationCompletion(Onboarding onboarding, List userRequests, List aggregates); + Uni onboardingUserPg(Onboarding onboarding, List userRequests); + Uni approve(String onboardingId); Uni complete(String tokenId, FormItem formItem); 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 8aa387495..dea8bef28 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 @@ -53,9 +53,10 @@ 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.InfocamereApi; +import org.openapi.quarkus.party_registry_proxy_json.api.NationalRegistriesApi; import org.openapi.quarkus.party_registry_proxy_json.api.UoApi; -import org.openapi.quarkus.party_registry_proxy_json.model.AOOResource; -import org.openapi.quarkus.party_registry_proxy_json.model.UOResource; +import org.openapi.quarkus.party_registry_proxy_json.model.*; import org.openapi.quarkus.user_registry_json.api.UserApi; import org.openapi.quarkus.user_registry_json.model.*; @@ -89,6 +90,7 @@ public class OnboardingServiceDefault implements OnboardingService { public static final String USERS_FIELD_TAXCODE = "fiscalCode"; public static final String TIMEOUT_ORCHESTRATION_RESPONSE = "65"; private static final String ID_MAIL_PREFIX = "ID_MAIL#"; + public static final String NOT_MANAGER_OF_THE_INSTITUTION_ON_THE_REGISTRY = "User is not manager of the institution on the registry"; @RestClient @Inject @@ -118,6 +120,18 @@ public class OnboardingServiceDefault implements OnboardingService { @Inject OrchestrationApi orchestrationApi; + @RestClient + @Inject + InfocamereApi infocamereApi; + + @RestClient + @Inject + NationalRegistriesApi nationalRegistriesApi; + + @RestClient + @Inject + org.openapi.quarkus.user_json.api.InstitutionApi userInstitutionApi; + @Inject OnboardingMapper onboardingMapper; @@ -1278,4 +1292,225 @@ private Uni getEC() { .type(EC) .build()); } + + /** + * Initiates the onboarding process for a user in the PG (Persona Giuridica) context. + * This method performs the following steps: + *
      + *
    • Validates the provided user requests to ensure only one manager is onboarded.
    • + *
    • Sets the workflow type and status of the onboarding to USERS_PG and PENDING, respectively.
    • + *
    • Retrieves any previous completed onboarding data for the institution and product.
    • + *
    • Copies relevant data from the previous onboarding to the current onboarding instance.
    • + *
    • Retrieves and sets the manager UID for the new onboarding.
    • + *
    • Checks if the user is already a manager within the institution.
    • + *
    • Verifies the manager's association with the institution in external registries (Infocamere or ADE).
    • + *
    • Persists the onboarding data and initiates orchestration.
    • + *
    + * + * @param onboarding the onboarding data to process + * @param userRequests the list of user requests associated with the onboarding + * @return a Uni that emits the onboarding response upon successful completion + * @throws InvalidRequestException if the user list is invalid or the user is already a manager + * @throws ResourceNotFoundException if no previous onboarding data is found for the institution + */ + public Uni onboardingUserPg(Onboarding onboarding, List userRequests) { + checkOnboardingPgUserList(userRequests); + onboarding.setWorkflowType(WorkflowType.USERS_PG); + onboarding.setStatus(OnboardingStatus.PENDING); + onboarding.setCreatedAt(LocalDateTime.now()); + + return retrievePreviousCompletedOnboarding(onboarding) + .map(previousOnboarding -> copyDataFromPreviousToCurrentOnboarding(previousOnboarding, onboarding)) + .flatMap(unused -> retrieveAndSetManagerUidOnNewOnboarding(onboarding, userRequests)) + .flatMap(unused -> checkIfUserIsAlreadyManager(onboarding)) + .flatMap(unused -> checkIfUserIsManagerOnRegistries(onboarding, userRequests)) + .onItem().transformToUni(unused -> persistAndStartOrchestrationOnboarding(onboarding, + orchestrationApi.apiStartOnboardingOrchestrationGet(onboarding.getId(), TIMEOUT_ORCHESTRATION_RESPONSE))) + .onItem().transform(onboardingMapper::toResponse); + } + + /** + * Validates the list of user requests to ensure only one manager is present. + * + * @param userRequests the list of user requests to validate + * @throws InvalidRequestException if the user list is empty, contains more than one user, or the user role is not MANAGER + */ + private void checkOnboardingPgUserList(List userRequests) { + if(CollectionUtils.isEmpty(userRequests) || userRequests.size() > 1 || !PartyRole.MANAGER.equals(userRequests.get(0).getRole())) { + throw new InvalidRequestException("This API allows the onboarding of only one user with role MANAGER"); + } + } + + private Uni retrievePreviousCompletedOnboarding(Onboarding onboarding) { + LOG.infof("Retrieving previous completed onboarding for taxCode %s, origin %s, productId %s", + onboarding.getInstitution().getTaxCode(), onboarding.getInstitution().getOrigin(), onboarding.getProductId()); + + return getOnboardingByFilters( + onboarding.getInstitution().getTaxCode(), + null, + String.valueOf(onboarding.getInstitution().getOrigin()), + null, + onboarding.getProductId() + ) + .collect() + .asList() + .onItem().transformToUni(this::getOnboardingList) + .onItem().ifNull().failWith(resourceNotFoundExceptionSupplier(onboarding)) + .map(onboardings -> onboardings.stream() + .filter(o -> Objects.isNull(o.getReferenceOnboardingId())) + .findFirst() + .orElse(null) + ); + } + + private Supplier resourceNotFoundExceptionSupplier(Onboarding onboarding) { + return () -> new ResourceNotFoundException(String.format("Onboarding not found for taxCode %s, origin %s, productId %s", + onboarding.getInstitution().getTaxCode(), onboarding.getInstitution().getOrigin(), onboarding.getProductId())); + } + + private Onboarding copyDataFromPreviousToCurrentOnboarding(Onboarding previousOnboarding, Onboarding currentOnboarding) { + currentOnboarding.setReferenceOnboardingId(previousOnboarding.getId()); + currentOnboarding.setInstitution(previousOnboarding.getInstitution()); + return currentOnboarding; + } + + private Uni retrieveAndSetManagerUidOnNewOnboarding(Onboarding onboarding, List userRequests) { + return getProductByOnboarding(onboarding) + .flatMap(product -> validationRole(userRequests, validRoles(product, PHASE_ADDITION_ALLOWED.ONBOARDING, onboarding.getInstitution().getInstitutionType())) + .map(unused -> retrieveRoleMappingsFromProduct(product, onboarding)) + ) + .flatMap(roleMappings -> retrieveUserResources(userRequests, roleMappings)) + .onItem().invoke(onboarding::setUsers) + .replaceWith(onboarding); + } + + private Map retrieveRoleMappingsFromProduct(Product product, Onboarding onboarding) { + return Objects.nonNull(product.getParent()) + ? product.getParent().getRoleMappings(onboarding.getInstitution().getInstitutionType().name()) + : product.getRoleMappings(onboarding.getInstitution().getInstitutionType().name()); + } + + /** + * Checks if the user is already a manager within the institution invoking selfcare-user API. + * + * @param currentOnboarding the current onboarding data + * @return a Uni that completes if the user is not already a manager, otherwise fails + * @throws InvalidRequestException if the user is already a manager of the institution + */ + private Uni checkIfUserIsAlreadyManager(Onboarding currentOnboarding) { + String newManagerId = currentOnboarding.getUsers().stream() + .filter(user -> PartyRole.MANAGER.equals(user.getRole())) + .map(User::getId) + .findAny() + .orElse(null); + String institutionId = currentOnboarding.getInstitution().getId(); + + LOG.infof("Checking if user with id: %s is already manager of the institution with id: %s", newManagerId, institutionId); + + return userInstitutionApi.retrieveUserInstitutions( + institutionId, + null, + List.of(currentOnboarding.getProductId()), + List.of(PartyRole.MANAGER.name()), + List.of("ACTIVE"), + null + ).invoke(users -> { + LOG.debugf("Managers found: %s for institution with id: %s", users, institutionId); + if (users.stream().anyMatch(userInstitution -> userInstitution.getUserId().equals(newManagerId))) { + throw new InvalidRequestException("User is already manager of the institution"); + } + }).replaceWithVoid(); + } + + /** + * Checks if the user is a manager in the external registries based on the institution's origin. + * + * @param onboarding the current onboarding data + * @param userRequests the list of user requests associated with the onboarding + * @return a Uni that completes if the user is a valid manager in the registry, otherwise fails + * @throws InvalidRequestException if the user is not a manager in the external registry + */ + private Uni checkIfUserIsManagerOnRegistries(Onboarding onboarding, List userRequests) { + LOG.infof("Checking if user is manager on registries for onboarding with origin %s", onboarding.getInstitution().getOrigin()); + String userTaxCode = userRequests.stream() + .filter(userRequest -> PartyRole.MANAGER.equals(userRequest.getRole())) + .map(UserRequest::getTaxCode) + .findAny() + .orElse(null); + + String businessTaxCode = onboarding.getInstitution().getTaxCode(); + + if(onboarding.getInstitution().getOrigin() == Origin.INFOCAMERE) { + return checkIfUserIsManagerOnInfocamere(userTaxCode, businessTaxCode); + } else { + return checkIfUserIsManagerOnADE(userTaxCode, businessTaxCode); + } + } + + /** + * Checks if the user is a manager in the Infocamere registry. + * + * @param userTaxCode the tax code of the user + * @param businessTaxCode the tax code of the business (institution) + * @return a Uni that completes if the user is a manager, otherwise fails + * @throws InvalidRequestException if the user is not a manager in Infocamere + */ + private Uni checkIfUserIsManagerOnInfocamere(String userTaxCode, String businessTaxCode) { + return infocamereApi.institutionsByLegalTaxIdUsingPOST(toGetInstitutionsByLegalDto(userTaxCode)) + .flatMap(businessesResource -> checkIfBusinessIsContained(businessesResource, businessTaxCode)); + } + + private GetInstitutionsByLegalDto toGetInstitutionsByLegalDto(String userTaxCode) { + return GetInstitutionsByLegalDto.builder() + .filter(GetInstitutionsByLegalFilterDto.builder() + .legalTaxId(userTaxCode) + .build()) + .build(); + } + + /** + * Validates if the business tax code is contained within the retrieved businesses. + * + * @param businessesResource the resource containing businesses data + * @param taxCode the tax code to validate against + * @return a Uni that completes if the tax code is found, otherwise fails + * @throws InvalidRequestException if the tax code is not found in the businesses resource + */ + private Uni checkIfBusinessIsContained(BusinessesResource businessesResource, String taxCode) { + if( + Objects.isNull(businessesResource) || + Objects.isNull(businessesResource.getBusinesses()) || + businessesResource.getBusinesses().stream().noneMatch(business -> business.getBusinessTaxId().equals(taxCode)) + ) { + throw new InvalidRequestException(NOT_MANAGER_OF_THE_INSTITUTION_ON_THE_REGISTRY); + } + + return Uni.createFrom().voidItem(); + } + + /** + * Checks if the user is a manager in the ADE (Agenzia delle Entrate) registry. + * + * @param userTaxCode the tax code of the user + * @param businessTaxCode the tax code of the business (institution) + * @return a Uni that completes if the user is a manager, otherwise fails + * @throws InvalidRequestException if the user is not a manager in ADE + */ + private Uni checkIfUserIsManagerOnADE(String userTaxCode, String businessTaxCode) { + return nationalRegistriesApi.verifyLegalUsingGET(userTaxCode, businessTaxCode) + .onItem().transformToUni(legalVerificationResult -> { + if(!legalVerificationResult.getVerificationResult()) { + throw new InvalidRequestException(NOT_MANAGER_OF_THE_INSTITUTION_ON_THE_REGISTRY); + } + return Uni.createFrom().voidItem(); + }) + .onFailure(WebApplicationException.class).recoverWithUni(ex -> { + // If the user is not manager of the institution, the response status code could be 400 + if (((WebApplicationException) ex).getResponse().getStatus() == 400) { + return Uni.createFrom().failure(new InvalidRequestException("User is not manager of the institution on the registry")); + } + + return Uni.createFrom().failure(ex); + }); + } } diff --git a/apps/onboarding-ms/src/main/openapi/user.json b/apps/onboarding-ms/src/main/openapi/user.json new file mode 100644 index 000000000..203722f42 --- /dev/null +++ b/apps/onboarding-ms/src/main/openapi/user.json @@ -0,0 +1,1908 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "User API", + "version" : "1.0.0" + }, + "servers" : [ { + "url" : "http://localhost:8080", + "description" : "Auto generated value" + }, { + "url" : "http://0.0.0.0:8080", + "description" : "Auto generated value" + } ], + "tags" : [ { + "name" : "Events" + }, { + "name" : "Institution" + }, { + "name" : "User" + }, { + "name" : "external-pnpg" + }, { + "name" : "external-v2" + }, { + "name" : "internal-v1" + }, { + "name" : "support" + }, { + "name" : "support-pnpg" + } ], + "paths" : { + "/authorize" : { + "get" : { + "tags" : [ "User Permission Controller" ], + "summary" : "Get permission for a user in an institution", + "description" : "Determines if the authenticated user possesses a specific permission within the given institution and product context.", + "operationId" : "getPermission", + "parameters" : [ { + "name" : "institutionId", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "permission", + "in" : "query", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/PermissionTypeEnum" + } + }, { + "name" : "productId", + "in" : "query", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "boolean" + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/events/sc-users" : { + "post" : { + "tags" : [ "Events" ], + "summary" : "Resend all user events based on institutionId, userId, and fromDate", + "description" : "Resends all events for a specific user within a given institution starting from the specified date and time. This endpoint allows administrators to trigger the reprocessing or notification of user-related events that occurred after the provided `fromDate`.", + "operationId" : "sendUsersEvents", + "parameters" : [ { + "name" : "fromDate", + "in" : "query", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/LocalDateTime" + } + }, { + "name" : "institutionId", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "userId", + "in" : "query", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "201" : { + "description" : "Created" + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/institutions/{institutionId}" : { + "put" : { + "tags" : [ "Institution" ], + "summary" : "Update institution's description across all userInstitution records", + "description" : "Modifies the description field in all occurrences of `userInstitution` entities associated with a given institutionId. This ensures that the institution's descriptive information is consistently updated across all related user records.", + "operationId" : "updateInstitutionDescription", + "parameters" : [ { + "name" : "institutionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UpdateDescriptionDto" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/institutions/{institutionId}/products/{productId}/created-at" : { + "put" : { + "tags" : [ "Institution" ], + "summary" : "Update user's onboarded product creation date", + "description" : "Updates the `createdAt` timestamp for a user's onboarded product based on the provided institutionId, productId, and list of userIds. This is useful for tracking when a user was onboarded to a specific product within an institution.", + "operationId" : "updateUserProductCreatedAt", + "parameters" : [ { + "name" : "institutionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "productId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "createdAt", + "in" : "query", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/LocalDateTime" + } + }, { + "name" : "userIds", + "in" : "query", + "required" : true, + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/institutions/{institutionId}/user-institutions" : { + "get" : { + "tags" : [ "Institution" ], + "summary" : "Retrieve users with optional filters", + "description" : "Fetches a list of users associated with a specific institution, applying optional filters such as userId, roles, states, products, and productRoles. This allows for flexible querying based on various user attributes and statuses.", + "operationId" : "retrieveUserInstitutions", + "parameters" : [ { + "name" : "institutionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "productRoles", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "products", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "roles", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "states", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "userId", + "in" : "query", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserInstitutionResponse" + } + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/institutions/{institutionId}/users" : { + "get" : { + "tags" : [ "support", "support-pnpg", "Institution" ], + "summary" : "Retrieve user's information including product role details", + "description" : "Fetches detailed information about users associated with a specific institution, including their roles on various products. This endpoint is useful for administrators to obtain comprehensive user-role mappings within an institution.", + "operationId" : "getInstitutionUsersUsingGET", + "parameters" : [ { + "name" : "institutionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserProductResponse" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "401" : { + "description" : "Not Authorized", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Retrieve paged users with optional query filters", + "description" : "The API retrieves paged users with optional filters in input as query params", + "operationId" : "retrievePaginatedAndFilteredUser", + "parameters" : [ { + "name" : "institutionId", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "page", + "in" : "query", + "schema" : { + "format" : "int32", + "default" : "0", + "type" : "integer" + } + }, { + "name" : "productRoles", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "products", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "roles", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "size", + "in" : "query", + "schema" : { + "format" : "int32", + "default" : "100", + "type" : "integer" + } + }, { + "name" : "states", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "userId", + "in" : "query", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserInstitutionResponse" + } + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + }, + "post" : { + "tags" : [ "User" ], + "summary" : "Create or update a user by fiscal code", + "description" : "The createOrUpdateByFiscalCode function is used to create a new user or update an existing one.", + "operationId" : "createOrUpdateByFiscalCode", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CreateUserDto" + } + } + } + }, + "responses" : { + "200" : { + "description" : "User created or updated!", + "content" : { + "application/json" : { } + } + }, + "201" : { + "description" : "User already has the active role for that product!" + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/emails" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Retrieve users' emails by institution ID and product ID", + "description" : "The API retrieves Users' emails using institution id and product id", + "operationId" : "getUsersEmailByInstitutionAndProduct", + "parameters" : [ { + "name" : "institutionId", + "in" : "query", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "productId", + "in" : "query", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/ids" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Retrieve multiple users by their user IDs", + "description" : "Retrieve all users given their userIds", + "operationId" : "findAllByIds", + "parameters" : [ { + "name" : "userIds", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserInstitutionResponse" + } + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/notification" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Retrieve all SC-Users for DataLake with optional product filter", + "description" : "Retrieve all SC-User for DataLake filtered by optional productId", + "operationId" : "getUsers", + "parameters" : [ { + "name" : "page", + "in" : "query", + "schema" : { + "format" : "int32", + "default" : "0", + "type" : "integer" + } + }, { + "name" : "productId", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "size", + "in" : "query", + "schema" : { + "format" : "int32", + "default" : "100", + "type" : "integer" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UsersNotificationResponse" + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/search" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Search for a user using fiscal code", + "description" : "Search user by fiscalCode", + "operationId" : "searchUserByFiscalCode", + "parameters" : [ { + "name" : "institutionId", + "in" : "query", + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SearchUserDto" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetailResponse" + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{id}" : { + "get" : { + "tags" : [ "User", "external-v2", "support", "support-pnpg", "external-pnpg" ], + "summary" : "Retrieve user information by userId and optional ProductId", + "description" : "Retrieves user given userId and optional ProductId", + "operationId" : "getUserInfoUsingGET", + "parameters" : [ { + "name" : "id", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "institutionId", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "productId", + "in" : "query", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserResponse" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "401" : { + "description" : "Not Authorized", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{id}/details" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Retrieve detailed user information from PDV by userId", + "description" : "Retrieves user's information from pdv: name, familyName, email, fiscalCode and workContacts", + "operationId" : "getUserDetailsById", + "parameters" : [ { + "name" : "id", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "field", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "institutionId", + "in" : "query", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetailResponse" + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{id}/institution/{institutionId}/product/{productId}/status" : { + "put" : { + "tags" : [ "User" ], + "summary" : "Update the status of a user's product", + "description" : "Service to update user product status", + "operationId" : "updateUserProductStatus", + "parameters" : [ { + "name" : "id", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "institutionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "productId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "productRole", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "status", + "in" : "query", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/OnboardedProductState" + } + } ], + "responses" : { + "204" : { + "description" : "No Content" + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{id}/status" : { + "put" : { + "tags" : [ "User", "internal-v1" ], + "summary" : "Update a user's product status with optional filters", + "description" : "Update user status with optional filter for institution, product, role and productRole", + "operationId" : "updateUserStatusUsingPUT", + "parameters" : [ { + "name" : "id", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "institutionId", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "productId", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "productRole", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "role", + "in" : "query", + "schema" : { + "description" : "Available values: MANAGER, DELEGATE, SUB_DELEGATE, OPERATOR, ADMIN_EA", + "type" : "string" + } + }, { + "name" : "status", + "in" : "query", + "schema" : { + "$ref" : "#/components/schemas/OnboardedProductState" + } + } ], + "responses" : { + "204" : { + "description" : "No Content" + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "401" : { + "description" : "Not Authorized", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{id}/user-registry" : { + "put" : { + "tags" : [ "User" ], + "summary" : "Update user registry and send notification upon data update", + "description" : "Service to update user in user-registry and send notification when user data gets updated", + "operationId" : "updateUserRegistryAndSendNotification", + "parameters" : [ { + "name" : "id", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "institutionId", + "in" : "query", + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UpdateUserRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{userId}" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Update or create a user by userId with a new role", + "description" : "The createOrUpdateByUserId function is used to update existing user adding userRole.", + "operationId" : "createOrUpdateByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AddUserRoleDto" + } + } + } + }, + "responses" : { + "200" : { + "description" : "User created or updated!", + "content" : { + "application/json" : { } + } + }, + "201" : { + "description" : "User already has the active role for that product!" + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{userId}/institution/{institutionId}" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Retrieve a list of users with optional filters and permissions", + "description" : "The retrieveUsers function is used to retrieve a list of users from the UserInstitution collection and userRegistry.\nAt first it try to retrieve a UserInstitution document associated with a logged user (admin)\nIf this userInstitution object is not null, so user has AdminRole, it try to retriew the userInstitutions filtered by given institutionId, roles, states, products and productRoles\nand optional given personId, otherwise it do the same query using the logged user id instead of personId.\nAfter that it retrieve personal user data, foreach userId retrieved, from userRegistry and return a stream of UserDataResponse objects containing the requested user data.", + "operationId" : "retrieveUsers", + "parameters" : [ { + "name" : "institutionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "userId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "personId", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "productRoles", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "products", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "roles", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "states", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserDataResponse" + } + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{userId}/institutions" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Retrieve product information and user roles", + "description" : "Retrieves products info and role which the user is enabled", + "operationId" : "getUserProductsInfo", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "institutionId", + "in" : "query", + "schema" : { + "type" : "string" + } + }, { + "name" : "states", + "in" : "query", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserInfoResponse" + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{userId}/institutions/{institutionId}" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Retrieve userInstitution data with permitted actions for each product", + "description" : "Retrieves userInstitution data with list of actions permitted for each user's product", + "operationId" : "getUserInstitutionWithPermission", + "parameters" : [ { + "name" : "institutionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "userId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "productId", + "in" : "query", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserInstitutionWithActions" + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, + "/users/{userId}/institutions/{institutionId}/products/{productId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Logically delete the association between institution and product", + "description" : "Delete logically the association institution and product", + "operationId" : "deleteProducts", + "parameters" : [ { + "name" : "institutionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "productId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "userId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "204" : { + "description" : "No Content" + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + } + }, + "components" : { + "schemas" : { + "AddUserRoleDto" : { + "required" : [ "institutionId", "product" ], + "type" : "object", + "properties" : { + "institutionId" : { + "minLength" : 1, + "type" : "string" + }, + "product" : { + "$ref" : "#/components/schemas/Product" + }, + "institutionDescription" : { + "type" : "string" + }, + "institutionRootName" : { + "type" : "string" + }, + "userMailUuid" : { + "type" : "string" + }, + "hasToSendEmail" : { + "type" : "boolean" + } + } + }, + "CertifiableFieldResponseString" : { + "type" : "object", + "properties" : { + "value" : { + "type" : "string" + }, + "certified" : { + "$ref" : "#/components/schemas/CertificationEnum" + } + } + }, + "CertificationEnum" : { + "enum" : [ "NONE", "SPID" ], + "type" : "string" + }, + "CreateUserDto" : { + "required" : [ "institutionId", "user", "product" ], + "type" : "object", + "properties" : { + "institutionId" : { + "minLength" : 1, + "type" : "string" + }, + "user" : { + "$ref" : "#/components/schemas/User" + }, + "product" : { + "$ref" : "#/components/schemas/Product1" + }, + "institutionDescription" : { + "type" : "string" + }, + "institutionRootName" : { + "type" : "string" + }, + "hasToSendEmail" : { + "type" : "boolean" + } + } + }, + "Env" : { + "enum" : [ "ROOT", "DEV", "COLL", "PROD" ], + "type" : "string" + }, + "InvalidParam" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "reason" : { + "type" : "string" + } + } + }, + "LocalDateTime" : { + "format" : "date-time", + "type" : "string", + "example" : "2022-03-10T12:15:50" + }, + "OnboardedProductResponse" : { + "type" : "object", + "properties" : { + "productId" : { + "type" : "string" + }, + "tokenId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/OnboardedProductState" + }, + "productRole" : { + "type" : "string" + }, + "role" : { + "description" : "Available values: MANAGER, DELEGATE, SUB_DELEGATE, OPERATOR, ADMIN_EA", + "type" : "string" + }, + "env" : { + "$ref" : "#/components/schemas/Env" + }, + "createdAt" : { + "$ref" : "#/components/schemas/LocalDateTime" + }, + "updatedAt" : { + "$ref" : "#/components/schemas/LocalDateTime" + } + } + }, + "OnboardedProductState" : { + "enum" : [ "ACTIVE", "PENDING", "TOBEVALIDATED", "SUSPENDED", "DELETED", "REJECTED" ], + "type" : "string" + }, + "OnboardedProductWithActions" : { + "type" : "object", + "properties" : { + "productId" : { + "type" : "string" + }, + "tokenId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/OnboardedProductState" + }, + "productRole" : { + "type" : "string" + }, + "role" : { + "description" : "Available values: MANAGER, DELEGATE, SUB_DELEGATE, OPERATOR, ADMIN_EA", + "type" : "string" + }, + "env" : { + "$ref" : "#/components/schemas/Env" + }, + "createdAt" : { + "$ref" : "#/components/schemas/LocalDateTime" + }, + "updatedAt" : { + "$ref" : "#/components/schemas/LocalDateTime" + }, + "delegationId" : { + "type" : "string" + }, + "userProductActions" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "PermissionTypeEnum" : { + "enum" : [ "ADMIN", "ANY" ], + "type" : "string" + }, + "Problem" : { + "type" : "object", + "properties" : { + "detail" : { + "type" : "string" + }, + "instance" : { + "type" : "string" + }, + "invalidParams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/InvalidParam" + } + }, + "status" : { + "format" : "int32", + "type" : "integer" + }, + "title" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + } + }, + "Product" : { + "required" : [ "productId", "role", "productRoles" ], + "type" : "object", + "properties" : { + "productId" : { + "minLength" : 1, + "type" : "string" + }, + "role" : { + "description" : "Available values: MANAGER, DELEGATE, SUB_DELEGATE, OPERATOR, ADMIN_EA", + "type" : "string" + }, + "tokenId" : { + "type" : "string" + }, + "productRoles" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "delegationId" : { + "type" : "string" + } + } + }, + "Product1" : { + "required" : [ "productId", "role", "productRoles" ], + "type" : "object", + "properties" : { + "productId" : { + "minLength" : 1, + "type" : "string" + }, + "role" : { + "description" : "Available values: MANAGER, DELEGATE, SUB_DELEGATE, OPERATOR, ADMIN_EA", + "type" : "string" + }, + "tokenId" : { + "type" : "string" + }, + "productRoles" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "QueueEvent" : { + "enum" : [ "ADD", "UPDATE" ], + "type" : "string" + }, + "SearchUserDto" : { + "required" : [ "fiscalCode" ], + "type" : "object", + "properties" : { + "fiscalCode" : { + "type" : "string" + } + } + }, + "UpdateDescriptionDto" : { + "required" : [ "institutionDescription" ], + "type" : "object", + "properties" : { + "institutionDescription" : { + "minLength" : 1, + "type" : "string" + }, + "institutionRootName" : { + "type" : "string" + } + } + }, + "UpdateUserRequest" : { + "required" : [ "email" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "familyName" : { + "type" : "string" + }, + "email" : { + "type" : "string" + } + } + }, + "User" : { + "required" : [ "fiscalCode", "institutionEmail" ], + "type" : "object", + "properties" : { + "birthDate" : { + "type" : "string" + }, + "familyName" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "fiscalCode" : { + "minLength" : 1, + "type" : "string" + }, + "institutionEmail" : { + "minLength" : 1, + "type" : "string" + } + } + }, + "UserDataResponse" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + }, + "institutionId" : { + "type" : "string" + }, + "institutionDescription" : { + "type" : "string" + }, + "institutionRootName" : { + "type" : "string" + }, + "userMailUuid" : { + "type" : "string" + }, + "role" : { + "type" : "string" + }, + "status" : { + "type" : "string" + }, + "products" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/OnboardedProductResponse" + } + }, + "userResponse" : { + "$ref" : "#/components/schemas/UserResponse" + } + } + }, + "UserDetailResponse" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "fiscalCode" : { + "type" : "string" + }, + "name" : { + "$ref" : "#/components/schemas/CertifiableFieldResponseString" + }, + "familyName" : { + "$ref" : "#/components/schemas/CertifiableFieldResponseString" + }, + "email" : { + "$ref" : "#/components/schemas/CertifiableFieldResponseString" + }, + "workContacts" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/WorkContactResponse" + } + } + } + }, + "UserInfoResponse" : { + "type" : "object", + "properties" : { + "userId" : { + "type" : "string" + }, + "institutions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserInstitutionRoleResponse" + } + } + } + }, + "UserInstitutionResponse" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + }, + "institutionId" : { + "type" : "string" + }, + "institutionDescription" : { + "type" : "string" + }, + "institutionRootName" : { + "type" : "string" + }, + "userMailUuid" : { + "type" : "string" + }, + "products" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/OnboardedProductResponse" + } + } + } + }, + "UserInstitutionRoleResponse" : { + "type" : "object", + "properties" : { + "institutionId" : { + "type" : "string" + }, + "institutionName" : { + "type" : "string" + }, + "institutionRootName" : { + "type" : "string" + }, + "role" : { + "description" : "Available values: MANAGER, DELEGATE, SUB_DELEGATE, OPERATOR, ADMIN_EA", + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/OnboardedProductState" + } + } + }, + "UserInstitutionWithActions" : { + "type" : "object", + "properties" : { + "userId" : { + "type" : "string" + }, + "institutionId" : { + "type" : "string" + }, + "institutionDescription" : { + "type" : "string" + }, + "institutionRootName" : { + "type" : "string" + }, + "products" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/OnboardedProductWithActions" + } + }, + "userMailUuid" : { + "type" : "string" + } + } + }, + "UserNotificationResponse" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "institutionId" : { + "type" : "string" + }, + "productId" : { + "type" : "string" + }, + "onboardingTokenId" : { + "type" : "string" + }, + "createdAt" : { + "$ref" : "#/components/schemas/LocalDateTime" + }, + "updatedAt" : { + "$ref" : "#/components/schemas/LocalDateTime" + }, + "eventType" : { + "$ref" : "#/components/schemas/QueueEvent" + }, + "user" : { + "$ref" : "#/components/schemas/UserToNotify" + } + } + }, + "UserProductResponse" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "taxCode" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "surname" : { + "type" : "string" + }, + "email" : { + "type" : "string" + }, + "products" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/OnboardedProductResponse" + } + } + } + }, + "UserResponse" : { + "required" : [ "id", "name", "surname" ], + "type" : "object", + "properties" : { + "id" : { + "minLength" : 1, + "type" : "string" + }, + "taxCode" : { + "type" : "string" + }, + "name" : { + "pattern" : "\\S", + "type" : "string" + }, + "surname" : { + "pattern" : "\\S", + "type" : "string" + }, + "email" : { + "type" : "string" + }, + "workContacts" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + } + }, + "UserToNotify" : { + "type" : "object", + "properties" : { + "userId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "familyName" : { + "type" : "string" + }, + "email" : { + "type" : "string" + }, + "role" : { + "description" : "Available values: MANAGER, DELEGATE, SUB_DELEGATE, OPERATOR, ADMIN_EA", + "type" : "string" + }, + "productRole" : { + "type" : "string" + }, + "relationshipStatus" : { + "$ref" : "#/components/schemas/OnboardedProductState" + } + } + }, + "UsersNotificationResponse" : { + "type" : "object", + "properties" : { + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserNotificationResponse" + } + } + } + }, + "WorkContactResponse" : { + "type" : "object", + "properties" : { + "email" : { + "$ref" : "#/components/schemas/CertifiableFieldResponseString" + } + } + } + }, + "securitySchemes" : { + "SecurityScheme" : { + "type" : "http", + "description" : "Authentication", + "scheme" : "bearer", + "bearerFormat" : "JWT" + } + } + } +} \ No newline at end of file diff --git a/apps/onboarding-ms/src/main/resources/application.properties b/apps/onboarding-ms/src/main/resources/application.properties index a93d90a88..e1c24e271 100644 --- a/apps/onboarding-ms/src/main/resources/application.properties +++ b/apps/onboarding-ms/src/main/resources/application.properties @@ -75,9 +75,17 @@ quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.AooApi".u quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.InfocamerePdndApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.InstitutionApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.GeographicTaxonomiesApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} +quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.InfocamereApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} +quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.NationalRegistriesApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} quarkus.rest-client."org.openapi.quarkus.core_json.api.InstitutionApi".url=${MS_CORE_URL:http://localhost:8080} quarkus.rest-client."org.openapi.quarkus.core_json.api.InstitutionApi".read-timeout=60000 +quarkus.openapi-generator.codegen.spec.user_json.mutiny=true +quarkus.openapi-generator.codegen.spec.user_json.additional-model-type-annotations=@lombok.Builder; @lombok.NoArgsConstructor; @lombok.AllArgsConstructor +quarkus.openapi-generator.codegen.spec.user_json.enable-security-generation=false +quarkus.rest-client."org.openapi.quarkus.user_json.api.InstitutionApi".url=${USER_URL:http://localhost:8080} + + mp.openapi.extensions.smallrye.operationIdStrategy=METHOD onboarding-ms.istat-cache-duration-minutes=${ISTAT_CACHE_DURATION_MIN:30} 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 20d7f11b8..b071894c8 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 @@ -57,13 +57,9 @@ import org.openapi.quarkus.core_json.model.InstitutionsResponse; 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.InfocamerePdndApi; -import org.openapi.quarkus.party_registry_proxy_json.api.UoApi; -import org.openapi.quarkus.party_registry_proxy_json.model.AOOResource; -import org.openapi.quarkus.party_registry_proxy_json.model.InstitutionResource; -import org.openapi.quarkus.party_registry_proxy_json.model.PDNDBusinessResource; -import org.openapi.quarkus.party_registry_proxy_json.model.UOResource; +import org.openapi.quarkus.party_registry_proxy_json.api.*; +import org.openapi.quarkus.party_registry_proxy_json.model.*; +import org.openapi.quarkus.user_json.model.UserInstitutionResponse; import org.openapi.quarkus.user_registry_json.api.UserApi; import org.openapi.quarkus.user_registry_json.model.CertifiableFieldResourceOfstring; import org.openapi.quarkus.user_registry_json.model.UserId; @@ -119,6 +115,18 @@ class OnboardingServiceDefaultTest { @RestClient InfocamerePdndApi infocamerePdndApi; + @RestClient + @InjectMock + InfocamereApi infocamereApi; + + @RestClient + @InjectMock + NationalRegistriesApi nationalRegistriesApi; + + @RestClient + @InjectMock + org.openapi.quarkus.user_json.api.InstitutionApi userInstitutionApi; + @InjectMock AzureBlobClient azureBlobClient; @@ -2342,4 +2350,157 @@ void testCheckManageResourceNotFound() { subscriber.assertFailedWith(ResourceNotFoundException.class); } + + @Test + @RunOnVertxContext + void testOnboardingUserPgFailsWhenUserListIsWrong(UniAsserter asserter) { + assertThrows(InvalidRequestException.class, () -> onboardingService.onboardingUserPg(new Onboarding(), new ArrayList<>())); + } + + @Test + @RunOnVertxContext + void testOnboardingUserPgFailsWhenInstitutionWasNotPreviouslyOnboarded(UniAsserter asserter) { + Onboarding onboarding = createDummyOnboarding(); + List userRequests = List.of(manager); + + asserter.execute(() -> { + PanacheMock.mock(Onboarding.class); + ReactivePanacheQuery query = Mockito.mock(ReactivePanacheQuery.class); + when(query.stream()).thenReturn(Multi.createFrom().empty()); + when(Onboarding.find((Document) any(), any())).thenReturn(query); + }); + + asserter.assertFailedWith(() -> onboardingService.onboardingUserPg(onboarding, userRequests), ResourceNotFoundException.class); + } + + @Test + @RunOnVertxContext + void testOnboardingUserPgFailsWhenUserWasAlreadyManager(UniAsserter asserter) { + Onboarding previousOnboarding = createDummyOnboarding(); + previousOnboarding.getInstitution().setInstitutionType(InstitutionType.PG); + + Onboarding newOnboarding = createDummyOnboarding(); + newOnboarding.getInstitution().setInstitutionType(InstitutionType.PG); + + List userRequests = List.of(manager); + + mockFindOnboarding(asserter, previousOnboarding); + mockSimpleProductValidAssert(newOnboarding.getProductId(), false, asserter); + mockSimpleSearchPOSTAndPersist(asserter); + + mockRetrieveUserInstitutions(newOnboarding.getInstitution().getId(), managerResource.getId().toString(), asserter); + + asserter.assertFailedWith(() -> onboardingService.onboardingUserPg(newOnboarding, userRequests), InvalidRequestException.class); + + } + + @Test + @RunOnVertxContext + void testOnboardingUserPgFailsWhenUserIsNotManagerOnInfocamereRegistry(UniAsserter asserter) { + Onboarding previousOnboarding = createDummyOnboarding(); + previousOnboarding.getInstitution().setInstitutionType(InstitutionType.PG); + previousOnboarding.getInstitution().setOrigin(Origin.INFOCAMERE); + + Onboarding newOnboarding = createDummyOnboarding(); + newOnboarding.getInstitution().setInstitutionType(InstitutionType.PG); + newOnboarding.getInstitution().setOrigin(Origin.INFOCAMERE); + + List userRequests = List.of(manager); + + mockFindOnboarding(asserter, previousOnboarding); + mockSimpleProductValidAssert(newOnboarding.getProductId(), false, asserter); + mockSimpleSearchPOSTAndPersist(asserter); + + mockRetrieveUserInstitutions(newOnboarding.getInstitution().getId(), "old-manager-id", asserter); + + asserter.execute(() -> when(infocamereApi.institutionsByLegalTaxIdUsingPOST(any())) + .thenReturn(Uni.createFrom().item(new BusinessesResource()))); + + asserter.assertFailedWith(() -> onboardingService.onboardingUserPg(newOnboarding, userRequests), InvalidRequestException.class); + } + + @Test + @RunOnVertxContext + void testOnboardingUserPgFailsWhenUserIsNotManagerOnAdeRegistry(UniAsserter asserter) { + Onboarding previousOnboarding = createDummyOnboarding(); + previousOnboarding.getInstitution().setInstitutionType(InstitutionType.PG); + previousOnboarding.getInstitution().setOrigin(Origin.ADE); + + Onboarding newOnboarding = createDummyOnboarding(); + newOnboarding.getInstitution().setInstitutionType(InstitutionType.PG); + newOnboarding.getInstitution().setOrigin(Origin.ADE); + + List userRequests = List.of(manager); + + mockFindOnboarding(asserter, previousOnboarding); + mockSimpleProductValidAssert(newOnboarding.getProductId(), false, asserter); + mockSimpleSearchPOSTAndPersist(asserter); + + mockRetrieveUserInstitutions(newOnboarding.getInstitution().getId(), "old-manager-id", asserter); + + LegalVerificationResult legalVerificationResult = new LegalVerificationResult(); + legalVerificationResult.setVerificationResult(false); + + asserter.execute(() -> when(nationalRegistriesApi.verifyLegalUsingGET(any(), any())) + .thenReturn(Uni.createFrom().item(legalVerificationResult))); + + asserter.assertFailedWith(() -> onboardingService.onboardingUserPg(newOnboarding, userRequests), InvalidRequestException.class); + } + + @Test + @RunOnVertxContext + void testOnboardingUserPg(UniAsserter asserter) { + Onboarding previousOnboarding = createDummyOnboarding(); + previousOnboarding.getInstitution().setInstitutionType(InstitutionType.PG); + previousOnboarding.getInstitution().setOrigin(Origin.ADE); + + Onboarding newOnboarding = createDummyOnboarding(); + newOnboarding.getInstitution().setInstitutionType(InstitutionType.PG); + newOnboarding.getInstitution().setOrigin(Origin.ADE); + + List userRequests = List.of(manager); + + mockFindOnboarding(asserter, previousOnboarding); + mockSimpleProductValidAssert(newOnboarding.getProductId(), false, asserter); + mockSimpleSearchPOSTAndPersist(asserter); + + mockRetrieveUserInstitutions(newOnboarding.getInstitution().getId(), "old-manager-id", asserter); + + LegalVerificationResult legalVerificationResult = new LegalVerificationResult(); + legalVerificationResult.setVerificationResult(true); + + asserter.execute(() -> when(nationalRegistriesApi.verifyLegalUsingGET(any(), any())) + .thenReturn(Uni.createFrom().item(legalVerificationResult))); + + asserter.execute(() -> when(Onboarding.persistOrUpdate(any(List.class))) + .thenAnswer(arg -> { + List onboardings = (List) arg.getArguments()[0]; + onboardings.get(0).setId(UUID.randomUUID().toString()); + return Uni.createFrom().nullItem(); + })); + + asserter.execute(() -> when(orchestrationApi.apiStartOnboardingOrchestrationGet(any(), any())) + .thenReturn(Uni.createFrom().item(new OrchestrationResponse()))); + + asserter.assertNotNull(() -> onboardingService.onboardingUserPg(newOnboarding, userRequests)); + } + + private void mockRetrieveUserInstitutions(String institutionId, String userId, UniAsserter asserter) { + UserInstitutionResponse userInstitutionResponse = new UserInstitutionResponse(); + userInstitutionResponse.setId("test"); + userInstitutionResponse.setInstitutionId(institutionId); + userInstitutionResponse.setUserId(userId); + + asserter.execute(() -> when(userInstitutionApi.retrieveUserInstitutions(any(), any(), any(), any(), any(), any())) + .thenReturn(Uni.createFrom().item(List.of(userInstitutionResponse)))); + } + + private void mockFindOnboarding(UniAsserter asserter, Onboarding onboarding) { + asserter.execute(() -> { + PanacheMock.mock(Onboarding.class); + ReactivePanacheQuery query = Mockito.mock(ReactivePanacheQuery.class); + when(query.stream()).thenReturn(Multi.createFrom().item(onboarding)); + when(Onboarding.find((Document) any(), any())).thenReturn(query); + }); + } } From 0ece72a9489a2c88feec5e5cdd8668cf9c07ecaa Mon Sep 17 00:00:00 2001 From: empassaro <113031808+empassaro@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:24:12 +0200 Subject: [PATCH 3/8] [PNPG-244] feat: added API onboarding/users/pg-from-ic-and-ade (#552) --- apps/onboarding-ms/src/main/docs/openapi.json | 64 +++++++++++++++++++ apps/onboarding-ms/src/main/docs/openapi.yaml | 48 ++++++++++++++ .../controller/OnboardingController.java | 12 ++++ .../request/OnboardingUserPgRequest.java | 22 +++++++ .../onboarding/mapper/OnboardingMapper.java | 9 +++ .../service/OnboardingServiceDefault.java | 3 - .../controller/OnboardingControllerTest.java | 41 ++++++++++++ 7 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/controller/request/OnboardingUserPgRequest.java diff --git a/apps/onboarding-ms/src/main/docs/openapi.json b/apps/onboarding-ms/src/main/docs/openapi.json index 6379e97f6..1e853ee33 100644 --- a/apps/onboarding-ms/src/main/docs/openapi.json +++ b/apps/onboarding-ms/src/main/docs/openapi.json @@ -938,6 +938,44 @@ } ] } }, + "/v1/onboarding/users/pg-from-ic-and-ade" : { + "post" : { + "tags" : [ "Onboarding Controller" ], + "summary" : "Add Manager of PG institution", + "description" : "Create new onboarding request to add new Manager and replace the old inactive Managers of the institution.", + "operationId" : "onboardingUsersPgFromIcAndAde", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/OnboardingUserPgRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/OnboardingResponse" + } + } + } + }, + "401" : { + "description" : "Not Authorized" + }, + "403" : { + "description" : "Not Allowed" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, "/v1/onboarding/verify" : { "head" : { "tags" : [ "Onboarding Controller" ], @@ -2415,6 +2453,32 @@ "enum" : [ "REQUEST", "TOBEVALIDATED", "PENDING", "COMPLETED", "FAILED", "REJECTED", "DELETED" ], "type" : "string" }, + "OnboardingUserPgRequest" : { + "required" : [ "productId", "users", "taxCode", "origin" ], + "type" : "object", + "properties" : { + "productId" : { + "minLength" : 1, + "type" : "string" + }, + "institutionType" : { + "$ref" : "#/components/schemas/InstitutionType" + }, + "users" : { + "minItems" : 1, + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserRequest" + } + }, + "taxCode" : { + "type" : "string" + }, + "origin" : { + "$ref" : "#/components/schemas/Origin" + } + } + }, "OnboardingUserRequest" : { "required" : [ "productId", "users" ], "type" : "object", diff --git a/apps/onboarding-ms/src/main/docs/openapi.yaml b/apps/onboarding-ms/src/main/docs/openapi.yaml index 99e073ec8..ee62ce68f 100644 --- a/apps/onboarding-ms/src/main/docs/openapi.yaml +++ b/apps/onboarding-ms/src/main/docs/openapi.yaml @@ -679,6 +679,32 @@ paths: description: Not Allowed security: - SecurityScheme: [] + /v1/onboarding/users/pg-from-ic-and-ade: + post: + tags: + - Onboarding Controller + summary: Add Manager of PG institution + description: Create new onboarding request to add new Manager and replace the + old inactive Managers of the institution. + operationId: onboardingUsersPgFromIcAndAde + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/OnboardingUserPgRequest" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/OnboardingResponse" + "401": + description: Not Authorized + "403": + description: Not Allowed + security: + - SecurityScheme: [] /v1/onboarding/verify: head: tags: @@ -1766,6 +1792,28 @@ components: - REJECTED - DELETED type: string + OnboardingUserPgRequest: + required: + - productId + - users + - taxCode + - origin + type: object + properties: + productId: + minLength: 1 + type: string + institutionType: + $ref: "#/components/schemas/InstitutionType" + users: + minItems: 1 + type: array + items: + $ref: "#/components/schemas/UserRequest" + taxCode: + type: string + origin: + $ref: "#/components/schemas/Origin" OnboardingUserRequest: required: - productId 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 a9736ebf4..c9eeeb707 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 @@ -214,6 +214,18 @@ public Uni onboardingPgCompletion(@Valid OnboardingPgRequest .onboardingCompletion(fillUserId(onboardingMapper.toEntity(onboardingRequest), userId), onboardingRequest.getUsers())); } + @Operation( + summary = "Add Manager of PG institution", + description = "Create new onboarding request to add new Manager and replace the old inactive Managers of the institution." + ) + @POST + @Path("/users/pg-from-ic-and-ade") + public Uni onboardingUsersPgFromIcAndAde(@Valid OnboardingUserPgRequest onboardingRequest, @Context SecurityContext ctx) { + return readUserIdFromToken(ctx) + .onItem().transformToUni(userId -> onboardingService + .onboardingUserPg(fillUserId(onboardingMapper.toEntity(onboardingRequest), userId), onboardingRequest.getUsers())); + } + private Uni readUserIdFromToken(SecurityContext ctx) { return currentIdentityAssociation.getDeferredIdentity() diff --git a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/controller/request/OnboardingUserPgRequest.java b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/controller/request/OnboardingUserPgRequest.java new file mode 100644 index 000000000..69c5b740b --- /dev/null +++ b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/controller/request/OnboardingUserPgRequest.java @@ -0,0 +1,22 @@ +package it.pagopa.selfcare.onboarding.controller.request; + +import it.pagopa.selfcare.onboarding.common.InstitutionType; +import it.pagopa.selfcare.onboarding.common.Origin; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Data +public class OnboardingUserPgRequest { + @NotEmpty(message = "productId is required") + private String productId; + private InstitutionType institutionType = InstitutionType.PG; + @NotEmpty(message = "at least one user is required") + private List users; + @NotNull(message = "taxCode is required") + private String taxCode; + @NotNull + private Origin origin; +} diff --git a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/mapper/OnboardingMapper.java b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/mapper/OnboardingMapper.java index 1e299177c..3947028c5 100644 --- a/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/mapper/OnboardingMapper.java +++ b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/mapper/OnboardingMapper.java @@ -49,6 +49,15 @@ public interface OnboardingMapper { @Mapping(target = "id", expression = "java(UUID.randomUUID().toString())") Onboarding toEntity(OnboardingPgRequest request); + @Mapping(source = "taxCode", target = "institution.taxCode") + @Mapping(source = "origin", target = "institution.origin") + @Mapping(source = "institutionType", target = "institution.institutionType") + @Mapping(target = "workflowType", expression = "java(WorkflowType.USERS_PG)") + @Mapping(target = "status", expression = "java(OnboardingStatus.PENDING)") + @Mapping(target = "createdAt", expression = "java(LocalDateTime.now())") + @Mapping(target = "id", expression = "java(UUID.randomUUID().toString())") + Onboarding toEntity(OnboardingUserPgRequest request); + @Mapping(target = "id", expression = "java(UUID.randomUUID().toString())") @Mapping(target = "userRequestUid", source = "userId") @Mapping(target = "productId", source = "request.productId") 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 dea8bef28..f56eb93d5 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 @@ -1315,9 +1315,6 @@ private Uni getEC() { */ public Uni onboardingUserPg(Onboarding onboarding, List userRequests) { checkOnboardingPgUserList(userRequests); - onboarding.setWorkflowType(WorkflowType.USERS_PG); - onboarding.setStatus(OnboardingStatus.PENDING); - onboarding.setCreatedAt(LocalDateTime.now()); return retrievePreviousCompletedOnboarding(onboarding) .map(previousOnboarding -> copyDataFromPreviousToCurrentOnboarding(previousOnboarding, onboarding)) 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 4da9188a4..34b9f4010 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 @@ -53,6 +53,8 @@ class OnboardingControllerTest { static final InstitutionBaseRequest institution; static final InstitutionPspRequest institutionPsp; + static final OnboardingUserPgRequest onboardingUserPgValid; + @InjectMock OnboardingService onboardingService; @@ -95,6 +97,12 @@ class OnboardingControllerTest { onboardingPgValid.setDigitalAddress("email@pagopa.it"); onboardingPgValid.setUsers(List.of(userDTO)); + onboardingUserPgValid = new OnboardingUserPgRequest(); + onboardingUserPgValid.setTaxCode("code"); + onboardingUserPgValid.setProductId("productId"); + onboardingUserPgValid.setUsers(List.of(userDTO)); + onboardingUserPgValid.setOrigin(Origin.ADE); + } @Test @@ -628,6 +636,39 @@ void onboardingCompletePg() { assertEquals(InstitutionType.PG, captor.getValue().getInstitution().getInstitutionType()); } + @Test + @TestSecurity(user = "userJwt") + void onboardingPgUser() { + Mockito.when(onboardingService.onboardingUserPg(any(), any())) + .thenReturn(Uni.createFrom().item(new OnboardingResponse())); + + given() + .when() + .body(onboardingUserPgValid) + .contentType(ContentType.JSON) + .post("/users/pg-from-ic-and-ade") + .then() + .statusCode(200); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Onboarding.class); + Mockito.verify(onboardingService, times(1)) + .onboardingUserPg(captor.capture(), any()); + assertEquals(InstitutionType.PG, captor.getValue().getInstitution().getInstitutionType()); + } + + @Test + @TestSecurity(user = "userJwt") + void onboardingPgUserFailsWithWrongBody() { + + given() + .when() + .body(new OnboardingUserPgRequest()) + .contentType(ContentType.JSON) + .post("/users/pg-from-ic-and-ade") + .then() + .statusCode(400); + } + @Test @TestSecurity(user = "userJwt") void onboardingImport() { From af12a6ec91bd3c77591775f4c06661e9b71c4c15 Mon Sep 17 00:00:00 2001 From: empassaro <113031808+empassaro@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:33:57 +0200 Subject: [PATCH 4/8] [PNPG-250] feat: DELETE_MANAGERS_BY_IC_AND_ADE activity implementation (#544) --- .../functions/OnboardingFunctions.java | 6 + .../functions/utils/ActivityName.java | 1 + .../onboarding/service/CompletionService.java | 2 + .../service/CompletionServiceDefault.java | 126 +++++++++++++++- .../src/main/resources/application.properties | 7 + .../functions/OnboardingFunctionsTest.java | 13 ++ .../service/CompletionServiceDefaultTest.java | 136 +++++++++++++++++- 7 files changed, 283 insertions(+), 8 deletions(-) diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java index 48efd4323..b70758330 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java @@ -311,4 +311,10 @@ public void createAggregatesCsv(@DurableActivityTrigger(name = "onboardingString context.getLogger().info(String.format(FORMAT_LOGGER_ONBOARDING_STRING, CREATE_AGGREGATES_CSV_ACTIVITY, onboardingWorkflowString)); contractService.uploadAggregatesCsv(readOnboardingWorkflowValue(objectMapper, onboardingWorkflowString)); } + + @FunctionName(DELETE_MANAGERS_BY_IC_AND_ADE) + public void deleteOldPgManagers(@DurableActivityTrigger(name = "onboardingString") String onboardingString, final ExecutionContext context) { + context.getLogger().info(() -> String.format(FORMAT_LOGGER_ONBOARDING_STRING, DELETE_MANAGERS_BY_IC_AND_ADE, onboardingString)); + completionService.deleteOldPgManagers(readOnboardingValue(objectMapper, onboardingString)); + } } diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/utils/ActivityName.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/utils/ActivityName.java index 6b01f699f..5251ab1d2 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/utils/ActivityName.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/utils/ActivityName.java @@ -24,6 +24,7 @@ public class ActivityName { public static final String RESEND_NOTIFICATIONS_ACTIVITY = "ResendNotificationsActivity"; public static final String CREATE_AGGREGATES_CSV_ACTIVITY = "CreateAggregatesCsv"; public static final String EXISTS_DELEGATION_ACTIVITY = "ExistsDelegationActivity"; + public static final String DELETE_MANAGERS_BY_IC_AND_ADE = "DeleteManagersByIcAndAde"; private ActivityName() { } diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionService.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionService.java index 51f5fd77c..8c4c46593 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionService.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionService.java @@ -31,4 +31,6 @@ public interface CompletionService { String existsDelegation(OnboardingAggregateOrchestratorInput onboardingAggregateOrchestratorInput); + void deleteOldPgManagers(Onboarding onboarding); + } diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefault.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefault.java index e619c3bbc..8ec10ff4e 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefault.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefault.java @@ -1,10 +1,7 @@ package it.pagopa.selfcare.onboarding.service; import com.microsoft.azure.functions.ExecutionContext; -import it.pagopa.selfcare.onboarding.common.InstitutionPaSubunitType; -import it.pagopa.selfcare.onboarding.common.InstitutionType; -import it.pagopa.selfcare.onboarding.common.OnboardingStatus; -import it.pagopa.selfcare.onboarding.common.Origin; +import it.pagopa.selfcare.onboarding.common.*; import it.pagopa.selfcare.onboarding.dto.OnboardingAggregateOrchestratorInput; import it.pagopa.selfcare.onboarding.entity.*; import it.pagopa.selfcare.onboarding.exception.GenericOnboardingException; @@ -28,8 +25,16 @@ import org.openapi.quarkus.core_json.api.InstitutionApi; import org.openapi.quarkus.core_json.model.*; import org.openapi.quarkus.party_registry_proxy_json.api.AooApi; +import org.openapi.quarkus.party_registry_proxy_json.api.InfocamereApi; +import org.openapi.quarkus.party_registry_proxy_json.api.NationalRegistriesApi; import org.openapi.quarkus.party_registry_proxy_json.api.UoApi; +import org.openapi.quarkus.party_registry_proxy_json.model.BusinessesResource; +import org.openapi.quarkus.party_registry_proxy_json.model.GetInstitutionsByLegalDto; +import org.openapi.quarkus.party_registry_proxy_json.model.GetInstitutionsByLegalFilterDto; +import org.openapi.quarkus.party_registry_proxy_json.model.LegalVerificationResult; import org.openapi.quarkus.user_json.model.AddUserRoleDto; +import org.openapi.quarkus.user_json.model.OnboardedProductState; +import org.openapi.quarkus.user_json.model.UserInstitutionResponse; import org.openapi.quarkus.user_registry_json.api.UserApi; import java.time.LocalDateTime; @@ -43,7 +48,6 @@ import static it.pagopa.selfcare.onboarding.common.OnboardingStatus.REJECTED; import static it.pagopa.selfcare.onboarding.common.PartyRole.MANAGER; import static it.pagopa.selfcare.onboarding.common.WorkflowType.CONFIRMATION_AGGREGATE; -import static it.pagopa.selfcare.onboarding.service.OnboardingService.USERS_FIELD_LIST; import static jakarta.ws.rs.core.Response.Status.Family.SUCCESSFUL; import static org.openapi.quarkus.core_json.model.DelegationResponse.StatusEnum.ACTIVE; @@ -71,6 +75,16 @@ public class CompletionServiceDefault implements CompletionService { @RestClient @Inject org.openapi.quarkus.party_registry_proxy_json.api.InstitutionApi institutionRegistryProxyApi; + @RestClient + @Inject + org.openapi.quarkus.user_json.api.InstitutionApi userInstitutionApi; + @RestClient + @Inject + InfocamereApi infocamereApi; + @RestClient + @Inject + NationalRegistriesApi nationalRegistriesApi; + private final InstitutionMapper institutionMapper; private final OnboardingRepository onboardingRepository; @@ -82,6 +96,7 @@ public class CompletionServiceDefault implements CompletionService { private final OnboardingMapper onboardingMapper; private final boolean hasToSendEmail; private final boolean forceInstitutionCreation; + private static final String USERS_FIELD_LIST = "fiscalCode,familyName,name"; public CompletionServiceDefault(ProductService productService, NotificationService notificationService, @@ -376,4 +391,105 @@ private List getDestinationMails(Onboarding onboarding) { .map(workContract -> workContract.getEmail().getValue()) .collect(Collectors.toList()); } + + @Override + public void deleteOldPgManagers(Onboarding onboarding) { + String institutionId = onboarding.getInstitution().getId(); + String productId = onboarding.getProductId(); + List oldPgManagersUid = retrieveActiveManagersOnInstitution(institutionId, productId); + Origin origin = onboarding.getInstitution().getOrigin(); + + oldPgManagersUid.stream() + .map(uid -> new PgManagerInfo(uid, retrieveTaxCode(uid))) + .filter(pgManagerInfo -> !isActiveManagerOnRegistries(pgManagerInfo.getTaxCode(), onboarding.getInstitution().getTaxCode(), origin)) + .forEach(pgManagerInfo -> deleteManagerFromProduct(pgManagerInfo.getUid(), institutionId, productId)); + } + + private List retrieveActiveManagersOnInstitution(String institutionId, String productId) { + List activeManagers = userInstitutionApi.institutionsInstitutionIdUserInstitutionsGet( + institutionId, + null, + List.of(productId), + List.of(MANAGER.name()), + List.of(OnboardedProductState.ACTIVE.name()), + null + ); + + if(Objects.isNull(activeManagers) || CollectionUtils.isEmpty(activeManagers)) { + return Collections.emptyList(); + } + + return activeManagers.stream() + .map(UserInstitutionResponse::getUserId) + .toList(); + } + + private String retrieveTaxCode(String uid) { + return userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, uid).getFiscalCode(); + } + + private boolean isActiveManagerOnRegistries(String userTaxCode, String institutionTaxCode, Origin origin) { + return switch (origin) { + case INFOCAMERE -> isActiveManagerOnInfocamereRegistry(userTaxCode, institutionTaxCode); + case ADE -> isActiveManagerOnAdeRegistry(userTaxCode, institutionTaxCode); + default -> throw new GenericOnboardingException("Origin not supported"); + }; + } + + private boolean isActiveManagerOnInfocamereRegistry(String userTaxCode, String institutionTaxCode) { + BusinessesResource businessesResource = infocamereApi.institutionsByLegalTaxIdUsingPOST(toGetInstitutionByLegalDto(userTaxCode)); + if(Objects.isNull(businessesResource) || CollectionUtils.isEmpty(businessesResource.getBusinesses())) { + return false; + } + + return businessesResource.getBusinesses().stream() + .anyMatch(business -> institutionTaxCode.equals(business.getBusinessTaxId())); + } + + private GetInstitutionsByLegalDto toGetInstitutionByLegalDto(String userTaxCode) { + GetInstitutionsByLegalDto getInstitutionsByLegalDto = new GetInstitutionsByLegalDto(); + GetInstitutionsByLegalFilterDto getInstitutionsByLegalFilterDto = new GetInstitutionsByLegalFilterDto(); + getInstitutionsByLegalFilterDto.setLegalTaxId(userTaxCode); + getInstitutionsByLegalDto.setFilter(getInstitutionsByLegalFilterDto); + return getInstitutionsByLegalDto; + } + + private boolean isActiveManagerOnAdeRegistry(String userTaxCode, String institutionTaxCode) { + try { + LegalVerificationResult legalVerificationResult = nationalRegistriesApi.verifyLegalUsingGET(userTaxCode, institutionTaxCode); + return legalVerificationResult.getVerificationResult(); + } catch (WebApplicationException e) { + // 400 status code means that the user is not a manager of the institution + if (e.getResponse().getStatus() == 400) { + return false; + } + throw new GenericOnboardingException(String.format("Error during verify legal %s", e.getMessage())); + } + } + + private void deleteManagerFromProduct(String uid, String institutionId, String productId) { + try (Response response = userApi.usersUserIdInstitutionsInstitutionIdProductsProductIdDelete(institutionId, productId, uid)) { + if (!SUCCESSFUL.equals(response.getStatusInfo().getFamily())) { + throw new GenericOnboardingException(String.format("Failed to delete user %s from product %s in institution %s", uid, productId, institutionId)); + } + } + } + + private static class PgManagerInfo { + private final String uid; + private final String taxCode; + + public PgManagerInfo(String uid, String taxCode) { + this.uid = uid; + this.taxCode = taxCode; + } + + public String getUid() { + return uid; + } + + public String getTaxCode() { + return taxCode; + } + } } diff --git a/apps/onboarding-functions/src/main/resources/application.properties b/apps/onboarding-functions/src/main/resources/application.properties index bdb3d6820..685bd89eb 100644 --- a/apps/onboarding-functions/src/main/resources/application.properties +++ b/apps/onboarding-functions/src/main/resources/application.properties @@ -62,6 +62,9 @@ quarkus.openapi-generator.codegen.spec.user_json.additional-api-type-annotations quarkus.rest-client."org.openapi.quarkus.user_json.api.UserApi".url=${MS_USER_URL:http://localhost:8081} quarkus.rest-client."org.openapi.quarkus.user_json.api.UserApi".read-timeout=60000 quarkus.rest-client."org.openapi.quarkus.user_json.api.UserApi".connection-pool-size=1024 +quarkus.rest-client."org.openapi.quarkus.user_json.api.InstitutionApi".url=${MS_USER_URL:http://localhost:8081} +quarkus.rest-client."org.openapi.quarkus.user_json.api.InstitutionApi".read-timeout=60000 +quarkus.rest-client."org.openapi.quarkus.user_json.api.InstitutionApi".connection-pool-size=1024 quarkus.openapi-generator.codegen.spec.party_registry_proxy_json.enable-security-generation=false quarkus.openapi-generator.codegen.spec.party_registry_proxy_json.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders(it.pagopa.selfcare.onboarding.client.auth.AuthenticationPropagationHeadersFactory.class) @@ -69,10 +72,14 @@ quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.UoApi".ur quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.AooApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.InstitutionApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.GeographicTaxonomiesApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} +quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.InfocamereApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} +quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.NationalRegistriesApi".url=${MS_PARTY_REGISTRY_URL:http://localhost:8080} quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.UoApi".read-timeout=30000 quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.AooApi".read-timeout=30000 quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.InstitutionApi".read-timeout=30000 quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.GeographicTaxonomiesApi".read-timeout=30000 +quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.InfocamereApi".read-timeout=30000 +quarkus.rest-client."org.openapi.quarkus.party_registry_proxy_json.api.NationalRegistriesApi".read-timeout=30000 ## AZURE STORAGE ## diff --git a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java index 8fe643cf6..da679df88 100644 --- a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java +++ b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java @@ -882,4 +882,17 @@ void sendTestEmail() { verify(completionService, times(1)) .sendTestEmail(executionContext); } + + @Test + void deleteOldPgManagers() { + final String onboardingString = "{\"onboardingId\":\"onboardingId\"}"; + + when(executionContext.getLogger()).thenReturn(Logger.getGlobal()); + doNothing().when(completionService).deleteOldPgManagers(any()); + + function.deleteOldPgManagers(onboardingString, executionContext); + + verify(completionService, times(1)) + .deleteOldPgManagers(any()); + } } \ No newline at end of file diff --git a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefaultTest.java b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefaultTest.java index a34aa065f..78b2d2b01 100644 --- a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefaultTest.java +++ b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefaultTest.java @@ -32,10 +32,11 @@ import org.openapi.quarkus.core_json.api.InstitutionApi; import org.openapi.quarkus.core_json.model.*; import org.openapi.quarkus.party_registry_proxy_json.api.AooApi; +import org.openapi.quarkus.party_registry_proxy_json.api.InfocamereApi; +import org.openapi.quarkus.party_registry_proxy_json.api.NationalRegistriesApi; import org.openapi.quarkus.party_registry_proxy_json.api.UoApi; -import org.openapi.quarkus.party_registry_proxy_json.model.AOOResource; -import org.openapi.quarkus.party_registry_proxy_json.model.InstitutionResource; -import org.openapi.quarkus.party_registry_proxy_json.model.UOResource; +import org.openapi.quarkus.party_registry_proxy_json.model.*; +import org.openapi.quarkus.user_json.model.UserInstitutionResponse; import org.openapi.quarkus.user_registry_json.api.UserApi; import org.openapi.quarkus.user_registry_json.model.UserResource; import org.openapi.quarkus.user_registry_json.model.WorkContactResource; @@ -84,6 +85,15 @@ public class CompletionServiceDefaultTest { @RestClient @InjectMock DelegationApi delegationApi; + @RestClient + @InjectMock + org.openapi.quarkus.user_json.api.InstitutionApi userInstitutionApi; + @RestClient + @InjectMock + InfocamereApi infocamereApi; + @RestClient + @InjectMock + NationalRegistriesApi nationalRegistriesApi; final String productId = "productId"; private static final UserResource userResource; @@ -827,6 +837,126 @@ void forceInstitutionCreationFlagTrue(){ verify(institutionApi, times(1)).getInstitutionsUsingGET(any(), any(), any(), any()); } + @Test + void deleteOldPgManagers_shouldDeleteInactiveManagers_OnInfocamere() { + Onboarding onboarding = createOnboarding(); + onboarding.getInstitution().setId("institution-id"); + onboarding.getInstitution().setTaxCode("institution-tax-code"); + onboarding.getInstitution().setOrigin(Origin.INFOCAMERE); + + UserInstitutionResponse user1 = new UserInstitutionResponse(); + user1.setUserId("user1"); + UserInstitutionResponse user2 = new UserInstitutionResponse(); + user2.setUserId("user2"); + when(userInstitutionApi.institutionsInstitutionIdUserInstitutionsGet( + eq("institution-id"), any(), eq(List.of("productId")), eq(List.of("MANAGER")), eq(List.of("ACTIVE")), any())) + .thenReturn(List.of(user1, user2)); + + UserResource userResource1 = new UserResource(); + userResource1.setFiscalCode("taxCode1"); + UserResource userResource2 = new UserResource(); + userResource2.setFiscalCode("taxCode2"); + + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, "user1")).thenReturn(userResource1); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, "user2")).thenReturn(userResource2); + + when(infocamereApi.institutionsByLegalTaxIdUsingPOST(any())).thenReturn(new BusinessesResource()); + + Response responseOk = new ServerResponse(null, 204, null); + when(userControllerApi.usersUserIdInstitutionsInstitutionIdProductsProductIdDelete("institution-id","productId", "user1")) + .thenReturn(responseOk); + when(userControllerApi.usersUserIdInstitutionsInstitutionIdProductsProductIdDelete("institution-id","productId", "user2")) + .thenReturn(responseOk); + + completionServiceDefault.deleteOldPgManagers(onboarding); + + verify(userControllerApi, times(2)).usersUserIdInstitutionsInstitutionIdProductsProductIdDelete(eq("institution-id"), eq("productId"), any()); + } + + @Test + void deleteOldPgManagers_shouldDeleteInactiveManagers_OnAde() { + Onboarding onboarding = createOnboarding(); + onboarding.getInstitution().setId("institution-id"); + onboarding.getInstitution().setTaxCode("institution-tax-code"); + onboarding.getInstitution().setOrigin(Origin.ADE); + + UserInstitutionResponse user1 = new UserInstitutionResponse(); + user1.setUserId("user1"); + UserInstitutionResponse user2 = new UserInstitutionResponse(); + user2.setUserId("user2"); + when(userInstitutionApi.institutionsInstitutionIdUserInstitutionsGet( + eq("institution-id"), any(), eq(List.of("productId")), eq(List.of("MANAGER")), eq(List.of("ACTIVE")), any())) + .thenReturn(List.of(user1, user2)); + + UserResource userResource1 = new UserResource(); + userResource1.setFiscalCode("taxCode1"); + UserResource userResource2 = new UserResource(); + userResource2.setFiscalCode("taxCode2"); + + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, "user1")).thenReturn(userResource1); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, "user2")).thenReturn(userResource2); + + LegalVerificationResult legalVerificationResult = new LegalVerificationResult(); + legalVerificationResult.setVerificationResult(false); + when(nationalRegistriesApi.verifyLegalUsingGET(eq("taxCode1"), any())).thenReturn(legalVerificationResult); + when(nationalRegistriesApi.verifyLegalUsingGET(eq("taxCode2"), any())).thenThrow(new WebApplicationException(400)); + + Response responseOk = new ServerResponse(null, 204, null); + when(userControllerApi.usersUserIdInstitutionsInstitutionIdProductsProductIdDelete("institution-id","productId", "user1")) + .thenReturn(responseOk); + when(userControllerApi.usersUserIdInstitutionsInstitutionIdProductsProductIdDelete("institution-id","productId", "user2")) + .thenReturn(responseOk); + + completionServiceDefault.deleteOldPgManagers(onboarding); + + verify(userControllerApi, times(2)).usersUserIdInstitutionsInstitutionIdProductsProductIdDelete(eq("institution-id"), eq("productId"), any()); + } + + @Test + void deleteOldPgManagers_shouldNotDeleteActiveManagers() { + // Shouldn't perform deletion, because user1 will be found on the registry. + Onboarding onboarding = createOnboarding(); + onboarding.getInstitution().setId("institution-id"); + onboarding.getInstitution().setTaxCode("institution-tax-code"); + onboarding.getInstitution().setOrigin(Origin.INFOCAMERE); + + UserInstitutionResponse user1 = new UserInstitutionResponse(); + user1.setUserId("user1"); + when(userInstitutionApi.institutionsInstitutionIdUserInstitutionsGet( + eq("institution-id"), any(), eq(List.of("productId")), eq(List.of("MANAGER")), eq(List.of("ACTIVE")), any())) + .thenReturn(List.of(user1)); + + UserResource userResource1 = new UserResource(); + userResource1.setFiscalCode("taxCode1"); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, "user1")).thenReturn(userResource1); + + BusinessesResource businessesResource = new BusinessesResource(); + BusinessResource businessResource = new BusinessResource(); + businessResource.setBusinessTaxId("institution-tax-code"); + businessesResource.setBusinesses(List.of(businessResource)); + when(infocamereApi.institutionsByLegalTaxIdUsingPOST(any())).thenReturn(businessesResource); + + completionServiceDefault.deleteOldPgManagers(onboarding); + + verify(userControllerApi, never()).usersUserIdInstitutionsInstitutionIdProductsProductIdDelete(eq("institution-id"), eq("productId"), any()); + } + + @Test + void deleteOldPgManagers_shouldHandleEmptyManagersList() { + Onboarding onboarding = createOnboarding(); + onboarding.getInstitution().setId("institution-id"); + onboarding.getInstitution().setTaxCode("institution-tax-code"); + onboarding.getInstitution().setOrigin(Origin.INFOCAMERE); + + when(userInstitutionApi.institutionsInstitutionIdUserInstitutionsGet( + eq("institution-id"), any(), eq(List.of("productId")), eq(List.of("MANAGER")), eq(List.of("ACTIVE")), any())) + .thenReturn(Collections.emptyList()); + + completionServiceDefault.deleteOldPgManagers(onboarding); + + verify(userControllerApi, never()).usersUserIdInstitutionsInstitutionIdProductsProductIdDelete(any(), eq("institution-id"), eq("productId")); + } + private User createDummyUser(Onboarding onboarding) { User user = new User(); user.setRole(PartyRole.MANAGER); From f64af9a17915d96c100aae931e7ec6dba28011ba Mon Sep 17 00:00:00 2001 From: empassaro <113031808+empassaro@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:45:56 +0200 Subject: [PATCH 5/8] [PNPG-249] feat: added WorkflowExecutorForUsersPg (#545) --- .../functions/OnboardingFunctions.java | 1 + .../workflow/WorkflowExecutorForUsersPg.java | 42 +++++++++++++++++++ .../functions/OnboardingFunctionsTest.java | 39 +++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/workflow/WorkflowExecutorForUsersPg.java diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java index b70758330..181d0f3f8 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java @@ -157,6 +157,7 @@ public void onboardingsOrchestrator( case IMPORT -> workflowExecutor = new WorkflowExecutorImport(objectMapper, optionsRetry); case USERS -> workflowExecutor = new WorkflowExecutorForUsers(objectMapper, optionsRetry); case INCREMENT_REGISTRATION_AGGREGATOR -> workflowExecutor = new WorkflowExecutorIncrementRegistrationAggregator(objectMapper, optionsRetry, onboardingMapper); + case USERS_PG -> workflowExecutor = new WorkflowExecutorForUsersPg(objectMapper, optionsRetry); default -> throw new IllegalArgumentException("Workflow options not found!"); } diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/workflow/WorkflowExecutorForUsersPg.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/workflow/WorkflowExecutorForUsersPg.java new file mode 100644 index 000000000..802f84d02 --- /dev/null +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/workflow/WorkflowExecutorForUsersPg.java @@ -0,0 +1,42 @@ +package it.pagopa.selfcare.onboarding.workflow; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.microsoft.durabletask.TaskOptions; +import com.microsoft.durabletask.TaskOrchestrationContext; +import it.pagopa.selfcare.onboarding.common.OnboardingStatus; +import it.pagopa.selfcare.onboarding.entity.Onboarding; +import it.pagopa.selfcare.onboarding.entity.OnboardingWorkflow; +import it.pagopa.selfcare.onboarding.entity.OnboardingWorkflowUser; + +import java.util.Optional; + +import static it.pagopa.selfcare.onboarding.common.OnboardingStatus.COMPLETED; +import static it.pagopa.selfcare.onboarding.entity.OnboardingWorkflowType.USER; +import static it.pagopa.selfcare.onboarding.functions.utils.ActivityName.*; +import static it.pagopa.selfcare.onboarding.utils.Utils.getOnboardingString; + +public record WorkflowExecutorForUsersPg(ObjectMapper objectMapper, TaskOptions optionsRetry) implements WorkflowExecutor { + @Override + public Optional executeRequestState(TaskOrchestrationContext ctx, OnboardingWorkflow onboardingWorkflow) { + return Optional.empty(); + } + + @Override + public Optional executeToBeValidatedState(TaskOrchestrationContext ctx, OnboardingWorkflow onboardingWorkflow) { + return Optional.empty(); + } + + @Override + public Optional executePendingState(TaskOrchestrationContext ctx, OnboardingWorkflow onboardingWorkflow) { + final String onboardingString = getOnboardingString(objectMapper(), onboardingWorkflow.getOnboarding()); + ctx.callActivity(DELETE_MANAGERS_BY_IC_AND_ADE, onboardingString, optionsRetry(), String.class).await(); + ctx.callActivity(CREATE_USERS_ACTIVITY, onboardingString, optionsRetry(), String.class).await(); + ctx.callActivity(STORE_ONBOARDING_ACTIVATEDAT, onboardingString, optionsRetry(), String.class).await(); + return Optional.of(COMPLETED); + } + + @Override + public OnboardingWorkflow createOnboardingWorkflow(Onboarding onboarding) { + return new OnboardingWorkflowUser(onboarding, USER.name()); + } +} diff --git a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java index da679df88..36598d956 100644 --- a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java +++ b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java @@ -735,6 +735,45 @@ void onboardingRejectedOrchestrator() { assertEquals(SEND_MAIL_REJECTION_ACTIVITY, captorActivity.getAllValues().get(0)); } + @Test + void usersPgOrchestrator_whenStatusPending() { + Onboarding onboarding = new Onboarding(); + onboarding.setId("onboardingId"); + onboarding.setStatus(OnboardingStatus.PENDING); + onboarding.setWorkflowType(WorkflowType.USERS_PG); + onboarding.setInstitution(new Institution()); + + TaskOrchestrationContext orchestrationContext = mockTaskOrchestrationContext(onboarding); + + + function.onboardingsOrchestrator(orchestrationContext, executionContext); + + ArgumentCaptor captorActivity = ArgumentCaptor.forClass(String.class); + verify(orchestrationContext, times(4)) + .callActivity(captorActivity.capture(), any(), any(),any()); + assertEquals(DELETE_MANAGERS_BY_IC_AND_ADE, captorActivity.getAllValues().get(0)); + assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(1)); + assertEquals(STORE_ONBOARDING_ACTIVATEDAT, captorActivity.getAllValues().get(2)); + } + + @Test + void usersPgOrchestrator_whenStatusRequest() { + Onboarding onboarding = new Onboarding(); + onboarding.setId("onboardingId"); + onboarding.setStatus(OnboardingStatus.REQUEST); + onboarding.setWorkflowType(WorkflowType.USERS_PG); + onboarding.setInstitution(new Institution()); + + TaskOrchestrationContext orchestrationContext = mockTaskOrchestrationContext(onboarding); + + + function.onboardingsOrchestrator(orchestrationContext, executionContext); + + ArgumentCaptor captorActivity = ArgumentCaptor.forClass(String.class); + verify(orchestrationContext, times(0)) + .callActivity(captorActivity.capture(), any(), any(),any()); + } + @Test void createInstitutionAndPersistInstitutionId() { From 92d9babe5371db917f7f1281078005aea9ab457a Mon Sep 17 00:00:00 2001 From: pierpaolodidato89 <137791912+pierpaolodidato89@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:19:34 +0200 Subject: [PATCH 6/8] Removed post processor method and added reject onboarding fn into executePendingState --- .../functions/OnboardingFunctions.java | 5 +--- .../service/CompletionServiceDefault.java | 4 +-- .../onboarding/workflow/WorkflowExecutor.java | 8 +---- .../functions/OnboardingFunctionsTest.java | 30 +++++++++---------- 4 files changed, 18 insertions(+), 29 deletions(-) diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java index 181d0f3f8..9bc17708d 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctions.java @@ -162,10 +162,7 @@ public void onboardingsOrchestrator( } Optional optNextStatus = workflowExecutor.execute(ctx, onboarding); - optNextStatus.ifPresent(onboardingStatus -> { - service.updateOnboardingStatus(onboardingId, onboardingStatus); - workflowExecutor.postProcessor(ctx, onboarding, onboardingStatus); - }); + optNextStatus.ifPresent(onboardingStatus -> service.updateOnboardingStatus(onboardingId, onboardingStatus)); } catch (TaskFailedException ex) { functionContext.getLogger().warning("Error during workflowExecutor execute, msg: " + ex.getMessage()); service.updateOnboardingStatusAndInstanceId(onboardingId, OnboardingStatus.FAILED, ctx.getInstanceId()); diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefault.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefault.java index 8ec10ff4e..8ca8ec6de 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefault.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/CompletionServiceDefault.java @@ -238,8 +238,8 @@ public void rejectOutdatedOnboardings(Onboarding onboarding) { LocalDateTime now = LocalDateTime.now(); onboardingRepository .update("status = ?1 and updatedAt = ?2 ", REJECTED, now) - .where("productId = ?1 and institution.origin = ?2 and institution.originId = ?3 and status = PENDING or status = TOBEVALIDATED", - onboarding.getProductId(), onboarding.getInstitution().getOrigin(), onboarding.getInstitution().getOriginId()); + .where("productId = ?1 and institution.origin = ?2 and institution.originId = ?3 and _id != ?4 and status = PENDING or status = TOBEVALIDATED", + onboarding.getProductId(), onboarding.getInstitution().getOrigin(), onboarding.getInstitution().getOriginId(), onboarding.getId()); } @Override diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/workflow/WorkflowExecutor.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/workflow/WorkflowExecutor.java index 1b8121bff..a2a112179 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/workflow/WorkflowExecutor.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/workflow/WorkflowExecutor.java @@ -51,6 +51,7 @@ default String createInstitutionAndOnboarding(TaskOrchestrationContext ctx, Onbo ctx.callActivity(CREATE_ONBOARDING_ACTIVITY, onboardingWithInstitutionIdString, optionsRetry(), String.class).await(); ctx.callActivity(CREATE_USERS_ACTIVITY, onboardingWithInstitutionIdString, optionsRetry(), String.class).await(); ctx.callActivity(STORE_ONBOARDING_ACTIVATEDAT, onboardingWithInstitutionIdString, optionsRetry(), String.class).await(); + ctx.callActivity(REJECT_OUTDATED_ONBOARDINGS, onboardingString, optionsRetry(), String.class).await(); createTestEnvironmentsOnboarding(ctx, onboarding, onboardingWithInstitutionIdString); @@ -123,11 +124,4 @@ default Optional executeRejectedState(TaskOrchestrationContext return Optional.empty(); } - default void postProcessor(TaskOrchestrationContext ctx, Onboarding onboarding, OnboardingStatus onboardingStatus) { - if (COMPLETED.equals(onboardingStatus)) { - final String onboardingString = getOnboardingString(objectMapper(), onboarding); - ctx.callActivity(REJECT_OUTDATED_ONBOARDINGS, onboardingString, optionsRetry(), String.class).await(); - } - } - } diff --git a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java index 36598d956..667cb6e31 100644 --- a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java +++ b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/functions/OnboardingFunctionsTest.java @@ -191,8 +191,8 @@ void onboardingOrchestratorContractRegistrationAggregator_Pending(){ assertEquals(CREATE_ONBOARDING_ACTIVITY, captorActivity.getAllValues().get(1)); assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(2)); assertEquals(STORE_ONBOARDING_ACTIVATEDAT, captorActivity.getAllValues().get(3)); - assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(4)); - assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(5)); + assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(4)); + assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(5)); Mockito.verify(orchestrationContext, times(3)) .callSubOrchestrator(eq(ONBOARDINGS_AGGREGATE_ORCHESTRATOR), any(), any()); @@ -339,8 +339,8 @@ void onboardingsOrchestratorConfirmation() { assertEquals(CREATE_ONBOARDING_ACTIVITY, captorActivity.getAllValues().get(1)); assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(2)); assertEquals(STORE_ONBOARDING_ACTIVATEDAT, captorActivity.getAllValues().get(3)); - assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(4)); - assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(5)); + assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(4)); + assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(5)); verify(service, times(1)) .updateOnboardingStatus(onboarding.getId(), OnboardingStatus.COMPLETED); @@ -366,10 +366,10 @@ void onboardingsOrchestratorConfirmationWithTestProductIds() { assertEquals(CREATE_ONBOARDING_ACTIVITY, captorActivity.getAllValues().get(1)); assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(2)); assertEquals(STORE_ONBOARDING_ACTIVATEDAT, captorActivity.getAllValues().get(3)); - assertEquals(CREATE_ONBOARDING_ACTIVITY, captorActivity.getAllValues().get(4)); - assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(5)); - assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(6)); - assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(7)); + assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(4)); + assertEquals(CREATE_ONBOARDING_ACTIVITY, captorActivity.getAllValues().get(5)); + assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(6)); + assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(7)); verify(service, times(1)) .updateOnboardingStatus(onboarding.getId(), OnboardingStatus.COMPLETED); @@ -389,7 +389,7 @@ void onboardingOrchestratorConfirmAggregate(){ function.onboardingsOrchestrator(orchestrationContext, executionContext); ArgumentCaptor captorActivity = ArgumentCaptor.forClass(String.class); - Mockito.verify(orchestrationContext, times(7)) + Mockito.verify(orchestrationContext, times(6)) .callActivity(captorActivity.capture(), any(), any(),any()); assertEquals(CREATE_INSTITUTION_ACTIVITY, captorActivity.getAllValues().get(0)); assertEquals(CREATE_ONBOARDING_ACTIVITY, captorActivity.getAllValues().get(1)); @@ -397,7 +397,6 @@ void onboardingOrchestratorConfirmAggregate(){ assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(3)); assertEquals(STORE_ONBOARDING_ACTIVATEDAT, captorActivity.getAllValues().get(4)); assertEquals(SEND_MAIL_COMPLETION_AGGREGATE_ACTIVITY, captorActivity.getAllValues().get(5)); - assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(6)); Mockito.verify(service, times(1)) .updateOnboardingStatus(onboarding.getId(), OnboardingStatus.COMPLETED); @@ -520,12 +519,11 @@ void onboardingsOrchestratorNewAdmin() { function.onboardingsOrchestrator(orchestrationContext, executionContext); ArgumentCaptor captorActivity = ArgumentCaptor.forClass(String.class); - verify(orchestrationContext, times(4)) + verify(orchestrationContext, times(3)) .callActivity(captorActivity.capture(), any(), any(),any()); assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(0)); assertEquals(STORE_ONBOARDING_ACTIVATEDAT, captorActivity.getAllValues().get(1)); assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(2)); - assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(3)); verify(service, times(1)) .updateOnboardingStatus(onboarding.getId(), OnboardingStatus.COMPLETED); @@ -572,8 +570,8 @@ void onboardingsOrchestratorForApprovePtWhenToBeValidated() { assertEquals(CREATE_ONBOARDING_ACTIVITY, captorActivity.getAllValues().get(1)); assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(2)); assertEquals(STORE_ONBOARDING_ACTIVATEDAT, captorActivity.getAllValues().get(3)); - assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(4)); - assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(5)); + assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(4)); + assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(5)); verify(service, times(1)) .updateOnboardingStatus(onboarding.getId(), OnboardingStatus.COMPLETED); @@ -709,8 +707,8 @@ void onboardingCompletionOrchestrator() { assertEquals(CREATE_ONBOARDING_ACTIVITY, captorActivity.getAllValues().get(1)); assertEquals(CREATE_USERS_ACTIVITY, captorActivity.getAllValues().get(2)); assertEquals(STORE_ONBOARDING_ACTIVATEDAT, captorActivity.getAllValues().get(3)); - assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(4)); - assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(5)); + assertEquals(REJECT_OUTDATED_ONBOARDINGS, captorActivity.getAllValues().get(4)); + assertEquals(SEND_MAIL_COMPLETION_ACTIVITY, captorActivity.getAllValues().get(5)); verify(service, times(1)) .updateOnboardingStatus(onboarding.getId(), OnboardingStatus.COMPLETED); From 59cc1e9e049a7156121f0249bb2fc86bbe3f84bd Mon Sep 17 00:00:00 2001 From: empassaro <113031808+empassaro@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:12:13 +0200 Subject: [PATCH 7/8] [PNPG-244] feat: added env for MS_USER_URL (#554) --- apps/onboarding-ms/README.md | 2 +- apps/onboarding-ms/src/main/resources/application.properties | 2 +- .../onboarding-ms/env/dev-pnpg/terraform.tfvars | 4 ++++ infra/container_apps/onboarding-ms/env/dev/terraform.tfvars | 4 ++++ .../onboarding-ms/env/prod-pnpg/terraform.tfvars | 4 ++++ infra/container_apps/onboarding-ms/env/prod/terraform.tfvars | 4 ++++ .../onboarding-ms/env/uat-pnpg/terraform.tfvars | 4 ++++ infra/container_apps/onboarding-ms/env/uat/terraform.tfvars | 4 ++++ 8 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/onboarding-ms/README.md b/apps/onboarding-ms/README.md index 76b3ebe67..70bb7a1e2 100644 --- a/apps/onboarding-ms/README.md +++ b/apps/onboarding-ms/README.md @@ -39,7 +39,7 @@ Before running you must set these properties as environment variables. | quarkus.rest-client."**.UoApi".url
    | MS_PARTY_REGISTRY_URL | | yes | | quarkus.rest-client."**.OrchestrationApi".url
    | ONBOARDING_FUNCTIONS_URL | | yes | | quarkus.rest-client."**.OrchestrationApi".api-key
    | ONBOARDING-FUNCTIONS-API-KEY | | yes | -| quarkus.rest-client."**.InstitutionApi".url
    | USER_URL | | yes | +| quarkus.rest-client."**.InstitutionApi".url
    | MS_USER_URL | | yes | | onboarding.institutions-allowed-list
    | ONBOARDING_ALLOWED_INSTITUTIONS_PRODUCTS | | no | > **_NOTE:_** properties that contains secret must have the same name of its secret as uppercase. diff --git a/apps/onboarding-ms/src/main/resources/application.properties b/apps/onboarding-ms/src/main/resources/application.properties index e1c24e271..d78756094 100644 --- a/apps/onboarding-ms/src/main/resources/application.properties +++ b/apps/onboarding-ms/src/main/resources/application.properties @@ -83,7 +83,7 @@ quarkus.rest-client."org.openapi.quarkus.core_json.api.InstitutionApi".read-time quarkus.openapi-generator.codegen.spec.user_json.mutiny=true quarkus.openapi-generator.codegen.spec.user_json.additional-model-type-annotations=@lombok.Builder; @lombok.NoArgsConstructor; @lombok.AllArgsConstructor quarkus.openapi-generator.codegen.spec.user_json.enable-security-generation=false -quarkus.rest-client."org.openapi.quarkus.user_json.api.InstitutionApi".url=${USER_URL:http://localhost:8080} +quarkus.rest-client."org.openapi.quarkus.user_json.api.InstitutionApi".url=${MS_USER_URL:http://localhost:8080} mp.openapi.extensions.smallrye.operationIdStrategy=METHOD diff --git a/infra/container_apps/onboarding-ms/env/dev-pnpg/terraform.tfvars b/infra/container_apps/onboarding-ms/env/dev-pnpg/terraform.tfvars index 8e75e50e6..950518c88 100644 --- a/infra/container_apps/onboarding-ms/env/dev-pnpg/terraform.tfvars +++ b/infra/container_apps/onboarding-ms/env/dev-pnpg/terraform.tfvars @@ -67,6 +67,10 @@ app_settings = [ { name = "SIGNATURE_VALIDATION_ENABLED" value = "false" + }, + { + name = "MS_USER_URL" + value = "http://selc-d-pnpg-user-ms-ca" } ] diff --git a/infra/container_apps/onboarding-ms/env/dev/terraform.tfvars b/infra/container_apps/onboarding-ms/env/dev/terraform.tfvars index 75ebe9fe3..2a911b8ef 100644 --- a/infra/container_apps/onboarding-ms/env/dev/terraform.tfvars +++ b/infra/container_apps/onboarding-ms/env/dev/terraform.tfvars @@ -68,6 +68,10 @@ app_settings = [ { name = "SIGNATURE_VALIDATION_ENABLED" value = "false" + }, + { + name = "MS_USER_URL" + value = "http://selc-d-user-ms-ca" } ] diff --git a/infra/container_apps/onboarding-ms/env/prod-pnpg/terraform.tfvars b/infra/container_apps/onboarding-ms/env/prod-pnpg/terraform.tfvars index 6cd2719c2..c1a5d5e35 100644 --- a/infra/container_apps/onboarding-ms/env/prod-pnpg/terraform.tfvars +++ b/infra/container_apps/onboarding-ms/env/prod-pnpg/terraform.tfvars @@ -67,6 +67,10 @@ app_settings = [ { name = "SIGNATURE_VALIDATION_ENABLED" value = "false" + }, + { + name = "MS_USER_URL" + value = "http://selc-p-pnpg-user-ms-ca" } ] diff --git a/infra/container_apps/onboarding-ms/env/prod/terraform.tfvars b/infra/container_apps/onboarding-ms/env/prod/terraform.tfvars index 59cba00f3..39259bf3f 100644 --- a/infra/container_apps/onboarding-ms/env/prod/terraform.tfvars +++ b/infra/container_apps/onboarding-ms/env/prod/terraform.tfvars @@ -68,6 +68,10 @@ app_settings = [ { name = "STORAGE_CONTAINER_CONTRACT" value = "selc-p-contracts-blob" + }, + { + name = "MS_USER_URL" + value = "http://selc-p-user-ms-ca" } ] diff --git a/infra/container_apps/onboarding-ms/env/uat-pnpg/terraform.tfvars b/infra/container_apps/onboarding-ms/env/uat-pnpg/terraform.tfvars index f30db5c3c..4924bbf8f 100644 --- a/infra/container_apps/onboarding-ms/env/uat-pnpg/terraform.tfvars +++ b/infra/container_apps/onboarding-ms/env/uat-pnpg/terraform.tfvars @@ -56,6 +56,10 @@ app_settings = [ { name = "SIGNATURE_VALIDATION_ENABLED" value = "false" + }, + { + name = "MS_USER_URL" + value = "http://selc-u-pnpg-user-ms-ca" } ] diff --git a/infra/container_apps/onboarding-ms/env/uat/terraform.tfvars b/infra/container_apps/onboarding-ms/env/uat/terraform.tfvars index 259ef3662..42e95c3b2 100644 --- a/infra/container_apps/onboarding-ms/env/uat/terraform.tfvars +++ b/infra/container_apps/onboarding-ms/env/uat/terraform.tfvars @@ -59,6 +59,10 @@ app_settings = [ { name = "STORAGE_CONTAINER_CONTRACT" value = "selc-u-contracts-blob" + }, + { + name = "MS_USER_URL" + value = "http://selc-u-user-ms-ca" } ] From 4ff4ec5a2269dc034d3a031df9ef20078e89be36 Mon Sep 17 00:00:00 2001 From: empassaro <113031808+empassaro@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:04:43 +0200 Subject: [PATCH 8/8] [PNPG-244] feat: added Authentication header propagation for selfcare-user clients (#555) --- apps/onboarding-ms/src/main/resources/application.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/onboarding-ms/src/main/resources/application.properties b/apps/onboarding-ms/src/main/resources/application.properties index d78756094..d445c27a5 100644 --- a/apps/onboarding-ms/src/main/resources/application.properties +++ b/apps/onboarding-ms/src/main/resources/application.properties @@ -81,10 +81,11 @@ quarkus.rest-client."org.openapi.quarkus.core_json.api.InstitutionApi".url=${MS_ quarkus.rest-client."org.openapi.quarkus.core_json.api.InstitutionApi".read-timeout=60000 quarkus.openapi-generator.codegen.spec.user_json.mutiny=true +quarkus.openapi-generator.codegen.spec.user_json.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders(it.pagopa.selfcare.onboarding.client.auth.AuthenticationPropagationHeadersFactory.class) quarkus.openapi-generator.codegen.spec.user_json.additional-model-type-annotations=@lombok.Builder; @lombok.NoArgsConstructor; @lombok.AllArgsConstructor quarkus.openapi-generator.codegen.spec.user_json.enable-security-generation=false quarkus.rest-client."org.openapi.quarkus.user_json.api.InstitutionApi".url=${MS_USER_URL:http://localhost:8080} - +quarkus.rest-client."org.openapi.quarkus.user_json.api.InstitutionApi".read-timeout=60000 mp.openapi.extensions.smallrye.operationIdStrategy=METHOD