From e68c68338f59f6db8751083a9b9ebe12faa10f9a Mon Sep 17 00:00:00 2001 From: Giampiero Ferrara Date: Mon, 28 Oct 2024 09:52:57 +0100 Subject: [PATCH] [SELC-5806] feat: Refactored class Product --- apps/onboarding-functions/pom.xml | 6 +- .../onboarding/entity/OnboardingWorkflow.java | 48 +- .../entity/OnboardingWorkflowAggregator.java | 129 +- .../entity/OnboardingWorkflowInstitution.java | 143 +- .../entity/OnboardingWorkflowUser.java | 133 +- .../onboarding/service/OnboardingService.java | 30 +- .../onboarding/utils/InstitutionUtils.java | 21 + .../service/CompletionServiceDefaultTest.java | 40 +- .../service/OnboardingServiceTest.java | 1064 ++--- apps/onboarding-ms/pom.xml | 4 +- .../service/OnboardingServiceDefault.java | 3538 ++++++++++------- .../onboarding/util/InstitutionUtils.java | 21 + apps/pom.xml | 2 +- libs/onboarding-sdk-azure-storage/pom.xml | 2 +- libs/onboarding-sdk-common/pom.xml | 2 +- libs/onboarding-sdk-crypto/pom.xml | 2 +- libs/onboarding-sdk-pom/pom.xml | 2 +- libs/onboarding-sdk-product/pom.xml | 27 +- .../product/entity/ContractStorage.java | 34 - .../product/entity/ContractTemplate.java | 23 + .../selfcare/product/entity/Product.java | 651 +-- .../product/service/ProductService.java | 6 +- .../service/ProductServiceCacheable.java | 140 +- .../service/ProductServiceDefault.java | 24 +- .../selfcare/product/entity/ProductTest.java | 359 +- .../service/ProductServiceCacheableTest.java | 13 +- .../src/test/resources/product.json | 56 + test-coverage/pom.xml | 8 +- 28 files changed, 3727 insertions(+), 2801 deletions(-) create mode 100644 apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/utils/InstitutionUtils.java create mode 100644 apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/util/InstitutionUtils.java delete mode 100644 libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/ContractStorage.java create mode 100644 libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/ContractTemplate.java create mode 100644 libs/onboarding-sdk-product/src/test/resources/product.json diff --git a/apps/onboarding-functions/pom.xml b/apps/onboarding-functions/pom.xml index fe9afd5c0..61c5e765a 100644 --- a/apps/onboarding-functions/pom.xml +++ b/apps/onboarding-functions/pom.xml @@ -193,17 +193,17 @@ it.pagopa.selfcare onboarding-sdk-crypto - 0.3.5 + 0.4.0 it.pagopa.selfcare onboarding-sdk-azure-storage - 0.3.5 + 0.4.0 it.pagopa.selfcare onboarding-sdk-product - 0.3.5 + 0.4.0 org.apache.commons diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflow.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflow.java index b5464f01f..9aa68d26a 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflow.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflow.java @@ -9,44 +9,44 @@ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ - @JsonSubTypes.Type(value = OnboardingWorkflowAggregator.class, name = "AGGREGATOR"), - @JsonSubTypes.Type(value = OnboardingWorkflowInstitution.class, name = "INSTITUTION"), - @JsonSubTypes.Type(value = OnboardingWorkflowUser.class, name = "USER") + @JsonSubTypes.Type(value = OnboardingWorkflowAggregator.class, name = "AGGREGATOR"), + @JsonSubTypes.Type(value = OnboardingWorkflowInstitution.class, name = "INSTITUTION"), + @JsonSubTypes.Type(value = OnboardingWorkflowUser.class, name = "USER") }) public abstract class OnboardingWorkflow { - protected static final String PDF_FORMAT_FILENAME = "%s_accordo_adesione.pdf"; + protected static final String PDF_FORMAT_FILENAME = "%s_accordo_adesione.pdf"; - OnboardingWorkflow(Onboarding onboarding) { - this.onboarding = onboarding; - } + OnboardingWorkflow(Onboarding onboarding) { + this.onboarding = onboarding; + } - public OnboardingWorkflow() { - } + public OnboardingWorkflow() {} - protected Onboarding onboarding; - public abstract String emailRegistrationPath(MailTemplatePathConfig config); + protected Onboarding onboarding; - public abstract String getEmailCompletionPath(MailTemplatePathConfig config); + public abstract String getEmailRegistrationPath(MailTemplatePathConfig config); - public abstract String getPdfFormatFilename(); + public abstract String getEmailCompletionPath(MailTemplatePathConfig config); - public abstract TokenType getTokenType(); + public abstract String getPdfFormatFilename(); - public abstract String getConfirmTokenUrl(MailTemplatePlaceholdersConfig config); + public abstract TokenType getTokenType(); - public abstract String getRejectTokenUrl(MailTemplatePlaceholdersConfig config); + public abstract String getConfirmTokenUrl(MailTemplatePlaceholdersConfig config); - public abstract String getContractTemplatePath(Product product); + public abstract String getRejectTokenUrl(MailTemplatePlaceholdersConfig config); - public abstract String getContractTemplateVersion(Product product); + public abstract String getContractTemplatePath(Product product); - public Onboarding getOnboarding() { - return onboarding; - } + public abstract String getContractTemplateVersion(Product product); - public void setOnboarding(Onboarding onboarding) { - this.onboarding = onboarding; - } + public Onboarding getOnboarding() { + return onboarding; + } + public void setOnboarding(Onboarding onboarding) { + this.onboarding = onboarding; + } + } diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowAggregator.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowAggregator.java index f436b4d26..57ea16da6 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowAggregator.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowAggregator.java @@ -3,76 +3,69 @@ import it.pagopa.selfcare.onboarding.common.TokenType; import it.pagopa.selfcare.onboarding.config.MailTemplatePathConfig; import it.pagopa.selfcare.onboarding.config.MailTemplatePlaceholdersConfig; -import it.pagopa.selfcare.product.entity.ContractStorage; +import it.pagopa.selfcare.onboarding.utils.InstitutionUtils; import it.pagopa.selfcare.product.entity.Product; -import java.util.Objects; -import java.util.Optional; - public class OnboardingWorkflowAggregator extends OnboardingWorkflow { - private String type; - - public OnboardingWorkflowAggregator(){} - - public OnboardingWorkflowAggregator(Onboarding onboarding, String type) { - super(onboarding); - this.type = type; - } - - @Override - public TokenType getTokenType() { - return TokenType.INSTITUTION; - } - - @Override - public String getPdfFormatFilename() { - return PDF_FORMAT_FILENAME; - } - - @Override - public String emailRegistrationPath(MailTemplatePathConfig config) { - return config.registrationAggregatorPath(); - } - - @Override - public String getEmailCompletionPath(MailTemplatePathConfig config) { - return config.completePath(); - } - - @Override - public String getContractTemplatePath(Product product) { - if(Objects.isNull(onboarding.getInstitution()) || Objects.isNull(onboarding.getInstitution().getInstitutionType())){ - return null; - } - - return Optional.ofNullable(product.getInstitutionContractMappings()) - .filter(mappings -> mappings.containsKey(onboarding.getInstitution().getInstitutionType().name())) - .map(mappings -> mappings.get(onboarding.getInstitution().getInstitutionType().name())) - .map(ContractStorage::getContractTemplatePath) - .orElse(product.getContractTemplatePath()); - } - - @Override - public String getContractTemplateVersion(Product product) { - return product.getContractTemplateVersion(); - } - - @Override - public String getConfirmTokenUrl(MailTemplatePlaceholdersConfig config) { - return config.confirmTokenPlaceholder(); - } - - @Override - public String getRejectTokenUrl(MailTemplatePlaceholdersConfig config) { - return config.rejectTokenPlaceholder(); - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } + private String type; + + public OnboardingWorkflowAggregator() {} + + public OnboardingWorkflowAggregator(Onboarding onboarding, String type) { + super(onboarding); + this.type = type; + } + + @Override + public TokenType getTokenType() { + return TokenType.INSTITUTION; + } + + @Override + public String getPdfFormatFilename() { + return PDF_FORMAT_FILENAME; + } + + @Override + public String getEmailRegistrationPath(MailTemplatePathConfig config) { + return config.registrationAggregatorPath(); + } + + @Override + public String getEmailCompletionPath(MailTemplatePathConfig config) { + return config.completePath(); + } + + @Override + public String getContractTemplatePath(Product product) { + return product + .getUserContractTemplate(InstitutionUtils.getCurrentInstitutionType(onboarding)) + .getContractTemplatePath(); + } + + @Override + public String getContractTemplateVersion(Product product) { + return product + .getUserContractTemplate(InstitutionUtils.getCurrentInstitutionType(onboarding)) + .getContractTemplateVersion(); + } + + @Override + public String getConfirmTokenUrl(MailTemplatePlaceholdersConfig config) { + return config.confirmTokenPlaceholder(); + } + + @Override + public String getRejectTokenUrl(MailTemplatePlaceholdersConfig config) { + return config.rejectTokenPlaceholder(); + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } } diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowInstitution.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowInstitution.java index 917d30399..23596f3a4 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowInstitution.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowInstitution.java @@ -1,89 +1,78 @@ package it.pagopa.selfcare.onboarding.entity; +import static it.pagopa.selfcare.onboarding.common.ProductId.PROD_FD; +import static it.pagopa.selfcare.onboarding.common.ProductId.PROD_FD_GARANTITO; + import it.pagopa.selfcare.onboarding.common.InstitutionType; import it.pagopa.selfcare.onboarding.common.TokenType; import it.pagopa.selfcare.onboarding.config.MailTemplatePathConfig; import it.pagopa.selfcare.onboarding.config.MailTemplatePlaceholdersConfig; -import it.pagopa.selfcare.product.entity.ContractStorage; +import it.pagopa.selfcare.onboarding.utils.InstitutionUtils; import it.pagopa.selfcare.product.entity.Product; -import java.util.Objects; -import java.util.Optional; - -import static it.pagopa.selfcare.onboarding.common.ProductId.PROD_FD; -import static it.pagopa.selfcare.onboarding.common.ProductId.PROD_FD_GARANTITO; - public class OnboardingWorkflowInstitution extends OnboardingWorkflow { - private String type; - - public OnboardingWorkflowInstitution() { - } - - public OnboardingWorkflowInstitution(Onboarding onboarding, String type) { - super(onboarding); - this.type = type; - } - - @Override - public String emailRegistrationPath(MailTemplatePathConfig config) { - return config.registrationPath(); - } - - @Override - public String getEmailCompletionPath(MailTemplatePathConfig config) { - if (InstitutionType.PT.equals(this.onboarding.getInstitution().getInstitutionType())) { - return config.completePathPt(); - } else { - return this.onboarding.getProductId().equals(PROD_FD.getValue()) || this.onboarding.getProductId().equals(PROD_FD_GARANTITO.getValue()) - ? config.completePathFd() - : config.completePath(); - } - } - - @Override - public TokenType getTokenType() { - return TokenType.INSTITUTION; - } - - @Override - public String getPdfFormatFilename() { - return PDF_FORMAT_FILENAME; - } - - @Override - public String getConfirmTokenUrl(MailTemplatePlaceholdersConfig config) { - return config.confirmTokenPlaceholder(); - } - - @Override - public String getRejectTokenUrl(MailTemplatePlaceholdersConfig config) { - return config.rejectTokenPlaceholder(); - } - - @Override - public String getContractTemplatePath(Product product) { - if(Objects.isNull(onboarding.getInstitution()) || Objects.isNull(onboarding.getInstitution().getInstitutionType())){ - return null; - } - - return Optional.ofNullable(product.getInstitutionContractMappings()) - .filter(mappings -> mappings.containsKey(onboarding.getInstitution().getInstitutionType().name())) - .map(mappings -> mappings.get(onboarding.getInstitution().getInstitutionType().name())) - .map(ContractStorage::getContractTemplatePath) - .orElse(product.getContractTemplatePath()); - } - - @Override - public String getContractTemplateVersion(Product product) { - return product.getContractTemplateVersion(); - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; + private String type; + + public OnboardingWorkflowInstitution() {} + + public OnboardingWorkflowInstitution(Onboarding onboarding, String type) { + super(onboarding); + this.type = type; + } + + @Override + public String getEmailRegistrationPath(MailTemplatePathConfig config) { + return config.registrationPath(); + } + + @Override + public String getEmailCompletionPath(MailTemplatePathConfig config) { + if (InstitutionType.PT.equals(this.onboarding.getInstitution().getInstitutionType())) { + return config.completePathPt(); + } else { + return this.onboarding.getProductId().equals(PROD_FD.getValue()) + || this.onboarding.getProductId().equals(PROD_FD_GARANTITO.getValue()) + ? config.completePathFd() + : config.completePath(); } + } + + @Override + public TokenType getTokenType() { + return TokenType.INSTITUTION; + } + + @Override + public String getPdfFormatFilename() { + return PDF_FORMAT_FILENAME; + } + + @Override + public String getConfirmTokenUrl(MailTemplatePlaceholdersConfig config) { + return config.confirmTokenPlaceholder(); + } + + @Override + public String getRejectTokenUrl(MailTemplatePlaceholdersConfig config) { + return config.rejectTokenPlaceholder(); + } + + @Override + public String getContractTemplatePath(Product product) { + return product.getInstitutionContractTemplate(InstitutionUtils.getCurrentInstitutionType(onboarding)).getContractTemplatePath(); + } + + @Override + public String getContractTemplateVersion(Product product) { + return product.getInstitutionContractTemplate(InstitutionUtils.getCurrentInstitutionType(onboarding)).getContractTemplateVersion(); + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } } diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowUser.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowUser.java index 6350d24ad..f66ccfb4e 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowUser.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/entity/OnboardingWorkflowUser.java @@ -4,79 +4,82 @@ import it.pagopa.selfcare.onboarding.common.TokenType; import it.pagopa.selfcare.onboarding.config.MailTemplatePathConfig; import it.pagopa.selfcare.onboarding.config.MailTemplatePlaceholdersConfig; +import it.pagopa.selfcare.onboarding.utils.InstitutionUtils; import it.pagopa.selfcare.product.entity.Product; - import java.util.Objects; public class OnboardingWorkflowUser extends OnboardingWorkflow { - private static final String PDF_FORMAT_USER_FILENAME = "%s_nomina_referente.pdf"; - - private String type; - - public OnboardingWorkflowUser(Onboarding onboarding, String type) { - super(onboarding); - this.type = type; - } - - public OnboardingWorkflowUser() { - } - - - @Override - public String emailRegistrationPath(MailTemplatePathConfig config) { - final String managerId = this.onboarding.getUsers().stream() - .filter(user -> PartyRole.MANAGER == user.getRole()) - .map(User::getId) - .findAny() - .orElse(null); - if (Objects.nonNull(this.onboarding.getPreviousManagerId()) - && this.onboarding.getPreviousManagerId().equals(managerId)) { - return config.registrationUserPath(); - } - return config.registrationUserNewManagerPath(); - } - - @Override - public String getEmailCompletionPath(MailTemplatePathConfig config) { - return config.completePathUser(); - } - - @Override - public String getPdfFormatFilename() { - return PDF_FORMAT_USER_FILENAME; - } - - @Override - public TokenType getTokenType() { - return TokenType.USER; - } + private static final String PDF_FORMAT_USER_FILENAME = "%s_nomina_referente.pdf"; - @Override - public String getConfirmTokenUrl(MailTemplatePlaceholdersConfig config) { - return config.confirmTokenUserPlaceholder(); - } + private String type; - @Override - public String getRejectTokenUrl(MailTemplatePlaceholdersConfig config) { - return config.rejectTokenUserPlaceholder(); - } + public OnboardingWorkflowUser(Onboarding onboarding, String type) { + super(onboarding); + this.type = type; + } - @Override - public String getContractTemplatePath(Product product) { - return product.getUserContractTemplatePath(); - } - - @Override - public String getContractTemplateVersion(Product product) { - return product.getUserContractTemplateVersion(); - } - - public String getType() { - return type; - } + public OnboardingWorkflowUser() {} - public void setType(String type) { - this.type = type; + @Override + public String getEmailRegistrationPath(MailTemplatePathConfig config) { + final String managerId = + this.onboarding.getUsers().stream() + .filter(user -> PartyRole.MANAGER == user.getRole()) + .map(User::getId) + .findAny() + .orElse(null); + if (Objects.nonNull(this.onboarding.getPreviousManagerId()) + && this.onboarding.getPreviousManagerId().equals(managerId)) { + return config.registrationUserPath(); } + return config.registrationUserNewManagerPath(); + } + + @Override + public String getEmailCompletionPath(MailTemplatePathConfig config) { + return config.completePathUser(); + } + + @Override + public String getPdfFormatFilename() { + return PDF_FORMAT_USER_FILENAME; + } + + @Override + public TokenType getTokenType() { + return TokenType.USER; + } + + @Override + public String getConfirmTokenUrl(MailTemplatePlaceholdersConfig config) { + return config.confirmTokenUserPlaceholder(); + } + + @Override + public String getRejectTokenUrl(MailTemplatePlaceholdersConfig config) { + return config.rejectTokenUserPlaceholder(); + } + + @Override + public String getContractTemplatePath(Product product) { + return product + .getUserContractTemplate(InstitutionUtils.getCurrentInstitutionType(onboarding)) + .getContractTemplatePath(); + } + + @Override + public String getContractTemplateVersion(Product product) { + return product + .getUserContractTemplate(InstitutionUtils.getCurrentInstitutionType(onboarding)) + .getContractTemplateVersion(); + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } } diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingService.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingService.java index 9d61bdee6..13d6b65af 100644 --- a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingService.java +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/service/OnboardingService.java @@ -1,5 +1,8 @@ package it.pagopa.selfcare.onboarding.service; +import static it.pagopa.selfcare.onboarding.utils.Utils.*; +import static it.pagopa.selfcare.onboarding.utils.Utils.NOT_ALLOWED_WORKFLOWS_FOR_INSTITUTION_NOTIFICATIONS; + import com.microsoft.azure.functions.ExecutionContext; import eu.europa.esig.dss.enumerations.DigestAlgorithm; import eu.europa.esig.dss.model.DSSDocument; @@ -18,18 +21,11 @@ import it.pagopa.selfcare.onboarding.repository.OnboardingRepository; import it.pagopa.selfcare.onboarding.repository.TokenRepository; import it.pagopa.selfcare.onboarding.utils.GenericError; +import it.pagopa.selfcare.onboarding.utils.InstitutionUtils; import it.pagopa.selfcare.product.entity.Product; import it.pagopa.selfcare.product.service.ProductService; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import org.bson.Document; -import org.eclipse.microprofile.rest.client.inject.RestClient; -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.UserResource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.File; import java.time.LocalDate; import java.time.LocalDateTime; @@ -39,10 +35,14 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; - -import static it.pagopa.selfcare.onboarding.utils.Utils.*; import java.util.stream.Stream; -import static it.pagopa.selfcare.onboarding.utils.Utils.NOT_ALLOWED_WORKFLOWS_FOR_INSTITUTION_NOTIFICATIONS; +import org.bson.Document; +import org.eclipse.microprofile.rest.client.inject.RestClient; +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.UserResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @ApplicationScoped public class OnboardingService { @@ -102,7 +102,7 @@ public void createContract(OnboardingWorkflow onboardingWorkflow) { public void loadContract(Onboarding onboarding) { Product product = productService.getProductIsValid(onboarding.getProductId()); - contractService.loadContractPDF(product.getContractTemplatePath(), onboarding.getId(), product.getTitle()); + contractService.loadContractPDF(product.getInstitutionContractTemplate(InstitutionUtils.getCurrentInstitutionType(onboarding)).getContractTemplatePath(), onboarding.getId(), product.getTitle()); } public void saveTokenWithContract(OnboardingWorkflow onboardingWorkflow) { @@ -137,7 +137,7 @@ private void saveToken(OnboardingWorkflow onboardingWorkflow, Product product, S token.setId(onboarding.getId()); token.setOnboardingId(onboarding.getId()); token.setContractTemplate(onboardingWorkflow.getContractTemplatePath(product)); - token.setContractVersion(product.getContractTemplateVersion()); + token.setContractVersion(onboardingWorkflow.getContractTemplateVersion(product)); token.setContractFilename(CONTRACT_FILENAME_FUNC.apply(onboardingWorkflow.getPdfFormatFilename(), product.getTitle())); token.setCreatedAt(LocalDateTime.now()); token.setUpdatedAt(LocalDateTime.now()); @@ -161,7 +161,7 @@ public void sendMailRegistrationForContract(OnboardingWorkflow onboardingWorkflo Onboarding onboarding = onboardingWorkflow.getOnboarding(); SendMailInput sendMailInput = builderWithProductAndUserRequest(onboarding); - final String templatePath = onboardingWorkflow.emailRegistrationPath(mailTemplatePathConfig); + final String templatePath = onboardingWorkflow.getEmailRegistrationPath(mailTemplatePathConfig); final String confirmTokenUrl = onboardingWorkflow.getConfirmTokenUrl(mailTemplatePlaceholdersConfig); notificationService.sendMailRegistrationForContract(onboarding.getId(), @@ -186,7 +186,7 @@ public void sendMailRegistrationForContractWhenApprove(OnboardingWorkflow onboar onboarding.getInstitution().getDigitalAddress(), onboarding.getInstitution().getDescription(), "", product.getTitle(), "description", - onboardingWorkflow.emailRegistrationPath(mailTemplatePathConfig), + onboardingWorkflow.getEmailRegistrationPath(mailTemplatePathConfig), onboardingWorkflow.getConfirmTokenUrl(mailTemplatePlaceholdersConfig)); } diff --git a/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/utils/InstitutionUtils.java b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/utils/InstitutionUtils.java new file mode 100644 index 000000000..3e1881652 --- /dev/null +++ b/apps/onboarding-functions/src/main/java/it/pagopa/selfcare/onboarding/utils/InstitutionUtils.java @@ -0,0 +1,21 @@ +package it.pagopa.selfcare.onboarding.utils; + +import it.pagopa.selfcare.onboarding.entity.Onboarding; +import it.pagopa.selfcare.product.entity.Product; +import java.util.Objects; + +public class InstitutionUtils { + + public InstitutionUtils() {} + + public static String getCurrentInstitutionType(Onboarding onboarding) { + String institutionType = Product.CONTRACT_TYPE_DEFAULT; + + if (Objects.isNull(onboarding.getInstitution()) + || Objects.isNull(onboarding.getInstitution().getInstitutionType())) { + institutionType = onboarding.getInstitution().getInstitutionType().name(); + } + + return institutionType; + } +} 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 50da005a8..0a8d55302 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 @@ -1,5 +1,9 @@ package it.pagopa.selfcare.onboarding.service; +import static it.pagopa.selfcare.onboarding.service.OnboardingService.USERS_FIELD_LIST; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.microsoft.azure.functions.ExecutionContext; @@ -10,16 +14,20 @@ import io.quarkus.test.junit.TestProfile; import it.pagopa.selfcare.onboarding.common.*; import it.pagopa.selfcare.onboarding.dto.OnboardingAggregateOrchestratorInput; -import it.pagopa.selfcare.onboarding.entity.Billing; import it.pagopa.selfcare.onboarding.entity.*; +import it.pagopa.selfcare.onboarding.entity.Billing; import it.pagopa.selfcare.onboarding.exception.GenericOnboardingException; import it.pagopa.selfcare.onboarding.repository.OnboardingRepository; import it.pagopa.selfcare.onboarding.repository.TokenRepository; +import it.pagopa.selfcare.product.entity.ContractTemplate; import it.pagopa.selfcare.product.entity.Product; import it.pagopa.selfcare.product.service.ProductService; import jakarta.inject.Inject; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Response; +import java.time.LocalDateTime; +import java.util.*; +import java.util.logging.Logger; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.jboss.resteasy.core.ServerResponse; import org.junit.jupiter.api.Assertions; @@ -41,14 +49,6 @@ import org.openapi.quarkus.user_registry_json.model.UserResource; import org.openapi.quarkus.user_registry_json.model.WorkContactResource; -import java.time.LocalDateTime; -import java.util.*; -import java.util.logging.Logger; - -import static it.pagopa.selfcare.onboarding.service.OnboardingService.USERS_FIELD_LIST; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - @QuarkusTest public class CompletionServiceDefaultTest { @@ -1117,12 +1117,30 @@ private Onboarding createOnboarding() { private Product createDummyProduct() { Product product = new Product(); - product.setContractTemplatePath("example"); - product.setContractTemplateVersion("version"); + product.setInstitutionContractMappings(createDummyContractTemplateInstitution()); + product.setUserContractMappings(createDummyContractTemplateInstitution()); product.setTitle("Title"); product.setId(productId); return product; } + private static Map createDummyContractTemplateInstitution() { + Map institutionTemplate = new HashMap<>(); + ContractTemplate conctractTemplate = new ContractTemplate(); + conctractTemplate.setContractTemplatePath("example"); + conctractTemplate.setContractTemplateVersion("version"); + institutionTemplate.put(Product.CONTRACT_TYPE_DEFAULT, conctractTemplate); + return institutionTemplate; + } + + private static Map createDummyContractTemplateUser() { + Map institutionTemplate = new HashMap<>(); + ContractTemplate conctractTemplate = new ContractTemplate(); + conctractTemplate.setContractTemplatePath("example"); + conctractTemplate.setContractTemplateVersion("version"); + institutionTemplate.put("default", conctractTemplate); + return institutionTemplate; + } + } diff --git a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceTest.java b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceTest.java index b73dde762..2499b7e2f 100644 --- a/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceTest.java +++ b/apps/onboarding-functions/src/test/java/it/pagopa/selfcare/onboarding/service/OnboardingServiceTest.java @@ -1,5 +1,11 @@ package it.pagopa.selfcare.onboarding.service; +import static it.pagopa.selfcare.onboarding.service.OnboardingService.USERS_FIELD_LIST; +import static it.pagopa.selfcare.onboarding.service.OnboardingService.USERS_WORKS_FIELD_LIST; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + import com.microsoft.azure.functions.ExecutionContext; import eu.europa.esig.dss.enumerations.DigestAlgorithm; import eu.europa.esig.dss.model.DSSDocument; @@ -15,10 +21,13 @@ import it.pagopa.selfcare.onboarding.exception.GenericOnboardingException; import it.pagopa.selfcare.onboarding.repository.OnboardingRepository; import it.pagopa.selfcare.onboarding.repository.TokenRepository; -import it.pagopa.selfcare.product.entity.ContractStorage; +import it.pagopa.selfcare.product.entity.ContractTemplate; import it.pagopa.selfcare.product.entity.Product; import it.pagopa.selfcare.product.service.ProductService; import jakarta.inject.Inject; +import java.io.File; +import java.util.*; +import java.util.logging.Logger; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -27,519 +36,548 @@ import org.openapi.quarkus.user_registry_json.model.CertifiableFieldResourceOfstring; import org.openapi.quarkus.user_registry_json.model.UserResource; -import java.io.File; -import java.util.*; -import java.util.logging.Logger; - -import static it.pagopa.selfcare.onboarding.service.OnboardingService.USERS_FIELD_LIST; -import static it.pagopa.selfcare.onboarding.service.OnboardingService.USERS_WORKS_FIELD_LIST; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - @QuarkusTest class OnboardingServiceTest { - @InjectMock - OnboardingRepository onboardingRepository; - @InjectMock - TokenRepository tokenRepository; - @RestClient - @InjectMock - UserApi userRegistryApi; - @InjectMock - NotificationService notificationService; - @InjectMock - ContractService contractService; - @InjectMock - ProductService productService; - - @Inject - OnboardingService onboardingService; - - final String productId = "productId"; - - private Onboarding createOnboarding() { - Onboarding onboarding = new Onboarding(); - onboarding.setId(onboarding.getId()); - onboarding.setProductId(productId); - onboarding.setUsers(List.of()); - Institution institution = new Institution(); - institution.setDescription("description"); - institution.setInstitutionType(InstitutionType.PA); - onboarding.setInstitution(institution); - onboarding.setUserRequestUid("example-uid"); - return onboarding; - } - - private UserResource createUserResource(){ - UserResource userResource = new UserResource(); - userResource.setId(UUID.randomUUID()); - - CertifiableFieldResourceOfstring resourceOfName = new CertifiableFieldResourceOfstring(); - resourceOfName.setCertification(CertifiableFieldResourceOfstring.CertificationEnum.NONE); - resourceOfName.setValue("name"); - userResource.setName(resourceOfName); - - CertifiableFieldResourceOfstring resourceOfSurname = new CertifiableFieldResourceOfstring(); - resourceOfSurname.setCertification(CertifiableFieldResourceOfstring.CertificationEnum.NONE); - resourceOfSurname.setValue("surname"); - userResource.setFamilyName(resourceOfSurname); - return userResource; - } - - @Test - void getOnboarding() { - Onboarding onboarding = createOnboarding(); - when(onboardingRepository.findByIdOptional(any())).thenReturn(Optional.of(onboarding)); - - Optional actual = onboardingService.getOnboarding(onboarding.getId()); - assertTrue(actual.isPresent()); - assertEquals(onboarding.getId(), actual.get().getId()); - } - - @Test - void createContract_shouldThrowIfManagerNotfound() { - Onboarding onboarding = createOnboarding(); - OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); - assertThrows(GenericOnboardingException.class, () -> onboardingService.createContract(onboardingWorkflow)); - } - - @Test - void createContract_InstitutionContractMappings() { - - UserResource userResource = createUserResource(); - - Onboarding onboarding = createOnboarding(); - User manager = new User(); - manager.setId(userResource.getId().toString()); - manager.setRole(PartyRole.MANAGER); - onboarding.setUsers(List.of(manager)); - - Product product = createDummyProduct(); - /* add contract mapping */ - Map contractStorageMap = new HashMap<>(); - ContractStorage contractStorage = new ContractStorage(); - contractStorage.setContractTemplatePath("setContractTemplatePath"); - contractStorageMap.put(onboarding.getInstitution().getInstitutionType().name(), contractStorage); - product.setInstitutionContractMappings(contractStorageMap); - - when(userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST,manager.getId())) - .thenReturn(userResource); - - when(productService.getProductIsValid(onboarding.getProductId())) - .thenReturn(product); - - OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); - onboardingService.createContract(onboardingWorkflow); - - Mockito.verify(userRegistryApi, Mockito.times(1)) - .findByIdUsingGET(USERS_WORKS_FIELD_LIST,manager.getId()); - - Mockito.verify(productService, Mockito.times(1)) - .getProductIsValid(onboarding.getProductId()); - - ArgumentCaptor captorTemplatePath = ArgumentCaptor.forClass(String.class); - Mockito.verify(contractService, Mockito.times(1)) - .createContractPDF(captorTemplatePath.capture(), any(), any(), any(), any(), any()); - assertEquals(captorTemplatePath.getValue(), contractStorage.getContractTemplatePath()); - } - - private static OnboardingWorkflow getOnboardingWorkflowInstitution(Onboarding onboarding) { - return new OnboardingWorkflowInstitution(onboarding, "INSTITUTION"); - } - - @Test - void createContract() { - - UserResource userResource = createUserResource(); - UserResource delegateResource = createUserResource(); - - Onboarding onboarding = createOnboarding(); - User manager = new User(); - manager.setId(userResource.getId().toString()); - manager.setRole(PartyRole.MANAGER); - User delegate = new User(); - delegate.setId(delegateResource.getId().toString()); - delegate.setRole(PartyRole.DELEGATE); - onboarding.setUsers(List.of(manager, delegate)); - - Product product = createDummyProduct(); - - when(userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST,manager.getId())) - .thenReturn(userResource); - - when(userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST,delegate.getId())) - .thenReturn(delegateResource); - - when(productService.getProductIsValid(onboarding.getProductId())) - .thenReturn(product); - - OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); - onboardingService.createContract(onboardingWorkflow); - - Mockito.verify(userRegistryApi, Mockito.times(1)) - .findByIdUsingGET(USERS_WORKS_FIELD_LIST,manager.getId()); - - Mockito.verify(userRegistryApi, Mockito.times(1)) - .findByIdUsingGET(USERS_WORKS_FIELD_LIST,delegate.getId()); - - Mockito.verify(productService, Mockito.times(1)) - .getProductIsValid(onboarding.getProductId()); - - ArgumentCaptor captorTemplatePath = ArgumentCaptor.forClass(String.class); - Mockito.verify(contractService, Mockito.times(1)) - .createContractPDF(captorTemplatePath.capture(), any(), any(), any(), any(), any()); - assertEquals(captorTemplatePath.getValue(), product.getContractTemplatePath()); - } - - private Product createDummyProduct() { - Product product = new Product(); - product.setContractTemplatePath("example"); - product.setContractTemplateVersion("version"); - product.setTitle("Title"); - product.setId(productId); - return product; - } - @Test - void saveToken_shouldSkipIfTokenExists() { - OnboardingWorkflow onboardingWorkflow = new OnboardingWorkflowInstitution(); - Onboarding onboarding = createOnboarding(); - Token token = createDummyToken(); - onboardingWorkflow.setOnboarding(onboarding); - - when(tokenRepository.findByOnboardingId(onboarding.getId())) - .thenReturn(Optional.of(token)); - - onboardingService.saveTokenWithContract(onboardingWorkflow); - - Mockito.verify(tokenRepository, Mockito.times(1)) - .findByOnboardingId(onboarding.getId()); - Mockito.verifyNoMoreInteractions(tokenRepository); - } - - @Test - void saveToken() { - Onboarding onboarding = createOnboarding(); - OnboardingWorkflow onboardingWorkflow = new OnboardingWorkflowInstitution(); - onboardingWorkflow.setOnboarding(onboarding); - File contract = new File(Objects.requireNonNull(getClass().getClassLoader().getResource("application.properties")).getFile()); - DSSDocument document = new FileDocument(contract); - String digestExpected = document.getDigest(DigestAlgorithm.SHA256); - - Product productExpected = createDummyProduct(); - when(contractService.retrieveContractNotSigned(onboardingWorkflow, productExpected.getTitle())) - .thenReturn(contract); - when(productService.getProductIsValid(onboarding.getProductId())) - .thenReturn(productExpected); - - Mockito.doNothing().when(tokenRepository).persist(any(Token.class)); - - onboardingService.saveTokenWithContract(onboardingWorkflow); - - ArgumentCaptor tokenArgumentCaptor = ArgumentCaptor.forClass(Token.class); - Mockito.verify(tokenRepository, Mockito.times(1)) - .persist(tokenArgumentCaptor.capture()); - assertEquals(onboarding.getProductId(), tokenArgumentCaptor.getValue().getProductId()); - assertEquals(digestExpected, tokenArgumentCaptor.getValue().getChecksum()); - assertEquals(productExpected.getContractTemplatePath(), tokenArgumentCaptor.getValue().getContractTemplate()); - assertEquals(productExpected.getContractTemplateVersion(), tokenArgumentCaptor.getValue().getContractVersion()); - } - - @Test - void loadContract() { - - Onboarding onboarding = createOnboarding(); - Product product = createDummyProduct(); - - when(productService.getProductIsValid(onboarding.getProductId())) - .thenReturn(product); - - onboardingService.loadContract(onboarding); - - Mockito.verify(productService, Mockito.times(1)) - .getProductIsValid(onboarding.getProductId()); - Mockito.verify(contractService, Mockito.times(1)) - .loadContractPDF(product.getContractTemplatePath(), onboarding.getId(), product.getTitle()); - } - - @Test - void sendMailRegistrationWithContract() { - - Onboarding onboarding = createOnboarding(); - Product product = createDummyProduct(); - UserResource userResource = createUserResource(); - Token token = createDummyToken(); - - when(tokenRepository.findByOnboardingId(onboarding.getId())) - .thenReturn(Optional.of(token)); - when(productService.getProduct(onboarding.getProductId())) - .thenReturn(product); - - when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) - .thenReturn(userResource); - - OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); - OnboardingService.SendMailInput sendMailInput = new OnboardingService.SendMailInput(); - sendMailInput.userRequestName = userResource.getName().getValue(); - sendMailInput.userRequestSurname = userResource.getFamilyName().getValue(); - sendMailInput.product = product; - sendMailInput.institutionName = "description"; - - doNothing().when(notificationService) - .sendMailRegistrationForContract(onboarding.getId(), - onboarding.getInstitution().getDigitalAddress(), - sendMailInput, - "default", "default"); - - onboardingService.sendMailRegistrationForContract(onboardingWorkflow); - - Mockito.verify(notificationService, times(1)) - .sendMailRegistrationForContract(any(), - any(), - any(), - anyString(), - anyString()); - verifyNoMoreInteractions(notificationService); - } - - @Test - void sendMailRegistrationWithContractAggregator() { - - Onboarding onboarding = createOnboarding(); - Product product = createDummyProduct(); - UserResource userResource = createUserResource(); - Token token = createDummyToken(); - - when(tokenRepository.findByOnboardingId(onboarding.getId())) - .thenReturn(Optional.of(token)); - when(productService.getProduct(onboarding.getProductId())) - .thenReturn(product); - - when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) - .thenReturn(userResource); - doNothing().when(notificationService) - .sendMailRegistrationForContractAggregator(onboarding.getId(), - onboarding.getInstitution().getDigitalAddress(), - userResource.getName().getValue(), userResource.getFamilyName().getValue(), - product.getTitle()); - - onboardingService.sendMailRegistrationForContractAggregator(onboarding); - - Mockito.verify(notificationService, times(1)) - .sendMailRegistrationForContractAggregator(onboarding.getId(), - onboarding.getInstitution().getDigitalAddress(), - userResource.getName().getValue(), userResource.getFamilyName().getValue(), - product.getTitle()); - } - - @Test - void sendMailRegistrationWithContractWhenApprove() { - - Onboarding onboarding = createOnboarding(); - Product product = createDummyProduct(); - Token token = createDummyToken(); - - when(tokenRepository.findByOnboardingId(onboarding.getId())) - .thenReturn(Optional.of(token)); - when(productService.getProduct(onboarding.getProductId())) - .thenReturn(product); - - OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); - - doNothing().when(notificationService) - .sendMailRegistrationForContract(onboarding.getId(), - onboarding.getInstitution().getDigitalAddress(), - onboarding.getInstitution().getDescription(), "", - product.getTitle(), "description", - "default", "default"); - - onboardingService.sendMailRegistrationForContractWhenApprove(onboardingWorkflow); - - Mockito.verify(notificationService, times(1)) - .sendMailRegistrationForContract(onboarding.getId(), - onboarding.getInstitution().getDigitalAddress(), - onboarding.getInstitution().getDescription(), "", - product.getTitle(), "description", - "default", "default"); - } - - @Test - void sendMailRegistrationWithContract_throwExceptionWhenTokenIsNotPresent() { - Onboarding onboarding = createOnboarding(); - OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); - when(tokenRepository.findByOnboardingId(onboarding.getId())) - .thenReturn(Optional.empty()); - assertThrows(GenericOnboardingException.class, () -> onboardingService.sendMailRegistrationForContract(onboardingWorkflow)); - } - - @Test - void sendMailRegistration() { - - UserResource userResource = createUserResource(); - Product product = createDummyProduct(); - Onboarding onboarding = createOnboarding(); - - when(productService.getProduct(onboarding.getProductId())) - .thenReturn(product); - when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) - .thenReturn(userResource); - doNothing().when(notificationService).sendMailRegistration(onboarding.getInstitution().getDescription(), - onboarding.getInstitution().getDigitalAddress(), - userResource.getName().getValue(), userResource.getFamilyName().getValue(), - product.getTitle()); - - onboardingService.sendMailRegistration(onboarding); - - Mockito.verify(notificationService, times(1)) - .sendMailRegistration(onboarding.getInstitution().getDescription(), - onboarding.getInstitution().getDigitalAddress(), - userResource.getName().getValue(), userResource.getFamilyName().getValue(), - product.getTitle()); - } - - @Test - void sendMailRegistrationApprove() { - - Onboarding onboarding = createOnboarding(); - Product product = createDummyProduct(); - UserResource userResource = createUserResource(); - - when(productService.getProduct(onboarding.getProductId())) - .thenReturn(product); - when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) - .thenReturn(userResource); - - doNothing().when(notificationService) - .sendMailRegistrationApprove(any(), any(), any(),any(),any()); - - onboardingService.sendMailRegistrationApprove(onboarding); - - Mockito.verify(notificationService, times(1)) - .sendMailRegistrationApprove(onboarding.getInstitution().getDescription(), - userResource.getName().getValue(), - userResource.getFamilyName().getValue(), - product.getTitle(), - onboarding.getId()); - } - - @Test - void sendMailRegistrationApprove_throwExceptionWhenTokenIsNotPresent() { - Onboarding onboarding = createOnboarding(); - when(tokenRepository.findByOnboardingId(onboarding.getId())) - .thenReturn(Optional.empty()); - assertThrows(GenericOnboardingException.class, () -> onboardingService.sendMailRegistrationApprove(onboarding)); - } - - - @Test - void sendMailOnboardingApprove() { - - Onboarding onboarding = createOnboarding(); - Product product = createDummyProduct(); - UserResource userResource = createUserResource(); - - when(productService.getProduct(onboarding.getProductId())) - .thenReturn(product); - when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) - .thenReturn(userResource); - doNothing().when(notificationService).sendMailOnboardingApprove(any(), any(), any(),any(),any()); - - onboardingService.sendMailOnboardingApprove(onboarding); - - Mockito.verify(notificationService, times(1)) - .sendMailOnboardingApprove(onboarding.getInstitution().getDescription(), - userResource.getName().getValue(), - userResource.getFamilyName().getValue(), - product.getTitle(), - onboarding.getId()); - } - - - @Test - void sendMailOnboardingApprove_throwExceptionWhenTokenIsNotPresent() { - Onboarding onboarding = createOnboarding(); - when(tokenRepository.findByOnboardingId(onboarding.getId())) - .thenReturn(Optional.empty()); - assertThrows(GenericOnboardingException.class, () -> onboardingService.sendMailOnboardingApprove(onboarding)); - } - - @Test - void countOnboardingShouldReturnCorrectResultsWhenProductsExist() { - String from = "2021-01-01"; - String to = "2021-12-31"; - - ExecutionContext context = getExecutionContext(); - - PanacheQuery onboardingQuery = mock(PanacheQuery.class); - - Product product1 = new Product(); - product1.setId("product1"); - when(productService.getProducts(false, false)) - .thenReturn(List.of(product1)); - - when(onboardingRepository.find(any())).thenReturn(onboardingQuery); - when(onboardingQuery.count()).thenReturn(5L).thenReturn(3L); - - List results = onboardingService.countNotifications(product1.getId(),from,to,context); - - assertEquals(1, results.size()); - assertEquals(8, results.get(0).getNotificationCount()); - } - - @Test - void countOnboardingShouldReturnEmptyListWhenNoProductsExist() { - ExecutionContext context = getExecutionContext(); - - when(productService.getProducts(true, false)).thenReturn(List.of()); - List results = onboardingService.countNotifications(null, null, null, context); - assertTrue(results.isEmpty()); - } - - @Test - void getOnboardingsToResendShouldReturnResults() { - ResendNotificationsFilters filters = new ResendNotificationsFilters(); - filters.setFrom("2021-01-01"); - filters.setTo("2021-12-31"); - - getExecutionContext(); - - PanacheQuery onboardingQuery = mock(PanacheQuery.class); - when(onboardingRepository.find(any())).thenReturn(onboardingQuery); - when(onboardingQuery.page(anyInt(), anyInt())).thenReturn(onboardingQuery); - when(onboardingQuery.list()).thenReturn(List.of(new Onboarding(), new Onboarding())); - - List onboardings = onboardingService.getOnboardingsToResend(filters, 0, 100); - assertEquals(2, onboardings.size()); - } - - @Test - void getOnboardingsToResendShouldReturnEmptyList() { - ResendNotificationsFilters filters = new ResendNotificationsFilters(); - filters.setFrom("2021-01-01"); - filters.setTo("2021-12-31"); - - getExecutionContext(); - - PanacheQuery onboardingQuery = mock(PanacheQuery.class); - when(onboardingRepository.find(any())).thenReturn(onboardingQuery); - when(onboardingQuery.page(anyInt(), anyInt())).thenReturn(onboardingQuery); - when(onboardingQuery.list()).thenReturn(List.of()); - - List onboardings = onboardingService.getOnboardingsToResend(filters, 0, 100); - assertTrue(onboardings.isEmpty()); - } - - private ExecutionContext getExecutionContext() { - ExecutionContext context = mock(ExecutionContext.class); - doReturn(Logger.getGlobal()).when(context).getLogger(); - return context; - } - - private Token createDummyToken() { - Token token = new Token(); - token.setId(UUID.randomUUID().toString()); - return token; - } - + @InjectMock OnboardingRepository onboardingRepository; + @InjectMock TokenRepository tokenRepository; + @RestClient @InjectMock UserApi userRegistryApi; + @InjectMock NotificationService notificationService; + @InjectMock ContractService contractService; + @InjectMock ProductService productService; + + @Inject OnboardingService onboardingService; + + final String productId = "productId"; + + private Onboarding createOnboarding() { + Onboarding onboarding = new Onboarding(); + onboarding.setId(onboarding.getId()); + onboarding.setProductId(productId); + onboarding.setUsers(List.of()); + Institution institution = new Institution(); + institution.setDescription("description"); + institution.setInstitutionType(InstitutionType.PA); + onboarding.setInstitution(institution); + onboarding.setUserRequestUid("example-uid"); + return onboarding; + } + + private UserResource createUserResource() { + UserResource userResource = new UserResource(); + userResource.setId(UUID.randomUUID()); + + CertifiableFieldResourceOfstring resourceOfName = new CertifiableFieldResourceOfstring(); + resourceOfName.setCertification(CertifiableFieldResourceOfstring.CertificationEnum.NONE); + resourceOfName.setValue("name"); + userResource.setName(resourceOfName); + + CertifiableFieldResourceOfstring resourceOfSurname = new CertifiableFieldResourceOfstring(); + resourceOfSurname.setCertification(CertifiableFieldResourceOfstring.CertificationEnum.NONE); + resourceOfSurname.setValue("surname"); + userResource.setFamilyName(resourceOfSurname); + return userResource; + } + + @Test + void getOnboarding() { + Onboarding onboarding = createOnboarding(); + when(onboardingRepository.findByIdOptional(any())).thenReturn(Optional.of(onboarding)); + + Optional actual = onboardingService.getOnboarding(onboarding.getId()); + assertTrue(actual.isPresent()); + assertEquals(onboarding.getId(), actual.get().getId()); + } + + @Test + void createContract_shouldThrowIfManagerNotfound() { + Onboarding onboarding = createOnboarding(); + OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); + assertThrows( + GenericOnboardingException.class, + () -> onboardingService.createContract(onboardingWorkflow)); + } + + @Test + void createContract_InstitutionContractMappings() { + + UserResource userResource = createUserResource(); + + Onboarding onboarding = createOnboarding(); + User manager = new User(); + manager.setId(userResource.getId().toString()); + manager.setRole(PartyRole.MANAGER); + onboarding.setUsers(List.of(manager)); + + Product product = createDummyProduct(); + + when(userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST, manager.getId())) + .thenReturn(userResource); + + when(productService.getProductIsValid(onboarding.getProductId())).thenReturn(product); + + OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); + onboardingService.createContract(onboardingWorkflow); + + Mockito.verify(userRegistryApi, Mockito.times(1)) + .findByIdUsingGET(USERS_WORKS_FIELD_LIST, manager.getId()); + + Mockito.verify(productService, Mockito.times(1)).getProductIsValid(onboarding.getProductId()); + + ArgumentCaptor captorTemplatePath = ArgumentCaptor.forClass(String.class); + Mockito.verify(contractService, Mockito.times(1)) + .createContractPDF(captorTemplatePath.capture(), any(), any(), any(), any(), any()); + assertEquals( + captorTemplatePath.getValue(), + product + .getInstitutionContractTemplate(Product.CONTRACT_TYPE_DEFAULT) + .getContractTemplatePath()); + } + + private static OnboardingWorkflow getOnboardingWorkflowInstitution(Onboarding onboarding) { + return new OnboardingWorkflowInstitution(onboarding, "INSTITUTION"); + } + + @Test + void createContract() { + + UserResource userResource = createUserResource(); + UserResource delegateResource = createUserResource(); + + Onboarding onboarding = createOnboarding(); + User manager = new User(); + manager.setId(userResource.getId().toString()); + manager.setRole(PartyRole.MANAGER); + User delegate = new User(); + delegate.setId(delegateResource.getId().toString()); + delegate.setRole(PartyRole.DELEGATE); + onboarding.setUsers(List.of(manager, delegate)); + + Product product = createDummyProduct(); + + when(userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST, manager.getId())) + .thenReturn(userResource); + + when(userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST, delegate.getId())) + .thenReturn(delegateResource); + + when(productService.getProductIsValid(onboarding.getProductId())).thenReturn(product); + + OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); + onboardingService.createContract(onboardingWorkflow); + + Mockito.verify(userRegistryApi, Mockito.times(1)) + .findByIdUsingGET(USERS_WORKS_FIELD_LIST, manager.getId()); + + Mockito.verify(userRegistryApi, Mockito.times(1)) + .findByIdUsingGET(USERS_WORKS_FIELD_LIST, delegate.getId()); + + Mockito.verify(productService, Mockito.times(1)).getProductIsValid(onboarding.getProductId()); + + ArgumentCaptor captorTemplatePath = ArgumentCaptor.forClass(String.class); + Mockito.verify(contractService, Mockito.times(1)) + .createContractPDF(captorTemplatePath.capture(), any(), any(), any(), any(), any()); + assertEquals( + captorTemplatePath.getValue(), + product + .getInstitutionContractTemplate(Product.CONTRACT_TYPE_DEFAULT) + .getContractTemplatePath()); + } + + private Product createDummyProduct() { + Product product = new Product(); + product.setTitle("Title"); + product.setId(productId); + product.setInstitutionContractMappings(createDummyContractTemplateInstitution()); + product.setUserContractMappings(createDummyContractTemplateInstitution()); + + return product; + } + + private static Map createDummyContractTemplateInstitution() { + Map institutionTemplate = new HashMap<>(); + ContractTemplate conctractTemplate = new ContractTemplate(); + conctractTemplate.setContractTemplatePath("example"); + conctractTemplate.setContractTemplateVersion("version"); + institutionTemplate.put(Product.CONTRACT_TYPE_DEFAULT, conctractTemplate); + return institutionTemplate; + } + + private static Map createDummyContractTemplateUser() { + Map institutionTemplate = new HashMap<>(); + ContractTemplate conctractTemplate = new ContractTemplate(); + conctractTemplate.setContractTemplatePath("example"); + conctractTemplate.setContractTemplateVersion("version"); + institutionTemplate.put("default", conctractTemplate); + return institutionTemplate; + } + + @Test + void saveToken_shouldSkipIfTokenExists() { + OnboardingWorkflow onboardingWorkflow = new OnboardingWorkflowInstitution(); + Onboarding onboarding = createOnboarding(); + Token token = createDummyToken(); + onboardingWorkflow.setOnboarding(onboarding); + + when(tokenRepository.findByOnboardingId(onboarding.getId())).thenReturn(Optional.of(token)); + + onboardingService.saveTokenWithContract(onboardingWorkflow); + + Mockito.verify(tokenRepository, Mockito.times(1)).findByOnboardingId(onboarding.getId()); + Mockito.verifyNoMoreInteractions(tokenRepository); + } + + @Test + void saveToken() { + Onboarding onboarding = createOnboarding(); + OnboardingWorkflow onboardingWorkflow = new OnboardingWorkflowInstitution(); + onboardingWorkflow.setOnboarding(onboarding); + File contract = + new File( + Objects.requireNonNull( + getClass().getClassLoader().getResource("application.properties")) + .getFile()); + DSSDocument document = new FileDocument(contract); + String digestExpected = document.getDigest(DigestAlgorithm.SHA256); + + Product productExpected = createDummyProduct(); + when(contractService.retrieveContractNotSigned(onboardingWorkflow, productExpected.getTitle())) + .thenReturn(contract); + when(productService.getProductIsValid(onboarding.getProductId())).thenReturn(productExpected); + + Mockito.doNothing().when(tokenRepository).persist(any(Token.class)); + + onboardingService.saveTokenWithContract(onboardingWorkflow); + + ArgumentCaptor tokenArgumentCaptor = ArgumentCaptor.forClass(Token.class); + Mockito.verify(tokenRepository, Mockito.times(1)).persist(tokenArgumentCaptor.capture()); + assertEquals(onboarding.getProductId(), tokenArgumentCaptor.getValue().getProductId()); + assertEquals(digestExpected, tokenArgumentCaptor.getValue().getChecksum()); + assertEquals( + productExpected + .getInstitutionContractTemplate(Product.CONTRACT_TYPE_DEFAULT) + .getContractTemplatePath(), + tokenArgumentCaptor.getValue().getContractTemplate()); + assertEquals( + productExpected + .getInstitutionContractTemplate(Product.CONTRACT_TYPE_DEFAULT) + .getContractTemplateVersion(), + tokenArgumentCaptor.getValue().getContractVersion()); + } + + @Test + void loadContract() { + + Onboarding onboarding = createOnboarding(); + Product product = createDummyProduct(); + + when(productService.getProductIsValid(onboarding.getProductId())).thenReturn(product); + + onboardingService.loadContract(onboarding); + + Mockito.verify(productService, Mockito.times(1)).getProductIsValid(onboarding.getProductId()); + Mockito.verify(contractService, Mockito.times(1)) + .loadContractPDF( + product + .getInstitutionContractTemplate(Product.CONTRACT_TYPE_DEFAULT) + .getContractTemplatePath(), + onboarding.getId(), + product.getTitle()); + } + + @Test + void sendMailRegistrationWithContract() { + + Onboarding onboarding = createOnboarding(); + Product product = createDummyProduct(); + UserResource userResource = createUserResource(); + Token token = createDummyToken(); + + when(tokenRepository.findByOnboardingId(onboarding.getId())).thenReturn(Optional.of(token)); + when(productService.getProduct(onboarding.getProductId())).thenReturn(product); + + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) + .thenReturn(userResource); + + OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); + OnboardingService.SendMailInput sendMailInput = new OnboardingService.SendMailInput(); + sendMailInput.userRequestName = userResource.getName().getValue(); + sendMailInput.userRequestSurname = userResource.getFamilyName().getValue(); + sendMailInput.product = product; + sendMailInput.institutionName = "description"; + + doNothing() + .when(notificationService) + .sendMailRegistrationForContract( + onboarding.getId(), + onboarding.getInstitution().getDigitalAddress(), + sendMailInput, + "default", + "default"); + + onboardingService.sendMailRegistrationForContract(onboardingWorkflow); + + Mockito.verify(notificationService, times(1)) + .sendMailRegistrationForContract(any(), any(), any(), anyString(), anyString()); + verifyNoMoreInteractions(notificationService); + } + + @Test + void sendMailRegistrationWithContractAggregator() { + + Onboarding onboarding = createOnboarding(); + Product product = createDummyProduct(); + UserResource userResource = createUserResource(); + Token token = createDummyToken(); + + when(tokenRepository.findByOnboardingId(onboarding.getId())).thenReturn(Optional.of(token)); + when(productService.getProduct(onboarding.getProductId())).thenReturn(product); + + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) + .thenReturn(userResource); + doNothing() + .when(notificationService) + .sendMailRegistrationForContractAggregator( + onboarding.getId(), + onboarding.getInstitution().getDigitalAddress(), + userResource.getName().getValue(), + userResource.getFamilyName().getValue(), + product.getTitle()); + + onboardingService.sendMailRegistrationForContractAggregator(onboarding); + + Mockito.verify(notificationService, times(1)) + .sendMailRegistrationForContractAggregator( + onboarding.getId(), + onboarding.getInstitution().getDigitalAddress(), + userResource.getName().getValue(), + userResource.getFamilyName().getValue(), + product.getTitle()); + } + + @Test + void sendMailRegistrationWithContractWhenApprove() { + + Onboarding onboarding = createOnboarding(); + Product product = createDummyProduct(); + Token token = createDummyToken(); + + when(tokenRepository.findByOnboardingId(onboarding.getId())).thenReturn(Optional.of(token)); + when(productService.getProduct(onboarding.getProductId())).thenReturn(product); + + OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); + + doNothing() + .when(notificationService) + .sendMailRegistrationForContract( + onboarding.getId(), + onboarding.getInstitution().getDigitalAddress(), + onboarding.getInstitution().getDescription(), + "", + product.getTitle(), + "description", + "default", + "default"); + + onboardingService.sendMailRegistrationForContractWhenApprove(onboardingWorkflow); + + Mockito.verify(notificationService, times(1)) + .sendMailRegistrationForContract( + onboarding.getId(), + onboarding.getInstitution().getDigitalAddress(), + onboarding.getInstitution().getDescription(), + "", + product.getTitle(), + "description", + "default", + "default"); + } + + @Test + void sendMailRegistrationWithContract_throwExceptionWhenTokenIsNotPresent() { + Onboarding onboarding = createOnboarding(); + OnboardingWorkflow onboardingWorkflow = getOnboardingWorkflowInstitution(onboarding); + when(tokenRepository.findByOnboardingId(onboarding.getId())).thenReturn(Optional.empty()); + assertThrows( + GenericOnboardingException.class, + () -> onboardingService.sendMailRegistrationForContract(onboardingWorkflow)); + } + + @Test + void sendMailRegistration() { + + UserResource userResource = createUserResource(); + Product product = createDummyProduct(); + Onboarding onboarding = createOnboarding(); + + when(productService.getProduct(onboarding.getProductId())).thenReturn(product); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) + .thenReturn(userResource); + doNothing() + .when(notificationService) + .sendMailRegistration( + onboarding.getInstitution().getDescription(), + onboarding.getInstitution().getDigitalAddress(), + userResource.getName().getValue(), + userResource.getFamilyName().getValue(), + product.getTitle()); + + onboardingService.sendMailRegistration(onboarding); + + Mockito.verify(notificationService, times(1)) + .sendMailRegistration( + onboarding.getInstitution().getDescription(), + onboarding.getInstitution().getDigitalAddress(), + userResource.getName().getValue(), + userResource.getFamilyName().getValue(), + product.getTitle()); + } + + @Test + void sendMailRegistrationApprove() { + + Onboarding onboarding = createOnboarding(); + Product product = createDummyProduct(); + UserResource userResource = createUserResource(); + + when(productService.getProduct(onboarding.getProductId())).thenReturn(product); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) + .thenReturn(userResource); + + doNothing() + .when(notificationService) + .sendMailRegistrationApprove(any(), any(), any(), any(), any()); + + onboardingService.sendMailRegistrationApprove(onboarding); + + Mockito.verify(notificationService, times(1)) + .sendMailRegistrationApprove( + onboarding.getInstitution().getDescription(), + userResource.getName().getValue(), + userResource.getFamilyName().getValue(), + product.getTitle(), + onboarding.getId()); + } + + @Test + void sendMailRegistrationApprove_throwExceptionWhenTokenIsNotPresent() { + Onboarding onboarding = createOnboarding(); + when(tokenRepository.findByOnboardingId(onboarding.getId())).thenReturn(Optional.empty()); + assertThrows( + GenericOnboardingException.class, + () -> onboardingService.sendMailRegistrationApprove(onboarding)); + } + + @Test + void sendMailOnboardingApprove() { + + Onboarding onboarding = createOnboarding(); + Product product = createDummyProduct(); + UserResource userResource = createUserResource(); + + when(productService.getProduct(onboarding.getProductId())).thenReturn(product); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid())) + .thenReturn(userResource); + doNothing() + .when(notificationService) + .sendMailOnboardingApprove(any(), any(), any(), any(), any()); + + onboardingService.sendMailOnboardingApprove(onboarding); + + Mockito.verify(notificationService, times(1)) + .sendMailOnboardingApprove( + onboarding.getInstitution().getDescription(), + userResource.getName().getValue(), + userResource.getFamilyName().getValue(), + product.getTitle(), + onboarding.getId()); + } + + @Test + void sendMailOnboardingApprove_throwExceptionWhenTokenIsNotPresent() { + Onboarding onboarding = createOnboarding(); + when(tokenRepository.findByOnboardingId(onboarding.getId())).thenReturn(Optional.empty()); + assertThrows( + GenericOnboardingException.class, + () -> onboardingService.sendMailOnboardingApprove(onboarding)); + } + + @Test + void countOnboardingShouldReturnCorrectResultsWhenProductsExist() { + String from = "2021-01-01"; + String to = "2021-12-31"; + + ExecutionContext context = getExecutionContext(); + + PanacheQuery onboardingQuery = mock(PanacheQuery.class); + + Product product1 = new Product(); + product1.setId("product1"); + when(productService.getProducts(false, false)).thenReturn(List.of(product1)); + + when(onboardingRepository.find(any())).thenReturn(onboardingQuery); + when(onboardingQuery.count()).thenReturn(5L).thenReturn(3L); + + List results = + onboardingService.countNotifications(product1.getId(), from, to, context); + + assertEquals(1, results.size()); + assertEquals(8, results.get(0).getNotificationCount()); + } + + @Test + void countOnboardingShouldReturnEmptyListWhenNoProductsExist() { + ExecutionContext context = getExecutionContext(); + + when(productService.getProducts(true, false)).thenReturn(List.of()); + List results = + onboardingService.countNotifications(null, null, null, context); + assertTrue(results.isEmpty()); + } + + @Test + void getOnboardingsToResendShouldReturnResults() { + ResendNotificationsFilters filters = new ResendNotificationsFilters(); + filters.setFrom("2021-01-01"); + filters.setTo("2021-12-31"); + + getExecutionContext(); + + PanacheQuery onboardingQuery = mock(PanacheQuery.class); + when(onboardingRepository.find(any())).thenReturn(onboardingQuery); + when(onboardingQuery.page(anyInt(), anyInt())).thenReturn(onboardingQuery); + when(onboardingQuery.list()).thenReturn(List.of(new Onboarding(), new Onboarding())); + + List onboardings = onboardingService.getOnboardingsToResend(filters, 0, 100); + assertEquals(2, onboardings.size()); + } + + @Test + void getOnboardingsToResendShouldReturnEmptyList() { + ResendNotificationsFilters filters = new ResendNotificationsFilters(); + filters.setFrom("2021-01-01"); + filters.setTo("2021-12-31"); + + getExecutionContext(); + + PanacheQuery onboardingQuery = mock(PanacheQuery.class); + when(onboardingRepository.find(any())).thenReturn(onboardingQuery); + when(onboardingQuery.page(anyInt(), anyInt())).thenReturn(onboardingQuery); + when(onboardingQuery.list()).thenReturn(List.of()); + + List onboardings = onboardingService.getOnboardingsToResend(filters, 0, 100); + assertTrue(onboardings.isEmpty()); + } + + private ExecutionContext getExecutionContext() { + ExecutionContext context = mock(ExecutionContext.class); + doReturn(Logger.getGlobal()).when(context).getLogger(); + return context; + } + + private Token createDummyToken() { + Token token = new Token(); + token.setId(UUID.randomUUID().toString()); + return token; + } } diff --git a/apps/onboarding-ms/pom.xml b/apps/onboarding-ms/pom.xml index 9375b083c..0b34b8b2f 100644 --- a/apps/onboarding-ms/pom.xml +++ b/apps/onboarding-ms/pom.xml @@ -243,12 +243,12 @@ it.pagopa.selfcare onboarding-sdk-azure-storage - 0.3.5 + 0.4.0 it.pagopa.selfcare onboarding-sdk-product - 0.3.5 + 0.4.0 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 793581d63..63e47d01d 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 @@ -1,5 +1,12 @@ package it.pagopa.selfcare.onboarding.service; +import static it.pagopa.selfcare.onboarding.common.InstitutionPaSubunitType.*; +import static it.pagopa.selfcare.onboarding.common.ProductId.PROD_INTEROP; +import static it.pagopa.selfcare.onboarding.common.ProductId.PROD_PAGOPA; +import static it.pagopa.selfcare.onboarding.constants.CustomError.*; +import static it.pagopa.selfcare.onboarding.util.ErrorMessage.*; +import static it.pagopa.selfcare.product.utils.ProductUtils.validRoles; + import io.quarkus.logging.Log; import io.quarkus.mongodb.panache.common.reactive.Panache; import io.quarkus.mongodb.panache.reactive.ReactivePanacheQuery; @@ -31,8 +38,10 @@ import it.pagopa.selfcare.onboarding.model.OnboardingGetFilters; import it.pagopa.selfcare.onboarding.service.strategy.OnboardingValidationStrategy; import it.pagopa.selfcare.onboarding.service.util.OnboardingUtils; +import it.pagopa.selfcare.onboarding.util.InstitutionUtils; import it.pagopa.selfcare.onboarding.util.QueryUtils; import it.pagopa.selfcare.onboarding.util.SortEnum; +import it.pagopa.selfcare.product.entity.ContractTemplate; import it.pagopa.selfcare.product.entity.PHASE_ADDITION_ALLOWED; import it.pagopa.selfcare.product.entity.Product; import it.pagopa.selfcare.product.entity.ProductRoleInfo; @@ -40,6 +49,14 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.ws.rs.WebApplicationException; +import java.io.IOException; +import java.nio.file.Files; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.*; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.bson.Document; @@ -61,1479 +78,2098 @@ import org.openapi.quarkus.user_registry_json.api.UserApi; import org.openapi.quarkus.user_registry_json.model.*; -import java.io.IOException; -import java.nio.file.Files; -import java.time.LocalDateTime; -import java.time.OffsetDateTime; -import java.util.*; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import static it.pagopa.selfcare.onboarding.common.InstitutionPaSubunitType.*; -import static it.pagopa.selfcare.onboarding.common.ProductId.PROD_INTEROP; -import static it.pagopa.selfcare.onboarding.common.ProductId.PROD_PAGOPA; -import static it.pagopa.selfcare.onboarding.constants.CustomError.*; -import static it.pagopa.selfcare.onboarding.util.ErrorMessage.*; -import static it.pagopa.selfcare.product.utils.ProductUtils.validRoles; - @ApplicationScoped public class OnboardingServiceDefault implements OnboardingService { - private static final Logger LOG = Logger.getLogger(OnboardingServiceDefault.class); - protected static final String ATLEAST_ONE_PRODUCT_ROLE_REQUIRED = "At least one Product role related to %s Party role is required"; - protected static final String MORE_THAN_ONE_PRODUCT_ROLE_AVAILABLE = "More than one Product role related to %s Party role is available. Cannot automatically set the Product role"; - private static final String ONBOARDING_NOT_ALLOWED_ERROR_MESSAGE_TEMPLATE = "Institution with external id '%s' is not allowed to onboard '%s' product"; - private static final String ONBOARDING_NOT_FOUND_OR_ALREADY_DELETED = "Onboarding with id %s not found or already deleted"; - private static final String GSP_CATEGORY_INSTITUTION_TYPE = "L37"; - public static final String UNABLE_TO_COMPLETE_THE_ONBOARDING_FOR_INSTITUTION_FOR_PRODUCT_DISMISSED = "Unable to complete the onboarding for institution with taxCode '%s' to product '%s', the product is dismissed."; - public static final String USERS_FIELD_LIST = "fiscalCode,familyName,name,workContacts"; - 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 - UserApi userRegistryApi; - - @RestClient - @Inject - OnboardingApi onboardingApi; - - @RestClient - @Inject - org.openapi.quarkus.party_registry_proxy_json.api.InstitutionApi institutionRegistryProxyApi; - - @RestClient - @Inject - AooApi aooApi; - - @RestClient - @Inject - InstitutionApi institutionApi; - - @RestClient - @Inject - UoApi uoApi; - - @RestClient - @Inject - OrchestrationApi orchestrationApi; - - @RestClient - @Inject - InfocamereApi infocamereApi; - - @RestClient - @Inject - NationalRegistriesApi nationalRegistriesApi; - - @RestClient - @Inject - org.openapi.quarkus.user_json.api.InstitutionApi userInstitutionApi; - - @Inject - OnboardingMapper onboardingMapper; - - @Inject - InstitutionMapper institutionMapper; - - @Inject - OnboardingValidationStrategy onboardingValidationStrategy; - @Inject - ProductService productService; - @Inject - SignatureService signatureService; - @Inject - AzureBlobClient azureBlobClient; - @Inject - UserMapper userMapper; - @Inject - OnboardingUtils onboardingUtils; - - @ConfigProperty(name = "onboarding.expiring-date") - Integer onboardingExpireDate; - @ConfigProperty(name = "onboarding.orchestration.enabled") - Boolean onboardingOrchestrationEnabled; - @ConfigProperty(name = "onboarding-ms.signature.verify-enabled") - Boolean isVerifyEnabled; - @ConfigProperty(name = "onboarding-ms.blob-storage.path-contracts") - String pathContracts; - - @Override - public Uni onboarding(Onboarding onboarding, List userRequests, List aggregates) { - onboarding.setExpiringDate(OffsetDateTime.now().plusDays(onboardingExpireDate).toLocalDateTime()); - onboarding.setWorkflowType(getWorkflowType(onboarding)); - onboarding.setStatus(OnboardingStatus.REQUEST); - - return fillUsersAndOnboarding(onboarding, userRequests, aggregates, null, false); - } - - - @Override - public Uni onboardingIncrement(Onboarding onboarding, List userRequests, List aggregates) { - onboarding.setExpiringDate(OffsetDateTime.now().plusDays(onboardingExpireDate).toLocalDateTime()); - onboarding.setWorkflowType(WorkflowType.INCREMENT_REGISTRATION_AGGREGATOR); - onboarding.setStatus(OnboardingStatus.PENDING); - - return fillUsersAndOnboarding(onboarding, userRequests, aggregates, null, true); - } - - - /** - * As onboarding but it is specific for USERS workflow - */ - @Override - public Uni onboardingUsers(OnboardingUserRequest request, String userId) { - return getInstitutionFromUserRequest(request) - .onItem().transform(response -> institutionMapper.toEntity(response)) - .onItem().transform(institution -> { - Onboarding onboarding = onboardingMapper.toEntity(request, userId); - institution.setInstitutionType(request.getInstitutionType()); - onboarding.setInstitution(institution); - onboarding.setExpiringDate(OffsetDateTime.now().plusDays(onboardingExpireDate).toLocalDateTime()); + private static final Logger LOG = Logger.getLogger(OnboardingServiceDefault.class); + protected static final String ATLEAST_ONE_PRODUCT_ROLE_REQUIRED = + "At least one Product role related to %s Party role is required"; + protected static final String MORE_THAN_ONE_PRODUCT_ROLE_AVAILABLE = + "More than one Product role related to %s Party role is available. Cannot automatically set the Product role"; + private static final String ONBOARDING_NOT_ALLOWED_ERROR_MESSAGE_TEMPLATE = + "Institution with external id '%s' is not allowed to onboard '%s' product"; + private static final String ONBOARDING_NOT_FOUND_OR_ALREADY_DELETED = + "Onboarding with id %s not found or already deleted"; + private static final String GSP_CATEGORY_INSTITUTION_TYPE = "L37"; + public static final String + UNABLE_TO_COMPLETE_THE_ONBOARDING_FOR_INSTITUTION_FOR_PRODUCT_DISMISSED = + "Unable to complete the onboarding for institution with taxCode '%s' to product '%s', the product is dismissed."; + public static final String USERS_FIELD_LIST = "fiscalCode,familyName,name,workContacts"; + 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 UserApi userRegistryApi; + + @RestClient @Inject OnboardingApi onboardingApi; + + @RestClient @Inject + org.openapi.quarkus.party_registry_proxy_json.api.InstitutionApi institutionRegistryProxyApi; + + @RestClient @Inject AooApi aooApi; + + @RestClient @Inject InstitutionApi institutionApi; + + @RestClient @Inject UoApi uoApi; + + @RestClient @Inject OrchestrationApi orchestrationApi; + + @RestClient @Inject InfocamereApi infocamereApi; + + @RestClient @Inject NationalRegistriesApi nationalRegistriesApi; + + @RestClient @Inject org.openapi.quarkus.user_json.api.InstitutionApi userInstitutionApi; + + @Inject OnboardingMapper onboardingMapper; + + @Inject InstitutionMapper institutionMapper; + + @Inject OnboardingValidationStrategy onboardingValidationStrategy; + @Inject ProductService productService; + @Inject SignatureService signatureService; + @Inject AzureBlobClient azureBlobClient; + @Inject UserMapper userMapper; + @Inject OnboardingUtils onboardingUtils; + + @ConfigProperty(name = "onboarding.expiring-date") + Integer onboardingExpireDate; + + @ConfigProperty(name = "onboarding.orchestration.enabled") + Boolean onboardingOrchestrationEnabled; + + @ConfigProperty(name = "onboarding-ms.signature.verify-enabled") + Boolean isVerifyEnabled; + + @ConfigProperty(name = "onboarding-ms.blob-storage.path-contracts") + String pathContracts; + + @Override + public Uni onboarding( + Onboarding onboarding, + List userRequests, + List aggregates) { + onboarding.setExpiringDate( + OffsetDateTime.now().plusDays(onboardingExpireDate).toLocalDateTime()); + onboarding.setWorkflowType(getWorkflowType(onboarding)); + onboarding.setStatus(OnboardingStatus.REQUEST); + + return fillUsersAndOnboarding(onboarding, userRequests, aggregates, null, false); + } + + @Override + public Uni onboardingIncrement( + Onboarding onboarding, + List userRequests, + List aggregates) { + onboarding.setExpiringDate( + OffsetDateTime.now().plusDays(onboardingExpireDate).toLocalDateTime()); + onboarding.setWorkflowType(WorkflowType.INCREMENT_REGISTRATION_AGGREGATOR); + onboarding.setStatus(OnboardingStatus.PENDING); + + return fillUsersAndOnboarding(onboarding, userRequests, aggregates, null, true); + } + + /** As onboarding but it is specific for USERS workflow */ + @Override + public Uni onboardingUsers(OnboardingUserRequest request, String userId) { + return getInstitutionFromUserRequest(request) + .onItem() + .transform(response -> institutionMapper.toEntity(response)) + .onItem() + .transform( + institution -> { + Onboarding onboarding = onboardingMapper.toEntity(request, userId); + institution.setInstitutionType(request.getInstitutionType()); + onboarding.setInstitution(institution); + onboarding.setExpiringDate( + OffsetDateTime.now().plusDays(onboardingExpireDate).toLocalDateTime()); + return onboarding; + }) + .onItem() + .transformToUni(onboarding -> fillUsers(onboarding, request.getUsers(), null)); + } + + /** + * As above but it is specific for CONFIRMATION workflow where onboarding goes directly to persist + * phase It is created with PENDING state and wait for completion of the orchestration of + * persisting onboarding 'apiStartAndWaitOnboardingOrchestrationGet' + */ + @Override + public Uni onboardingCompletion( + Onboarding onboarding, List userRequests) { + onboarding.setWorkflowType(WorkflowType.CONFIRMATION); + onboarding.setStatus(OnboardingStatus.PENDING); + + return fillUsersAndOnboarding( + onboarding, userRequests, null, TIMEOUT_ORCHESTRATION_RESPONSE, false); + } + + @Override + public Uni onboardingAggregationCompletion( + Onboarding onboarding, + List userRequests, + List aggregates) { + onboarding.setWorkflowType(WorkflowType.CONTRACT_REGISTRATION_AGGREGATOR); + onboarding.setStatus(OnboardingStatus.PENDING); + + return fillUsersAndOnboarding(onboarding, userRequests, aggregates, null, false); + } + + /** As onboarding but it is specific for IMPORT workflow */ + @Override + public Uni onboardingImport( + Onboarding onboarding, + List userRequests, + OnboardingImportContract contractImported) { + onboarding.setWorkflowType(WorkflowType.IMPORT); + onboarding.setStatus(OnboardingStatus.PENDING); + return fillUsersAndOnboardingForImport( + onboarding, userRequests, contractImported, TIMEOUT_ORCHESTRATION_RESPONSE); + } + + /** + * @param timeout The orchestration instances will try complete within the defined timeout and the + * response is delivered synchronously. If is null the timeout is default 1 sec and the + * response is delivered asynchronously + */ + private Uni fillUsersAndOnboarding( + Onboarding onboarding, + List userRequests, + List aggregates, + String timeout, + boolean isAggregatesIncrement) { + onboarding.setCreatedAt(LocalDateTime.now()); + + return getProductByOnboarding(onboarding) + .onItem() + .transformToUni( + product -> + verifyAlreadyOnboarding( + onboarding.getInstitution(), + product.getId(), + product.getParentId(), + isAggregatesIncrement) + .replaceWith(product)) + .onItem() + .transformToUni( + product -> + getRegistryResource(onboarding) + .onItem() + .transformToUni( + proxyResource -> + onboardingUtils + .customValidationOnboardingData(onboarding, product, proxyResource) + .onItem() + .transformToUni( + ignored -> + setIstatCode(onboarding, proxyResource) + .onItem() + .transformToUni( + innerOnboarding -> + addParentDescriptionForAooOrUo( + onboarding, proxyResource)))) + /* if institution type is PRV or SCP, request should match data from registry proxy */ + .onItem() + .transformToUni(ignored -> onboardingUtils.validateFields(onboarding)) + /* if product has some test environments, request must also onboard them (for ex. prod-interop-coll) */ + .onItem() + .invoke(() -> onboarding.setTestEnvProductIds(product.getTestEnvProductIds())) + .onItem() + .invoke(() -> onboarding.setTestEnvProductIds(product.getTestEnvProductIds())) + .onItem() + .transformToUni( + current -> persistOnboarding(onboarding, userRequests, product, aggregates)) + /* Update onboarding data with users and start orchestration */ + .onItem() + .transformToUni( + currentOnboarding -> + persistAndStartOrchestrationOnboarding( + currentOnboarding, + orchestrationApi.apiStartOnboardingOrchestrationGet( + currentOnboarding.getId(), timeout))) + .onItem() + .transform(onboardingMapper::toResponse)); + } + + /** + * This method checks whether the product and any parent have already been onboarded for the + * provided institution. In the case where we are in the aggregate increment flow, the product on + * the aggregator entity must already be onboarded, so in the case of a ResourceConflictException, + * the exception should not be propagated. In the case of standard onboarding, the exception + * should be propagated, and the flow should be blocked. + */ + private Uni verifyAlreadyOnboarding( + Institution institution, String productId, String parentId, boolean isAggregatesIncrement) { + if (isAggregatesIncrement) { + return verifyAlreadyOnboardingForProductAndProductParent(institution, productId, parentId) + .onFailure(ResourceConflictException.class) + .recoverWithNull() + .replaceWithVoid(); + } + return verifyAlreadyOnboardingForProductAndProductParent(institution, productId, parentId) + .replaceWithVoid(); + } + + /** + * @param timeout The orchestration instances will try complete within the defined timeout and the + * response is delivered synchronously. If is null the timeout is default 1 sec and the + * response is delivered asynchronously + */ + private Uni fillUsers( + Onboarding onboarding, List userRequests, String timeout) { + onboarding.setCreatedAt(LocalDateTime.now()); + + return getProductByOnboarding(onboarding) + .onItem() + .transformToUni( + product -> + this.addReferencedOnboardingId(onboarding) + /* if product has some test environments, request must also onboard them (for ex. prod-interop-coll) */ + .onItem() + .invoke( + current -> onboarding.setTestEnvProductIds(product.getTestEnvProductIds())) + .onItem() + .transformToUni( + current -> persistOnboarding(onboarding, userRequests, product, null)) + /* Update onboarding data with users and start orchestration */ + .onItem() + .transformToUni( + currentOnboarding -> + persistAndStartOrchestrationOnboarding( + currentOnboarding, + orchestrationApi.apiStartOnboardingOrchestrationGet( + currentOnboarding.getId(), timeout))) + .onItem() + .transform(onboardingMapper::toResponse)); + } + + /** + * @param timeout The orchestration instances will try complete within the defined timeout and the + * response is delivered synchronously. If is null the timeout is default 1 sec and the + * response is delivered asynchronously + */ + private Uni fillUsersAndOnboardingForImport( + Onboarding onboarding, + List userRequests, + OnboardingImportContract contractImported, + String timeout) { + onboarding.setCreatedAt(LocalDateTime.now()); + + return getProductByOnboarding(onboarding) + .onItem() + .transformToUni( + product -> + verifyAlreadyOnboardingForProductAndProductParent( + onboarding.getInstitution(), product.getId(), product.getParentId()) + .replaceWith(product)) + .onItem() + .transformToUni( + product -> + getRegistryResource(onboarding) + .onItem() + .transformToUni( + proxyResource -> + onboardingUtils + .customValidationOnboardingData(onboarding, product, proxyResource) + .onItem() + .transformToUni( + ignored -> + setIstatCode(onboarding, proxyResource) + .onItem() + .transformToUni( + innerOnboarding -> + addParentDescriptionForAooOrUo( + onboarding, proxyResource)))) + /* if product has some test environments, request must also onboard them (for ex. prod-interop-coll) */ + .onItem() + .invoke(() -> onboarding.setTestEnvProductIds(product.getTestEnvProductIds())) + .onItem() + .transformToUni(this::setInstitutionTypeAndBillingData) + .onItem() + .transformToUni( + current -> persistOnboarding(onboarding, userRequests, product, null)) + .onItem() + .call( + onboardingPersisted -> + Panache.withTransaction( + () -> + Token.persist( + getToken(onboardingPersisted, product, contractImported)))) + /* Update onboarding data with users and start orchestration */ + .onItem() + .transformToUni( + currentOnboarding -> + persistAndStartOrchestrationOnboarding( + currentOnboarding, + orchestrationApi.apiStartOnboardingOrchestrationGet( + currentOnboarding.getId(), timeout))) + .onItem() + .transform(onboardingMapper::toResponse)); + } + + private Uni getRegistryResource(Onboarding onboarding) { + return switch ((onboarding.getInstitution().getSubunitType() != null) + ? onboarding.getInstitution().getSubunitType() + : EC) { + case AOO -> getAOO(onboarding); + case UO -> getUO(onboarding); + default -> getEC(); + }; + } + + private Uni persistOnboarding( + Onboarding onboarding, + List userRequests, + Product product, + List aggregates) { + + Log.infof( + "persist onboarding for: product %s, product parent %s", + product.getId(), product.getParentId()); + + Map roleMappings = + Objects.nonNull(product.getParent()) + ? product + .getParent() + .getRoleMappings(onboarding.getInstitution().getInstitutionType().name()) + : product.getRoleMappings(onboarding.getInstitution().getInstitutionType().name()); + + /* I have to retrieve onboarding id for saving reference to pdv */ + return Panache.withTransaction( + () -> + Onboarding.persist(onboarding) + .replaceWith(onboarding) + .onItem() + .transformToUni( + onboardingPersisted -> + validationRole( + userRequests, + validRoles( + product, + PHASE_ADDITION_ALLOWED.ONBOARDING, + onboarding.getInstitution().getInstitutionType())) + .onItem() + .transformToUni( + ignore -> + validateUserAggregatesRoles( + aggregates, + validRoles( + product, + PHASE_ADDITION_ALLOWED.ONBOARDING, + onboarding.getInstitution().getInstitutionType()))) + .onItem() + .transformToUni( + ignore -> + retrieveAndSetUserAggregatesResources( + onboardingPersisted, product, aggregates)) + .onItem() + .transformToUni( + ignore -> retrieveUserResources(userRequests, roleMappings)) + .onItem() + .invoke(onboardingPersisted::setUsers) + .replaceWith(onboardingPersisted))); + } + + private Uni addParentDescriptionForAooOrUo( + Onboarding onboarding, OnboardingUtils.ProxyResource proxyResource) { + + Log.infof( + "Adding parent description AOO/UOO for: taxCode %s, subunitCode %s, type %s", + onboarding.getInstitution().getTaxCode(), + onboarding.getInstitution().getSubunitCode(), + onboarding.getInstitution().getInstitutionType()); + + if (InstitutionType.PA == onboarding.getInstitution().getInstitutionType()) { + if (AOO == proxyResource.getType()) { + AOOResource resource = (AOOResource) proxyResource.getResource(); + return addParentDescriptionForAOO(onboarding, resource); + } else if (UO == proxyResource.getType()) { + UOResource resource = (UOResource) proxyResource.getResource(); + return addParentDescriptionForUO(onboarding, resource); + } + } + return Uni.createFrom().item(onboarding); + } + + private Uni setIstatCode( + Onboarding onboarding, OnboardingUtils.ProxyResource proxyResource) { + return switch (proxyResource.getType()) { + case AOO -> + Uni.createFrom() + .item( + () -> { + AOOResource resource = (AOOResource) proxyResource.getResource(); + onboarding.getInstitution().setIstatCode(resource.getCodiceComuneISTAT()); return onboarding; - }) - .onItem().transformToUni(onboarding -> fillUsers(onboarding, request.getUsers(), null)); - } - - /** - * As above but it is specific for CONFIRMATION workflow where onboarding goes directly to persist phase - * It is created with PENDING state and wait for completion of the orchestration of persisting onboarding 'apiStartAndWaitOnboardingOrchestrationGet' - */ - @Override - public Uni onboardingCompletion(Onboarding onboarding, List userRequests) { - onboarding.setWorkflowType(WorkflowType.CONFIRMATION); - onboarding.setStatus(OnboardingStatus.PENDING); - - return fillUsersAndOnboarding(onboarding, userRequests, null, TIMEOUT_ORCHESTRATION_RESPONSE, false); - } - - @Override - public Uni onboardingAggregationCompletion(Onboarding onboarding, List userRequests, List aggregates) { - onboarding.setWorkflowType(WorkflowType.CONTRACT_REGISTRATION_AGGREGATOR); - onboarding.setStatus(OnboardingStatus.PENDING); - - return fillUsersAndOnboarding(onboarding, userRequests, aggregates, null, false); - } - - /** - * As onboarding but it is specific for IMPORT workflow - */ - @Override - public Uni onboardingImport(Onboarding onboarding, List userRequests, OnboardingImportContract contractImported) { - onboarding.setWorkflowType(WorkflowType.IMPORT); - onboarding.setStatus(OnboardingStatus.PENDING); - return fillUsersAndOnboardingForImport(onboarding, userRequests, contractImported, TIMEOUT_ORCHESTRATION_RESPONSE); - } - - /** - * @param timeout The orchestration instances will try complete within the defined timeout and the response is delivered synchronously. - * If is null the timeout is default 1 sec and the response is delivered asynchronously - */ - private Uni fillUsersAndOnboarding(Onboarding onboarding, List userRequests, List aggregates, String timeout, boolean isAggregatesIncrement) { - onboarding.setCreatedAt(LocalDateTime.now()); - - return getProductByOnboarding(onboarding) - .onItem().transformToUni(product -> verifyAlreadyOnboarding(onboarding.getInstitution(), product.getId(), product.getParentId(), isAggregatesIncrement) - .replaceWith(product)) - .onItem().transformToUni(product -> getRegistryResource(onboarding) - .onItem().transformToUni(proxyResource -> onboardingUtils.customValidationOnboardingData(onboarding, product, proxyResource) - .onItem().transformToUni(ignored -> setIstatCode(onboarding, proxyResource) - .onItem().transformToUni(innerOnboarding -> addParentDescriptionForAooOrUo(onboarding, proxyResource)))) - /* if institution type is PRV or SCP, request should match data from registry proxy */ - .onItem().transformToUni(ignored -> onboardingUtils.validateFields(onboarding)) - /* if product has some test environments, request must also onboard them (for ex. prod-interop-coll) */ - .onItem().invoke(() -> onboarding.setTestEnvProductIds(product.getTestEnvProductIds())).onItem().invoke(() -> onboarding.setTestEnvProductIds(product.getTestEnvProductIds())) - .onItem().transformToUni(current -> persistOnboarding(onboarding, userRequests, product, aggregates)) - /* Update onboarding data with users and start orchestration */ - .onItem().transformToUni(currentOnboarding -> persistAndStartOrchestrationOnboarding(currentOnboarding, - orchestrationApi.apiStartOnboardingOrchestrationGet(currentOnboarding.getId(), timeout))) - .onItem().transform(onboardingMapper::toResponse)); - } - - /** - * This method checks whether the product and any parent have already been onboarded for the provided institution. - * In the case where we are in the aggregate increment flow, the product on the aggregator entity must already be onboarded, - * so in the case of a ResourceConflictException, the exception should not be propagated. - * In the case of standard onboarding, the exception should be propagated, and the flow should be blocked. - */ - private Uni verifyAlreadyOnboarding(Institution institution, String productId, String parentId, boolean isAggregatesIncrement) { - if (isAggregatesIncrement) { - return verifyAlreadyOnboardingForProductAndProductParent(institution, productId, parentId) - .onFailure(ResourceConflictException.class).recoverWithNull().replaceWithVoid(); - } - return verifyAlreadyOnboardingForProductAndProductParent(institution, productId, parentId).replaceWithVoid(); - } - - /** - * @param timeout The orchestration instances will try complete within the defined timeout and the response is delivered synchronously. - * If is null the timeout is default 1 sec and the response is delivered asynchronously - */ - private Uni fillUsers(Onboarding onboarding, List userRequests, String timeout) { - onboarding.setCreatedAt(LocalDateTime.now()); - - return getProductByOnboarding(onboarding) - .onItem().transformToUni(product -> this.addReferencedOnboardingId(onboarding) - /* if product has some test environments, request must also onboard them (for ex. prod-interop-coll) */ - .onItem().invoke(current -> onboarding.setTestEnvProductIds(product.getTestEnvProductIds())) - .onItem().transformToUni(current -> persistOnboarding(onboarding, userRequests, product, null)) - /* Update onboarding data with users and start orchestration */ - .onItem().transformToUni(currentOnboarding -> persistAndStartOrchestrationOnboarding(currentOnboarding, - orchestrationApi.apiStartOnboardingOrchestrationGet(currentOnboarding.getId(), timeout))) - .onItem().transform(onboardingMapper::toResponse)); - } - - /** - * @param timeout The orchestration instances will try complete within the defined timeout and the response is delivered synchronously. - * If is null the timeout is default 1 sec and the response is delivered asynchronously - */ - private Uni fillUsersAndOnboardingForImport(Onboarding onboarding, List userRequests, OnboardingImportContract contractImported, String timeout) { - onboarding.setCreatedAt(LocalDateTime.now()); - - return getProductByOnboarding(onboarding) - .onItem().transformToUni(product -> verifyAlreadyOnboardingForProductAndProductParent(onboarding.getInstitution(), product.getId(), product.getParentId()) - .replaceWith(product)) - .onItem().transformToUni(product -> getRegistryResource(onboarding) - .onItem().transformToUni(proxyResource -> onboardingUtils.customValidationOnboardingData(onboarding, product, proxyResource) - .onItem().transformToUni(ignored -> setIstatCode(onboarding, proxyResource) - .onItem().transformToUni(innerOnboarding -> addParentDescriptionForAooOrUo(onboarding, proxyResource)))) - /* if product has some test environments, request must also onboard them (for ex. prod-interop-coll) */ - .onItem().invoke(() -> onboarding.setTestEnvProductIds(product.getTestEnvProductIds())) - .onItem().transformToUni(this::setInstitutionTypeAndBillingData) - .onItem().transformToUni(current -> persistOnboarding(onboarding, userRequests, product, null)) - .onItem().call(onboardingPersisted -> Panache.withTransaction(() -> Token.persist(getToken(onboardingPersisted, product, contractImported)))) - /* Update onboarding data with users and start orchestration */ - .onItem().transformToUni(currentOnboarding -> persistAndStartOrchestrationOnboarding(currentOnboarding, - orchestrationApi.apiStartOnboardingOrchestrationGet(currentOnboarding.getId(), timeout))) - .onItem().transform(onboardingMapper::toResponse)); - } - - private Uni getRegistryResource(Onboarding onboarding) { - return switch ((onboarding.getInstitution().getSubunitType() != null) ? onboarding.getInstitution().getSubunitType() : EC) { - case AOO -> getAOO(onboarding); - case UO -> getUO(onboarding); - default -> getEC(); - }; - } - - private Uni persistOnboarding(Onboarding onboarding, List userRequests, Product product, List aggregates) { - - Log.infof("persist onboarding for: product %s, product parent %s", product.getId(), product.getParentId()); - - Map roleMappings = Objects.nonNull(product.getParent()) - ? product.getParent().getRoleMappings(onboarding.getInstitution().getInstitutionType().name()) - : product.getRoleMappings(onboarding.getInstitution().getInstitutionType().name()); - - /* I have to retrieve onboarding id for saving reference to pdv */ - return Panache.withTransaction(() -> Onboarding.persist(onboarding).replaceWith(onboarding) - .onItem().transformToUni(onboardingPersisted -> validationRole(userRequests, validRoles(product, PHASE_ADDITION_ALLOWED.ONBOARDING, onboarding.getInstitution().getInstitutionType())) - .onItem().transformToUni(ignore -> validateUserAggregatesRoles(aggregates, validRoles(product, PHASE_ADDITION_ALLOWED.ONBOARDING, onboarding.getInstitution().getInstitutionType()))) - .onItem().transformToUni(ignore -> retrieveAndSetUserAggregatesResources(onboardingPersisted, product, aggregates)) - .onItem().transformToUni(ignore -> retrieveUserResources(userRequests, roleMappings)) - .onItem().invoke(onboardingPersisted::setUsers).replaceWith(onboardingPersisted))); - } - - private Uni addParentDescriptionForAooOrUo(Onboarding onboarding, OnboardingUtils.ProxyResource proxyResource) { - - Log.infof("Adding parent description AOO/UOO for: taxCode %s, subunitCode %s, type %s", - onboarding.getInstitution().getTaxCode(), - onboarding.getInstitution().getSubunitCode(), - onboarding.getInstitution().getInstitutionType()); - - if (InstitutionType.PA == onboarding.getInstitution().getInstitutionType()) { - if (AOO == proxyResource.getType()) { - AOOResource resource = (AOOResource) proxyResource.getResource(); - return addParentDescriptionForAOO(onboarding, resource); - } else if (UO == proxyResource.getType()) { - UOResource resource = (UOResource) proxyResource.getResource(); - return addParentDescriptionForUO(onboarding, resource); - } - } - return Uni.createFrom().item(onboarding); - } - - private Uni setIstatCode(Onboarding onboarding, OnboardingUtils.ProxyResource proxyResource) { - return switch (proxyResource.getType()) { - case AOO -> Uni.createFrom().item(() -> { - AOOResource resource = (AOOResource) proxyResource.getResource(); - onboarding.getInstitution().setIstatCode(resource.getCodiceComuneISTAT()); - return onboarding; + }); + case UO -> + Uni.createFrom() + .item( + () -> { + UOResource resource = (UOResource) proxyResource.getResource(); + onboarding.getInstitution().setIstatCode(resource.getCodiceComuneISTAT()); + return onboarding; + }); + default -> Uni.createFrom().item(onboarding); + }; + } + + private Uni addParentDescriptionForUO(Onboarding onboarding, UOResource uoResource) { + LOG.infof( + "Founded parent %s for UO institution with subunitCode %s", + uoResource.getDenominazioneEnte(), onboarding.getInstitution().getSubunitCode()); + return Uni.createFrom() + .item( + () -> { + onboarding.getInstitution().setParentDescription(uoResource.getDenominazioneEnte()); + return onboarding; }); - case UO -> Uni.createFrom().item(() -> { - UOResource resource = (UOResource) proxyResource.getResource(); - onboarding.getInstitution().setIstatCode(resource.getCodiceComuneISTAT()); - return onboarding; + } + + private Uni addParentDescriptionForAOO( + Onboarding onboarding, AOOResource aooResource) { + LOG.infof( + "Founded parent %s for AOO institution with subunitCode %s", + aooResource.getDenominazioneEnte(), onboarding.getInstitution().getSubunitCode()); + return Uni.createFrom() + .item( + () -> { + onboarding.getInstitution().setParentDescription(aooResource.getDenominazioneEnte()); + return onboarding; }); - default -> Uni.createFrom().item(onboarding); - }; - } - - private Uni addParentDescriptionForUO(Onboarding onboarding, UOResource uoResource) { - LOG.infof("Founded parent %s for UO institution with subunitCode %s", uoResource.getDenominazioneEnte(), onboarding.getInstitution().getSubunitCode()); - return Uni.createFrom().item(() -> { - onboarding.getInstitution().setParentDescription(uoResource.getDenominazioneEnte()); - return onboarding; - }); - } - - private Uni addParentDescriptionForAOO(Onboarding onboarding, AOOResource aooResource) { - LOG.infof("Founded parent %s for AOO institution with subunitCode %s", aooResource.getDenominazioneEnte(), onboarding.getInstitution().getSubunitCode()); - return Uni.createFrom().item(() -> { - onboarding.getInstitution().setParentDescription(aooResource.getDenominazioneEnte()); - return onboarding; - }); - } - - private Uni addReferencedOnboardingId(Onboarding onboarding) { - final String taxCode = onboarding.getInstitution().getTaxCode(); - final String origin = onboarding.getInstitution().getOrigin().name(); - final String originId = onboarding.getInstitution().getOriginId(); - final String productId = onboarding.getProductId(); - final String subunitCode = onboarding.getInstitution().getSubunitCode(); - Multi onboardings = getOnboardingByFilters(taxCode, subunitCode, origin, originId, productId); - Uni current = onboardings.filter(item -> Objects.isNull(item.getReferenceOnboardingId())) - .toUni().onItem().ifNull().failWith(() -> new ResourceNotFoundException(String.format("Onboarding for taxCode %s, origin %s, originId %s, productId %s, subunitCode %s not found", - taxCode, origin, originId, productId, subunitCode))) - .invoke(previousOnboarding -> onboarding.setReferenceOnboardingId(previousOnboarding.getId())); - return current.onItem().transformToUni(ignored -> onboardings.collect().first()).onItem() - .invoke(lastOnboarding -> { - String previousManagerId = lastOnboarding.getUsers().stream() - .filter(user -> user.getRole().equals(PartyRole.MANAGER)) - .map(User::getId).findFirst().orElse(null); - onboarding.setPreviousManagerId(previousManagerId); - }).replaceWith(onboarding); - } - - private Multi getOnboardingByFilters(String taxCode, String subunitCode, String origin, - String originId, String productId) { - final Map queryParameter = QueryUtils.createMapForInstitutionOnboardingsQueryParameter( - taxCode, subunitCode, origin, - originId, OnboardingStatus.COMPLETED, - productId - ); - Document sort = QueryUtils.buildSortDocument(Onboarding.Fields.createdAt.name(), SortEnum.DESC); - Document query = QueryUtils.buildQuery(queryParameter); - return Onboarding.find(query, sort).stream(); - } - - public Uni persistAndStartOrchestrationOnboarding(Onboarding onboarding, Uni orchestration) { - final List onboardings = new ArrayList<>(); - onboardings.add(onboarding); - - Log.infof("Persist onboarding and start orchestration %b for: taxCode %s, subunitCode %s, type %s", - onboardingOrchestrationEnabled, - onboarding.getInstitution().getTaxCode(), - onboarding.getInstitution().getSubunitCode(), - onboarding.getInstitution().getInstitutionType()); - - if (Boolean.TRUE.equals(onboardingOrchestrationEnabled)) { - return Onboarding.persistOrUpdate(onboardings) - .onItem().transformToUni(saved -> orchestration) - .replaceWith(onboarding); - } else { - return Onboarding.persistOrUpdate(onboardings) - .replaceWith(onboarding); - } - } - - /** - * Identify which workflow must be trigger during onboarding process. - * Each workflow consist of different activities such as creating contract or sending appropriate mail. - * For more information look at ... - * - * @param onboarding actual onboarding request - * @return WorkflowType - */ - private WorkflowType getWorkflowType(Onboarding onboarding) { - InstitutionType institutionType = onboarding.getInstitution().getInstitutionType(); - if (InstitutionType.PT.equals(institutionType)) { - return WorkflowType.FOR_APPROVE_PT; - } - - if (Objects.nonNull(onboarding.getIsAggregator()) && onboarding.getIsAggregator().equals(Boolean.TRUE)) { - return WorkflowType.CONTRACT_REGISTRATION_AGGREGATOR; - } - - if (InstitutionType.PA.equals(institutionType) - || isGspAndProdInterop(institutionType, onboarding.getProductId()) - || InstitutionType.SA.equals(institutionType) - || InstitutionType.AS.equals(institutionType) - || (InstitutionType.PRV.equals(institutionType) && - !PROD_PAGOPA.getValue().equals(onboarding.getProductId()))) { - return WorkflowType.CONTRACT_REGISTRATION; - } - - if (InstitutionType.PG.equals(institutionType)) { - return WorkflowType.CONFIRMATION; - } - - return WorkflowType.FOR_APPROVE; - } - - private boolean isGspAndProdInterop(InstitutionType institutionType, String productId) { - return InstitutionType.GSP == institutionType - && productId.equals(PROD_INTEROP.getValue()); - } - - private Uni product(String productId) { - return Uni.createFrom().item(() -> productService.getProductIsValid(productId)) - .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()); - } - - public Uni getProductByOnboarding(Onboarding onboarding) { - - /* retrieve product, if is not valid will throw OnboardingNotAllowedException */ - return product(onboarding.getProductId()) - .onFailure().transform(ex -> new OnboardingNotAllowedException(String.format(UNABLE_TO_COMPLETE_THE_ONBOARDING_FOR_INSTITUTION_FOR_PRODUCT_DISMISSED, + } + + private Uni addReferencedOnboardingId(Onboarding onboarding) { + final String taxCode = onboarding.getInstitution().getTaxCode(); + final String origin = onboarding.getInstitution().getOrigin().name(); + final String originId = onboarding.getInstitution().getOriginId(); + final String productId = onboarding.getProductId(); + final String subunitCode = onboarding.getInstitution().getSubunitCode(); + Multi onboardings = + getOnboardingByFilters(taxCode, subunitCode, origin, originId, productId); + Uni current = + onboardings + .filter(item -> Objects.isNull(item.getReferenceOnboardingId())) + .toUni() + .onItem() + .ifNull() + .failWith( + () -> + new ResourceNotFoundException( + String.format( + "Onboarding for taxCode %s, origin %s, originId %s, productId %s, subunitCode %s not found", + taxCode, origin, originId, productId, subunitCode))) + .invoke( + previousOnboarding -> + onboarding.setReferenceOnboardingId(previousOnboarding.getId())); + return current + .onItem() + .transformToUni(ignored -> onboardings.collect().first()) + .onItem() + .invoke( + lastOnboarding -> { + String previousManagerId = + lastOnboarding.getUsers().stream() + .filter(user -> user.getRole().equals(PartyRole.MANAGER)) + .map(User::getId) + .findFirst() + .orElse(null); + onboarding.setPreviousManagerId(previousManagerId); + }) + .replaceWith(onboarding); + } + + private Multi getOnboardingByFilters( + String taxCode, String subunitCode, String origin, String originId, String productId) { + final Map queryParameter = + QueryUtils.createMapForInstitutionOnboardingsQueryParameter( + taxCode, subunitCode, origin, originId, OnboardingStatus.COMPLETED, productId); + Document sort = QueryUtils.buildSortDocument(Onboarding.Fields.createdAt.name(), SortEnum.DESC); + Document query = QueryUtils.buildQuery(queryParameter); + return Onboarding.find(query, sort).stream(); + } + + public Uni persistAndStartOrchestrationOnboarding( + Onboarding onboarding, Uni orchestration) { + final List onboardings = new ArrayList<>(); + onboardings.add(onboarding); + + Log.infof( + "Persist onboarding and start orchestration %b for: taxCode %s, subunitCode %s, type %s", + onboardingOrchestrationEnabled, + onboarding.getInstitution().getTaxCode(), + onboarding.getInstitution().getSubunitCode(), + onboarding.getInstitution().getInstitutionType()); + + if (Boolean.TRUE.equals(onboardingOrchestrationEnabled)) { + return Onboarding.persistOrUpdate(onboardings) + .onItem() + .transformToUni(saved -> orchestration) + .replaceWith(onboarding); + } else { + return Onboarding.persistOrUpdate(onboardings).replaceWith(onboarding); + } + } + + /** + * Identify which workflow must be trigger during onboarding process. Each workflow consist of + * different activities such as creating contract or sending appropriate mail. For more + * information look at ... + * + * @param onboarding actual onboarding request + * @return WorkflowType + */ + private WorkflowType getWorkflowType(Onboarding onboarding) { + InstitutionType institutionType = onboarding.getInstitution().getInstitutionType(); + if (InstitutionType.PT.equals(institutionType)) { + return WorkflowType.FOR_APPROVE_PT; + } + + if (Objects.nonNull(onboarding.getIsAggregator()) + && onboarding.getIsAggregator().equals(Boolean.TRUE)) { + return WorkflowType.CONTRACT_REGISTRATION_AGGREGATOR; + } + + if (InstitutionType.PA.equals(institutionType) + || isGspAndProdInterop(institutionType, onboarding.getProductId()) + || InstitutionType.SA.equals(institutionType) + || InstitutionType.AS.equals(institutionType) + || (InstitutionType.PRV.equals(institutionType) + && !PROD_PAGOPA.getValue().equals(onboarding.getProductId()))) { + return WorkflowType.CONTRACT_REGISTRATION; + } + + if (InstitutionType.PG.equals(institutionType)) { + return WorkflowType.CONFIRMATION; + } + + return WorkflowType.FOR_APPROVE; + } + + private boolean isGspAndProdInterop(InstitutionType institutionType, String productId) { + return InstitutionType.GSP == institutionType && productId.equals(PROD_INTEROP.getValue()); + } + + private Uni product(String productId) { + return Uni.createFrom() + .item(() -> productService.getProductIsValid(productId)) + .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()); + } + + public Uni getProductByOnboarding(Onboarding onboarding) { + + /* retrieve product, if is not valid will throw OnboardingNotAllowedException */ + return product(onboarding.getProductId()) + .onFailure() + .transform( + ex -> + new OnboardingNotAllowedException( + String.format( + UNABLE_TO_COMPLETE_THE_ONBOARDING_FOR_INSTITUTION_FOR_PRODUCT_DISMISSED, onboarding.getInstitution().getTaxCode(), - onboarding.getProductId()), DEFAULT_ERROR.getCode())); - } - - private Uni verifyAlreadyOnboardingForProductAndProductParent(Institution institution, String productId, String productParentId) { - return (Objects.nonNull(productParentId) - //If product has parent, I must verify if onboarding is present for parent and child - ? checkIfAlreadyOnboardingAndValidateAllowedMap(institution, productParentId) - .onFailure(ResourceConflictException.class) - .recoverWithUni(ignore -> checkIfAlreadyOnboardingAndValidateAllowedMap(institution, productId)) - //If product is a root, I must only verify if onboarding for root - : checkIfAlreadyOnboardingAndValidateAllowedMap(institution, productId) - ); - } - - private Uni verifyOnboardingNotExistForProductAndProductParent(Onboarding onboarding, String productId, String productParentId) { - return (Objects.nonNull(productParentId) - //If product has parent, I must verify if onboarding is present for parent and child - ? checkIfOnboardingNotExistAndValidateAllowedMap(onboarding, productParentId) - .onFailure(ResourceConflictException.class) - .recoverWithUni(ignore -> checkIfOnboardingNotExistAndValidateAllowedMap(onboarding, productId)) - //If product is a root, I must only verify if onboarding for root - : checkIfOnboardingNotExistAndValidateAllowedMap(onboarding, productId) - ); - } - - private Uni validateAllowedMap(String taxCode, String subunitCode, String productId) { - Log.infof("Validating allowed map for: taxCode %s, subunitCode %s, product %s", - taxCode, subunitCode, productId); - - if (!onboardingValidationStrategy.validate(productId, taxCode)) { - return Uni.createFrom().failure(new OnboardingNotAllowedException( - String.format(ONBOARDING_NOT_ALLOWED_ERROR_MESSAGE_TEMPLATE, taxCode, productId), + onboarding.getProductId()), DEFAULT_ERROR.getCode())); - } - - return Uni.createFrom().item(Boolean.TRUE); - } - - private Uni checkIfAlreadyOnboardingAndValidateAllowedMap(Institution institution, String productId) { - return validateAllowedMap(institution.getTaxCode(), institution.getSubunitCode(), productId) - .flatMap(ignored -> { - String origin = institution.getOrigin() != null ? institution.getOrigin().getValue() : null; - return verifyOnboarding(institution.getTaxCode(), institution.getSubunitCode(), origin, institution.getOriginId(), OnboardingStatus.COMPLETED, productId) - .flatMap(onboardingResponses -> onboardingResponses.isEmpty() - ? Uni.createFrom().item(Boolean.TRUE) - : Uni.createFrom().failure(new ResourceConflictException( - String.format(PRODUCT_ALREADY_ONBOARDED.getMessage(), productId, institution.getTaxCode()), - PRODUCT_ALREADY_ONBOARDED.getCode()))); - }); - } - - private Uni checkIfOnboardingNotExistAndValidateAllowedMap(Onboarding onboarding, String productId) { - return validateAllowedMap(onboarding.getInstitution().getTaxCode(), onboarding.getInstitution().getSubunitCode(), productId) - .flatMap(ignored -> { - if (Objects.isNull(onboarding.getReferenceOnboardingId())) { - return Uni.createFrom().failure(new InvalidRequestException(INVALID_REFERENCE_ONBORADING.getMessage(), INVALID_REFERENCE_ONBORADING.getCode())); - } - return Onboarding.findByIdOptional(onboarding.getReferenceOnboardingId()) - .onItem().transformToUni(opt -> opt - .map(Onboarding.class::cast) - .filter(referenceOnboarding -> referenceOnboarding.getStatus().equals(OnboardingStatus.COMPLETED)) - .map(referenceOnboarding -> Uni.createFrom().item(Boolean.TRUE)) - .orElse(Uni.createFrom().failure(new InvalidRequestException( - String.format(PRODUCT_NOT_ONBOARDED.getMessage(), onboarding.getProductId(), onboarding.getInstitution().getTaxCode(), PRODUCT_NOT_ONBOARDED.getCode()))))); - }); - } - - private String retrieveProductRole(UserRequest userInfo, Map roleMappings) { - try { - if (Objects.isNull(roleMappings) || roleMappings.isEmpty()) - throw new IllegalArgumentException("Role mappings is required"); - - if (Objects.isNull(roleMappings.get(userInfo.getRole()))) - throw new IllegalArgumentException(String.format(ATLEAST_ONE_PRODUCT_ROLE_REQUIRED, userInfo.getRole())); - if (Objects.isNull((roleMappings.get(userInfo.getRole()).getRoles()))) - throw new IllegalArgumentException(String.format(ATLEAST_ONE_PRODUCT_ROLE_REQUIRED, userInfo.getRole())); - if (roleMappings.get(userInfo.getRole()).getRoles().size() != 1) - throw new IllegalArgumentException(String.format(MORE_THAN_ONE_PRODUCT_ROLE_AVAILABLE, userInfo.getRole())); - return roleMappings.get(userInfo.getRole()).getRoles().get(0).getCode(); - - } catch (IllegalArgumentException e) { - throw new OnboardingNotAllowedException(e.getMessage(), DEFAULT_ERROR.getCode()); - } - } - - private Uni> validationRole(List users, List validRoles) { - - List usersNotValidRole = users.stream() - .filter(user -> !validRoles.contains(user.getRole())) - .toList(); - if (!usersNotValidRole.isEmpty()) { - String usersNotValidRoleString = usersNotValidRole.stream() - .map(user -> user.getRole().toString()) - .collect(Collectors.joining(",")); - return Uni.createFrom().failure(new InvalidRequestException(String.format(CustomError.ROLES_NOT_ADMITTED_ERROR.getMessage(), usersNotValidRoleString), - CustomError.ROLES_NOT_ADMITTED_ERROR.getCode())); - } - - return Uni.createFrom().item(users); - } - - private Uni validateUserAggregatesRoles(List aggregates, List validRoles) { - LOG.debug("starting validateUserAggregatesRoles"); - if (!CollectionUtils.isEmpty(aggregates)) { - return Multi.createFrom().iterable(aggregates) - .filter(aggregate -> !CollectionUtils.isEmpty(aggregate.getUsers())) - .onItem().invoke(aggregate -> LOG.debugf("Validating role for users of aggregate: %s", aggregate.getTaxCode())) - .onItem().transformToUniAndMerge(aggregate -> validationRole(aggregate.getUsers(), validRoles) - .onFailure().invoke(throwable -> LOG.error("Error during validation role for aggregate: %s", aggregate.getTaxCode(), throwable))) - .collect().asList().replaceWithVoid(); - } - return Uni.createFrom().voidItem(); - } - - private Uni retrieveAndSetUserAggregatesResources(Onboarding onboarding, Product product, List aggregates) { - - Log.infof("Retrieving user resources for aggregates: product %s, product parent %s", product.getId(), product.getParentId()); - - Map roleMappings = Objects.nonNull(product.getParent()) - ? product.getParent().getRoleMappings(onboarding.getInstitution().getInstitutionType().name()) - : product.getRoleMappings(onboarding.getInstitution().getInstitutionType().name()); - - if (!CollectionUtils.isEmpty(aggregates)) { - return Multi.createFrom().iterable(aggregates) - .filter(aggregate -> !CollectionUtils.isEmpty(aggregate.getUsers())) - .onItem().invoke(aggregate -> LOG.debugf("Retrieving user resources for aggregate: %s", aggregate.getTaxCode())) - .onItem().transformToUni(aggregate -> retrieveUserResources(aggregate.getUsers(), roleMappings) - .onFailure().invoke(throwable -> LOG.errorf("Error during retrieving user resources for aggregate: %s", aggregate.getTaxCode(), throwable)) - .onItem().invoke(users -> setUsersInAggregateToPersist(onboarding, aggregate, users))) - .concatenate().onItem().ignoreAsUni(); - } - - return Uni.createFrom().voidItem(); - } - - private static void setUsersInAggregateToPersist(Onboarding onboarding, AggregateInstitutionRequest aggregate, List users) { - onboarding.getAggregates().stream() - .filter(aggregateInstitution -> aggregateInstitution.getTaxCode().equals(aggregate.getTaxCode())) - .findAny() - .ifPresent(aggregateInstitutionRequest -> aggregateInstitutionRequest.setUsers(users)); - } - - private Uni> retrieveUserResources(List users, Map roleMappings) { - - return Multi.createFrom().iterable(users) - .onItem().transformToUni(user -> userRegistryApi - /* search user by tax code */ - .searchUsingPOST(USERS_FIELD_LIST, new UserSearchDto().fiscalCode(user.getTaxCode())) - - /* retrieve userId, if found will eventually update some fields */ - .onItem().transformToUni(userResource -> { - Optional optUserMailRandomUuid = Optional.ofNullable(user.getEmail()).map(mail -> retrieveUserMailUuid(userResource, mail)); - Optional optUserFieldsDto = toUpdateUserRequest(user, userResource, optUserMailRandomUuid); - return optUserFieldsDto - .map(userUpdateRequest -> userRegistryApi.updateUsingPATCH(userResource.getId().toString(), userUpdateRequest) - .replaceWith(userResource.getId())) - .orElse(Uni.createFrom().item(userResource.getId())) - .map(userResourceId -> User.builder() - .id(userResourceId.toString()) - .role(user.getRole()) - .userMailUuid(optUserMailRandomUuid.orElse(null)) - .productRole(retrieveProductRole(user, roleMappings)) - .build()); - } - ) - /* if not found 404, will create new user */ - .onFailure(WebApplicationException.class).recoverWithUni(ex -> { - if (((WebApplicationException) ex).getResponse().getStatus() != 404) { - return Uni.createFrom().failure(ex); - } - - String userMailRandomUuid = ID_MAIL_PREFIX.concat(UUID.randomUUID().toString()); - return userRegistryApi.saveUsingPATCH(createSaveUserDto(user, userMailRandomUuid)) - .onItem().transform(userId -> User.builder() - .id(userId.getId().toString()) - .role(user.getRole()) - .userMailUuid(userMailRandomUuid) - .productRole(retrieveProductRole(user, roleMappings)) - .build()); + } + + private Uni verifyAlreadyOnboardingForProductAndProductParent( + Institution institution, String productId, String productParentId) { + return (Objects.nonNull(productParentId) + // If product has parent, I must verify if onboarding is present for parent and child + ? checkIfAlreadyOnboardingAndValidateAllowedMap(institution, productParentId) + .onFailure(ResourceConflictException.class) + .recoverWithUni( + ignore -> checkIfAlreadyOnboardingAndValidateAllowedMap(institution, productId)) + // If product is a root, I must only verify if onboarding for root + : checkIfAlreadyOnboardingAndValidateAllowedMap(institution, productId)); + } + + private Uni verifyOnboardingNotExistForProductAndProductParent( + Onboarding onboarding, String productId, String productParentId) { + return (Objects.nonNull(productParentId) + // If product has parent, I must verify if onboarding is present for parent and child + ? checkIfOnboardingNotExistAndValidateAllowedMap(onboarding, productParentId) + .onFailure(ResourceConflictException.class) + .recoverWithUni( + ignore -> checkIfOnboardingNotExistAndValidateAllowedMap(onboarding, productId)) + // If product is a root, I must only verify if onboarding for root + : checkIfOnboardingNotExistAndValidateAllowedMap(onboarding, productId)); + } + + private Uni validateAllowedMap(String taxCode, String subunitCode, String productId) { + Log.infof( + "Validating allowed map for: taxCode %s, subunitCode %s, product %s", + taxCode, subunitCode, productId); + + if (!onboardingValidationStrategy.validate(productId, taxCode)) { + return Uni.createFrom() + .failure( + new OnboardingNotAllowedException( + String.format(ONBOARDING_NOT_ALLOWED_ERROR_MESSAGE_TEMPLATE, taxCode, productId), + DEFAULT_ERROR.getCode())); + } + + return Uni.createFrom().item(Boolean.TRUE); + } + + private Uni checkIfAlreadyOnboardingAndValidateAllowedMap( + Institution institution, String productId) { + return validateAllowedMap(institution.getTaxCode(), institution.getSubunitCode(), productId) + .flatMap( + ignored -> { + String origin = + institution.getOrigin() != null ? institution.getOrigin().getValue() : null; + return verifyOnboarding( + institution.getTaxCode(), + institution.getSubunitCode(), + origin, + institution.getOriginId(), + OnboardingStatus.COMPLETED, + productId) + .flatMap( + onboardingResponses -> + onboardingResponses.isEmpty() + ? Uni.createFrom().item(Boolean.TRUE) + : Uni.createFrom() + .failure( + new ResourceConflictException( + String.format( + PRODUCT_ALREADY_ONBOARDED.getMessage(), + productId, + institution.getTaxCode()), + PRODUCT_ALREADY_ONBOARDED.getCode()))); + }); + } + + private Uni checkIfOnboardingNotExistAndValidateAllowedMap( + Onboarding onboarding, String productId) { + return validateAllowedMap( + onboarding.getInstitution().getTaxCode(), + onboarding.getInstitution().getSubunitCode(), + productId) + .flatMap( + ignored -> { + if (Objects.isNull(onboarding.getReferenceOnboardingId())) { + return Uni.createFrom() + .failure( + new InvalidRequestException( + INVALID_REFERENCE_ONBORADING.getMessage(), + INVALID_REFERENCE_ONBORADING.getCode())); + } + return Onboarding.findByIdOptional(onboarding.getReferenceOnboardingId()) + .onItem() + .transformToUni( + opt -> + opt.map(Onboarding.class::cast) + .filter( + referenceOnboarding -> + referenceOnboarding + .getStatus() + .equals(OnboardingStatus.COMPLETED)) + .map(referenceOnboarding -> Uni.createFrom().item(Boolean.TRUE)) + .orElse( + Uni.createFrom() + .failure( + new InvalidRequestException( + String.format( + PRODUCT_NOT_ONBOARDED.getMessage(), + onboarding.getProductId(), + onboarding.getInstitution().getTaxCode(), + PRODUCT_NOT_ONBOARDED.getCode()))))); + }); + } + + private String retrieveProductRole( + UserRequest userInfo, Map roleMappings) { + try { + if (Objects.isNull(roleMappings) || roleMappings.isEmpty()) + throw new IllegalArgumentException("Role mappings is required"); + + if (Objects.isNull(roleMappings.get(userInfo.getRole()))) + throw new IllegalArgumentException( + String.format(ATLEAST_ONE_PRODUCT_ROLE_REQUIRED, userInfo.getRole())); + if (Objects.isNull((roleMappings.get(userInfo.getRole()).getRoles()))) + throw new IllegalArgumentException( + String.format(ATLEAST_ONE_PRODUCT_ROLE_REQUIRED, userInfo.getRole())); + if (roleMappings.get(userInfo.getRole()).getRoles().size() != 1) + throw new IllegalArgumentException( + String.format(MORE_THAN_ONE_PRODUCT_ROLE_AVAILABLE, userInfo.getRole())); + return roleMappings.get(userInfo.getRole()).getRoles().get(0).getCode(); + + } catch (IllegalArgumentException e) { + throw new OnboardingNotAllowedException(e.getMessage(), DEFAULT_ERROR.getCode()); + } + } + + private Uni> validationRole( + List users, List validRoles) { + + List usersNotValidRole = + users.stream().filter(user -> !validRoles.contains(user.getRole())).toList(); + if (!usersNotValidRole.isEmpty()) { + String usersNotValidRoleString = + usersNotValidRole.stream() + .map(user -> user.getRole().toString()) + .collect(Collectors.joining(",")); + return Uni.createFrom() + .failure( + new InvalidRequestException( + String.format( + CustomError.ROLES_NOT_ADMITTED_ERROR.getMessage(), usersNotValidRoleString), + CustomError.ROLES_NOT_ADMITTED_ERROR.getCode())); + } + + return Uni.createFrom().item(users); + } + + private Uni validateUserAggregatesRoles( + List aggregates, List validRoles) { + LOG.debug("starting validateUserAggregatesRoles"); + if (!CollectionUtils.isEmpty(aggregates)) { + return Multi.createFrom() + .iterable(aggregates) + .filter(aggregate -> !CollectionUtils.isEmpty(aggregate.getUsers())) + .onItem() + .invoke( + aggregate -> + LOG.debugf("Validating role for users of aggregate: %s", aggregate.getTaxCode())) + .onItem() + .transformToUniAndMerge( + aggregate -> + validationRole(aggregate.getUsers(), validRoles) + .onFailure() + .invoke( + throwable -> + LOG.error( + "Error during validation role for aggregate: %s", + aggregate.getTaxCode(), throwable))) + .collect() + .asList() + .replaceWithVoid(); + } + return Uni.createFrom().voidItem(); + } + + private Uni retrieveAndSetUserAggregatesResources( + Onboarding onboarding, Product product, List aggregates) { + + Log.infof( + "Retrieving user resources for aggregates: product %s, product parent %s", + product.getId(), product.getParentId()); + + Map roleMappings = + Objects.nonNull(product.getParent()) + ? product + .getParent() + .getRoleMappings(onboarding.getInstitution().getInstitutionType().name()) + : product.getRoleMappings(onboarding.getInstitution().getInstitutionType().name()); + + if (!CollectionUtils.isEmpty(aggregates)) { + return Multi.createFrom() + .iterable(aggregates) + .filter(aggregate -> !CollectionUtils.isEmpty(aggregate.getUsers())) + .onItem() + .invoke( + aggregate -> + LOG.debugf("Retrieving user resources for aggregate: %s", aggregate.getTaxCode())) + .onItem() + .transformToUni( + aggregate -> + retrieveUserResources(aggregate.getUsers(), roleMappings) + .onFailure() + .invoke( + throwable -> + LOG.errorf( + "Error during retrieving user resources for aggregate: %s", + aggregate.getTaxCode(), throwable)) + .onItem() + .invoke(users -> setUsersInAggregateToPersist(onboarding, aggregate, users))) + .concatenate() + .onItem() + .ignoreAsUni(); + } + + return Uni.createFrom().voidItem(); + } + + private static void setUsersInAggregateToPersist( + Onboarding onboarding, AggregateInstitutionRequest aggregate, List users) { + onboarding.getAggregates().stream() + .filter( + aggregateInstitution -> + aggregateInstitution.getTaxCode().equals(aggregate.getTaxCode())) + .findAny() + .ifPresent(aggregateInstitutionRequest -> aggregateInstitutionRequest.setUsers(users)); + } + + private Uni> retrieveUserResources( + List users, Map roleMappings) { + + return Multi.createFrom() + .iterable(users) + .onItem() + .transformToUni( + user -> + userRegistryApi + /* search user by tax code */ + .searchUsingPOST( + USERS_FIELD_LIST, new UserSearchDto().fiscalCode(user.getTaxCode())) + + /* retrieve userId, if found will eventually update some fields */ + .onItem() + .transformToUni( + userResource -> { + Optional optUserMailRandomUuid = + Optional.ofNullable(user.getEmail()) + .map(mail -> retrieveUserMailUuid(userResource, mail)); + Optional optUserFieldsDto = + toUpdateUserRequest(user, userResource, optUserMailRandomUuid); + return optUserFieldsDto + .map( + userUpdateRequest -> + userRegistryApi + .updateUsingPATCH( + userResource.getId().toString(), userUpdateRequest) + .replaceWith(userResource.getId())) + .orElse(Uni.createFrom().item(userResource.getId())) + .map( + userResourceId -> + User.builder() + .id(userResourceId.toString()) + .role(user.getRole()) + .userMailUuid(optUserMailRandomUuid.orElse(null)) + .productRole(retrieveProductRole(user, roleMappings)) + .build()); }) - ) - .concatenate().collect().asList(); - } - - private SaveUserDto createSaveUserDto(UserRequest model, String userMailRandomUuid) { - SaveUserDto resource = new SaveUserDto(); - resource.setFiscalCode(model.getTaxCode()); - resource.setName(new CertifiableFieldResourceOfstring() - .value(model.getName()) - .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); - resource.setFamilyName(new CertifiableFieldResourceOfstring() - .value(model.getSurname()) + /* if not found 404, will create new user */ + .onFailure(WebApplicationException.class) + .recoverWithUni( + ex -> { + if (((WebApplicationException) ex).getResponse().getStatus() != 404) { + return Uni.createFrom().failure(ex); + } + + String userMailRandomUuid = + ID_MAIL_PREFIX.concat(UUID.randomUUID().toString()); + return userRegistryApi + .saveUsingPATCH(createSaveUserDto(user, userMailRandomUuid)) + .onItem() + .transform( + userId -> + User.builder() + .id(userId.getId().toString()) + .role(user.getRole()) + .userMailUuid(userMailRandomUuid) + .productRole(retrieveProductRole(user, roleMappings)) + .build()); + })) + .concatenate() + .collect() + .asList(); + } + + private SaveUserDto createSaveUserDto(UserRequest model, String userMailRandomUuid) { + SaveUserDto resource = new SaveUserDto(); + resource.setFiscalCode(model.getTaxCode()); + resource.setName( + new CertifiableFieldResourceOfstring() + .value(model.getName()) + .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); + resource.setFamilyName( + new CertifiableFieldResourceOfstring() + .value(model.getSurname()) + .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); + + if (Objects.nonNull(userMailRandomUuid)) { + WorkContactResource contact = new WorkContactResource(); + contact.setEmail( + new CertifiableFieldResourceOfstring() + .value(model.getEmail()) + .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); + resource.setWorkContacts(Map.of(userMailRandomUuid, contact)); + } + return resource; + } + + private String retrieveUserMailUuid(UserResource foundUser, String userMail) { + if (Objects.isNull(foundUser.getWorkContacts())) { + return ID_MAIL_PREFIX.concat(UUID.randomUUID().toString()); + } + + return foundUser.getWorkContacts().entrySet().stream() + .filter( + entry -> + Objects.nonNull(entry.getValue()) && Objects.nonNull(entry.getValue().getEmail())) + .filter(entry -> entry.getValue().getEmail().getValue().equals(userMail)) + .findFirst() + .map(Map.Entry::getKey) + .orElse(ID_MAIL_PREFIX.concat(UUID.randomUUID().toString())); + } + + protected static Optional toUpdateUserRequest( + UserRequest user, UserResource foundUser, Optional optUserMailRandomUuid) { + Optional mutableUserFieldsDto = Optional.empty(); + if (isFieldToUpdate(foundUser.getName(), user.getName())) { + MutableUserFieldsDto dto = new MutableUserFieldsDto(); + dto.setName( + new CertifiableFieldResourceOfstring() + .value(user.getName()) + .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); + mutableUserFieldsDto = Optional.of(dto); + } + if (isFieldToUpdate(foundUser.getFamilyName(), user.getSurname())) { + MutableUserFieldsDto dto = mutableUserFieldsDto.orElseGet(MutableUserFieldsDto::new); + dto.setFamilyName( + new CertifiableFieldResourceOfstring() + .value(user.getSurname()) + .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); + mutableUserFieldsDto = Optional.of(dto); + } + + if (optUserMailRandomUuid.isPresent()) { + Optional entryMail = + Objects.nonNull(foundUser.getWorkContacts()) + ? foundUser.getWorkContacts().keySet().stream() + .filter(key -> key.equals(optUserMailRandomUuid.get())) + .findFirst() + : Optional.empty(); + + if (entryMail.isEmpty()) { + MutableUserFieldsDto dto = mutableUserFieldsDto.orElseGet(MutableUserFieldsDto::new); + final WorkContactResource workContact = new WorkContactResource(); + workContact.setEmail( + new CertifiableFieldResourceOfstring() + .value(user.getEmail()) .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); - - if (Objects.nonNull(userMailRandomUuid)) { - WorkContactResource contact = new WorkContactResource(); - contact.setEmail(new CertifiableFieldResourceOfstring() - .value(model.getEmail()) - .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); - resource.setWorkContacts(Map.of(userMailRandomUuid, contact)); - } - return resource; - } - - private String retrieveUserMailUuid(UserResource foundUser, String userMail) { - if (Objects.isNull(foundUser.getWorkContacts())) { - return ID_MAIL_PREFIX.concat(UUID.randomUUID().toString()); - } - - return foundUser.getWorkContacts().entrySet().stream() - .filter(entry -> Objects.nonNull(entry.getValue()) && Objects.nonNull(entry.getValue().getEmail())) - .filter(entry -> entry.getValue().getEmail().getValue().equals(userMail)) - .findFirst() - .map(Map.Entry::getKey) - .orElse(ID_MAIL_PREFIX.concat(UUID.randomUUID().toString())); - } - - protected static Optional toUpdateUserRequest(UserRequest user, UserResource foundUser, Optional optUserMailRandomUuid) { - Optional mutableUserFieldsDto = Optional.empty(); - if (isFieldToUpdate(foundUser.getName(), user.getName())) { - MutableUserFieldsDto dto = new MutableUserFieldsDto(); - dto.setName(new CertifiableFieldResourceOfstring() - .value(user.getName()) - .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); - mutableUserFieldsDto = Optional.of(dto); - } - if (isFieldToUpdate(foundUser.getFamilyName(), user.getSurname())) { - MutableUserFieldsDto dto = mutableUserFieldsDto.orElseGet(MutableUserFieldsDto::new); - dto.setFamilyName(new CertifiableFieldResourceOfstring() - .value(user.getSurname()) - .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); - mutableUserFieldsDto = Optional.of(dto); - } - - if (optUserMailRandomUuid.isPresent()) { - Optional entryMail = Objects.nonNull(foundUser.getWorkContacts()) - ? foundUser.getWorkContacts().keySet().stream() - .filter(key -> key.equals(optUserMailRandomUuid.get())) - .findFirst() - : Optional.empty(); - - if (entryMail.isEmpty()) { - MutableUserFieldsDto dto = mutableUserFieldsDto.orElseGet(MutableUserFieldsDto::new); - final WorkContactResource workContact = new WorkContactResource(); - workContact.setEmail(new CertifiableFieldResourceOfstring() - .value(user.getEmail()) - .certification(CertifiableFieldResourceOfstring.CertificationEnum.NONE)); - dto.setWorkContacts(Map.of(optUserMailRandomUuid.get(), workContact)); - mutableUserFieldsDto = Optional.of(dto); - } - } - return mutableUserFieldsDto; - } - - private static boolean isFieldToUpdate(CertifiableFieldResourceOfstring certifiedField, String value) { - boolean isToUpdate = true; - if (certifiedField != null) { - boolean isNoneCertification = CertifiableFieldResourceOfstring.CertificationEnum.NONE.equals(certifiedField.getCertification()); - boolean isSameValue = isNoneCertification ? certifiedField.getValue().equals(value) : certifiedField.getValue().equalsIgnoreCase(value); - - if (isSameValue) { - isToUpdate = false; - } else if (!isNoneCertification) { - throw new InvalidRequestException(USERS_UPDATE_NOT_ALLOWED.getMessage(), USERS_UPDATE_NOT_ALLOWED.getCode()); - } - } - return isToUpdate; - } - - @Override - public Uni approve(String onboardingId) { - - return retrieveOnboardingAndCheckIfExpired(onboardingId) - .onItem().transformToUni(this::checkIfToBeValidated) - //Fail if onboarding exists for a product - .onItem().transformToUni(onboarding -> product(onboarding.getProductId()) - .onItem().transformToUni(product -> verifyAlreadyOnboardingForProductAndProductParent(onboarding.getInstitution(), + dto.setWorkContacts(Map.of(optUserMailRandomUuid.get(), workContact)); + mutableUserFieldsDto = Optional.of(dto); + } + } + return mutableUserFieldsDto; + } + + private static boolean isFieldToUpdate( + CertifiableFieldResourceOfstring certifiedField, String value) { + boolean isToUpdate = true; + if (certifiedField != null) { + boolean isNoneCertification = + CertifiableFieldResourceOfstring.CertificationEnum.NONE.equals( + certifiedField.getCertification()); + boolean isSameValue = + isNoneCertification + ? certifiedField.getValue().equals(value) + : certifiedField.getValue().equalsIgnoreCase(value); + + if (isSameValue) { + isToUpdate = false; + } else if (!isNoneCertification) { + throw new InvalidRequestException( + USERS_UPDATE_NOT_ALLOWED.getMessage(), USERS_UPDATE_NOT_ALLOWED.getCode()); + } + } + return isToUpdate; + } + + @Override + public Uni approve(String onboardingId) { + + return retrieveOnboardingAndCheckIfExpired(onboardingId) + .onItem() + .transformToUni(this::checkIfToBeValidated) + // Fail if onboarding exists for a product + .onItem() + .transformToUni( + onboarding -> + product(onboarding.getProductId()) + .onItem() + .transformToUni( + product -> + verifyAlreadyOnboardingForProductAndProductParent( + onboarding.getInstitution(), product.getId(), product.getParentId())) - .replaceWith(onboarding) - ) - .onItem().transformToUni(onboarding -> onboardingOrchestrationEnabled - ? orchestrationApi.apiStartOnboardingOrchestrationGet(onboardingId, null) + .replaceWith(onboarding)) + .onItem() + .transformToUni( + onboarding -> + onboardingOrchestrationEnabled + ? orchestrationApi + .apiStartOnboardingOrchestrationGet(onboardingId, null) .map(ignore -> onboarding) - : Uni.createFrom().item(onboarding)) - .map(onboardingMapper::toGetResponse); - } - - @Override - public Uni complete(String onboardingId, FormItem formItem) { - - if (Boolean.TRUE.equals(isVerifyEnabled)) { - //Retrieve as Tuple: managers fiscal-code from user registry and contract digest - //At least, verify contract signature using both - Function> verification = onboarding -> Uni.combine().all() - .unis(retrieveOnboardingUserFiscalCodeList(onboarding), retrieveContractDigest(onboardingId)) - .asTuple() - .onItem().transformToUni(inputSignatureVerification -> - Uni.createFrom().item(() -> { - signatureService.verifySignature(formItem.getFile(), - inputSignatureVerification.getItem2(), - inputSignatureVerification.getItem1()); - return onboarding; - }) - .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - ); - return complete(onboardingId, formItem, verification); - } else { - return completeWithoutSignatureVerification(onboardingId, formItem); - } - } - - @Override - public Uni completeOnboardingUsers(String onboardingId, FormItem formItem) { - if (Boolean.TRUE.equals(isVerifyEnabled)) { - //Retrieve as Tuple: managers fiscal-code from user registry and contract digest - //At least, verify contract signature using both - Function> verification = onboarding -> Uni.combine().all() - .unis(retrieveOnboardingUserFiscalCodeList(onboarding), retrieveContractDigest(onboardingId)) - .asTuple() - .onItem().transformToUni(inputSignatureVerification -> - Uni.createFrom().item(() -> { - signatureService.verifySignature(formItem.getFile(), - inputSignatureVerification.getItem2(), - inputSignatureVerification.getItem1()); - return onboarding; - }) - .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - ); - - return completeOnboardingUsers(onboardingId, formItem, verification); - } else { - return completeOnboardingUsersWithoutSignatureVerification(onboardingId, formItem); - } - } - - public Uni completeOnboardingUsersWithoutSignatureVerification(String onboardingId, FormItem formItem) { - Function> verification = ignored -> Uni.createFrom().item(ignored); - return completeOnboardingUsers(onboardingId, formItem, verification); - } - - @Override - public Uni completeWithoutSignatureVerification(String onboardingId, FormItem formItem) { - Function> verification = ignored -> Uni.createFrom().item(ignored); - return complete(onboardingId, formItem, verification); - } - - private Uni complete(String onboardingId, FormItem formItem, Function> verificationContractSignature) { - - return retrieveOnboardingAndCheckIfExpired(onboardingId) - .onItem().transformToUni(verificationContractSignature::apply) - //Fail if onboarding exists for a product - .onItem().transformToUni(onboarding -> product(onboarding.getProductId()) - .onItem().transformToUni(product -> verifyAlreadyOnboardingForProductAndProductParent(onboarding.getInstitution(), + : Uni.createFrom().item(onboarding)) + .map(onboardingMapper::toGetResponse); + } + + @Override + public Uni complete(String onboardingId, FormItem formItem) { + + if (Boolean.TRUE.equals(isVerifyEnabled)) { + // Retrieve as Tuple: managers fiscal-code from user registry and contract digest + // At least, verify contract signature using both + Function> verification = + onboarding -> + Uni.combine() + .all() + .unis( + retrieveOnboardingUserFiscalCodeList(onboarding), + retrieveContractDigest(onboardingId)) + .asTuple() + .onItem() + .transformToUni( + inputSignatureVerification -> + Uni.createFrom() + .item( + () -> { + signatureService.verifySignature( + formItem.getFile(), + inputSignatureVerification.getItem2(), + inputSignatureVerification.getItem1()); + return onboarding; + }) + .runSubscriptionOn(Infrastructure.getDefaultWorkerPool())); + return complete(onboardingId, formItem, verification); + } else { + return completeWithoutSignatureVerification(onboardingId, formItem); + } + } + + @Override + public Uni completeOnboardingUsers(String onboardingId, FormItem formItem) { + if (Boolean.TRUE.equals(isVerifyEnabled)) { + // Retrieve as Tuple: managers fiscal-code from user registry and contract digest + // At least, verify contract signature using both + Function> verification = + onboarding -> + Uni.combine() + .all() + .unis( + retrieveOnboardingUserFiscalCodeList(onboarding), + retrieveContractDigest(onboardingId)) + .asTuple() + .onItem() + .transformToUni( + inputSignatureVerification -> + Uni.createFrom() + .item( + () -> { + signatureService.verifySignature( + formItem.getFile(), + inputSignatureVerification.getItem2(), + inputSignatureVerification.getItem1()); + return onboarding; + }) + .runSubscriptionOn(Infrastructure.getDefaultWorkerPool())); + + return completeOnboardingUsers(onboardingId, formItem, verification); + } else { + return completeOnboardingUsersWithoutSignatureVerification(onboardingId, formItem); + } + } + + public Uni completeOnboardingUsersWithoutSignatureVerification( + String onboardingId, FormItem formItem) { + Function> verification = ignored -> Uni.createFrom().item(ignored); + return completeOnboardingUsers(onboardingId, formItem, verification); + } + + @Override + public Uni completeWithoutSignatureVerification( + String onboardingId, FormItem formItem) { + Function> verification = ignored -> Uni.createFrom().item(ignored); + return complete(onboardingId, formItem, verification); + } + + private Uni complete( + String onboardingId, + FormItem formItem, + Function> verificationContractSignature) { + + return retrieveOnboardingAndCheckIfExpired(onboardingId) + .onItem() + .transformToUni(verificationContractSignature::apply) + // Fail if onboarding exists for a product + .onItem() + .transformToUni( + onboarding -> + product(onboarding.getProductId()) + .onItem() + .transformToUni( + product -> + verifyAlreadyOnboardingForProductAndProductParent( + onboarding.getInstitution(), product.getId(), product.getParentId())) - .replaceWith(onboarding) - ) - //Upload contract on storage - .onItem().transformToUni(onboarding -> uploadSignedContractAndUpdateToken(onboardingId, formItem) - .map(ignore -> onboarding)) - // Start async activity if onboardingOrchestrationEnabled is true - .onItem().transformToUni(onboarding -> onboardingOrchestrationEnabled - ? orchestrationApi.apiStartOnboardingOrchestrationGet(onboarding.getId(), null) + .replaceWith(onboarding)) + // Upload contract on storage + .onItem() + .transformToUni( + onboarding -> + uploadSignedContractAndUpdateToken(onboardingId, formItem) + .map(ignore -> onboarding)) + // Start async activity if onboardingOrchestrationEnabled is true + .onItem() + .transformToUni( + onboarding -> + onboardingOrchestrationEnabled + ? orchestrationApi + .apiStartOnboardingOrchestrationGet(onboarding.getId(), null) .map(ignore -> onboarding) - : Uni.createFrom().item(onboarding)); - } - - private Uni completeOnboardingUsers(String onboardingId, FormItem formItem, Function> verificationContractSignature) { - - return retrieveOnboardingAndCheckIfExpired(onboardingId) - .onItem().transformToUni(verificationContractSignature::apply) - //Fail if onboarding exists for a product - .onItem().transformToUni(onboarding -> product(onboarding.getProductId()) - .onItem().transformToUni(product -> verifyOnboardingNotExistForProductAndProductParent(onboarding, - product.getId(), - product.getParentId())) - .replaceWith(onboarding) - ) - //Upload contract on storage - .onItem().transformToUni(onboarding -> uploadSignedContractAndUpdateToken(onboardingId, formItem) - .map(ignore -> onboarding)) - // Start async activity if onboardingOrchestrationEnabled is true - .onItem().transformToUni(onboarding -> onboardingOrchestrationEnabled - ? orchestrationApi.apiStartOnboardingOrchestrationGet(onboarding.getId(), null) + : Uni.createFrom().item(onboarding)); + } + + private Uni completeOnboardingUsers( + String onboardingId, + FormItem formItem, + Function> verificationContractSignature) { + + return retrieveOnboardingAndCheckIfExpired(onboardingId) + .onItem() + .transformToUni(verificationContractSignature::apply) + // Fail if onboarding exists for a product + .onItem() + .transformToUni( + onboarding -> + product(onboarding.getProductId()) + .onItem() + .transformToUni( + product -> + verifyOnboardingNotExistForProductAndProductParent( + onboarding, product.getId(), product.getParentId())) + .replaceWith(onboarding)) + // Upload contract on storage + .onItem() + .transformToUni( + onboarding -> + uploadSignedContractAndUpdateToken(onboardingId, formItem) + .map(ignore -> onboarding)) + // Start async activity if onboardingOrchestrationEnabled is true + .onItem() + .transformToUni( + onboarding -> + onboardingOrchestrationEnabled + ? orchestrationApi + .apiStartOnboardingOrchestrationGet(onboarding.getId(), null) .map(ignore -> onboarding) - : Uni.createFrom().item(onboarding)); - } - - private Uni uploadSignedContractAndUpdateToken(String onboardingId, FormItem formItem) { - return retrieveToken(onboardingId) - .onItem().transformToUni(token -> Uni.createFrom().item(Unchecked.supplier(() -> { - final String path = String.format("%s%s", pathContracts, onboardingId); - final String signedContractExtension = getFileExtension(formItem.getFileName()); - final String persistedContractFileName = Optional.ofNullable(token.getContractFilename()).orElse(onboardingId); - final String signedContractFileName = replaceFileExtension(persistedContractFileName, signedContractExtension); - final String filename = String.format("signed_%s", signedContractFileName); - - try { - return azureBlobClient.uploadFile(path, filename, Files.readAllBytes(formItem.getFile().toPath())); - } catch (IOException e) { - throw new OnboardingNotAllowedException(GENERIC_ERROR.getCode(), - "Error on upload contract for onboarding with id " + onboardingId); - } - })) - .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - .onItem().transformToUni(filepath -> Token.update("contractSigned", filepath) - .where("_id", token.getId()) - .replaceWith(filepath)) - ); - } - - private String getFileExtension(String name) { - String[] parts = name.split("\\."); - String ext = ""; - - if (parts.length == 2) { - return parts[1]; - } - - if (parts.length > 2) { - // join all parts except the first one - ext = String.join(".", Arrays.copyOfRange(parts, 1, parts.length)); - } - - return ext; - } - - private String replaceFileExtension(String originalFilename, String newExtension) { - int lastIndexOf = originalFilename.lastIndexOf("."); - if (lastIndexOf == -1) { - return originalFilename + newExtension; - } else { - return originalFilename.substring(0, lastIndexOf) + "." + newExtension; - } - } - - private Uni retrieveOnboardingAndCheckIfExpired(String onboardingId) { - //Retrieve Onboarding if exists - return Onboarding.findByIdOptional(onboardingId) - .onItem().transformToUni(opt -> opt - //I must cast to Onboarding because findByIdOptional return a generic ReactiveEntity - .map(Onboarding.class::cast) - //Check if onboarding is expired - .filter(onboarding -> !isOnboardingExpired(onboarding.getExpiringDate())) - .map(onboarding -> Uni.createFrom().item(onboarding)) - .orElse(Uni.createFrom().failure(new InvalidRequestException(String.format(ONBOARDING_EXPIRED.getMessage(), - onboardingId, ONBOARDING_EXPIRED.getCode()))))); - } - - - private Uni checkIfToBeValidated(Onboarding onboarding) { - return OnboardingStatus.TOBEVALIDATED.equals(onboarding.getStatus()) - ? Uni.createFrom().item(onboarding) - : Uni.createFrom().failure(new InvalidRequestException(String.format(ONBOARDING_NOT_TO_BE_VALIDATED.getMessage(), - onboarding.getId(), ONBOARDING_NOT_TO_BE_VALIDATED.getCode()))); - } - - public static boolean isOnboardingExpired(LocalDateTime dateTime) { - LocalDateTime now = LocalDateTime.now(); - return Objects.nonNull(dateTime) && (now.isEqual(dateTime) || now.isAfter(dateTime)); - } - - - private Uni retrieveContractDigest(String onboardingId) { - return retrieveToken(onboardingId) - .map(Token::getChecksum); - } - - private Uni retrieveToken(String onboardingId) { - return Token.list("onboardingId", onboardingId) - .map(tokens -> tokens.stream().findFirst() - .map(token -> (Token) token) - .orElseThrow()); - } - - private Uni> retrieveOnboardingUserFiscalCodeList(Onboarding onboarding) { - return Multi.createFrom().iterable(onboarding.getUsers().stream() - .filter(user -> PartyRole.MANAGER.equals(user.getRole())) - .map(User::getId) - .toList()) - .onItem().transformToUni(userId -> userRegistryApi.findByIdUsingGET(USERS_FIELD_TAXCODE, userId)) - .merge().collect().asList() - .onItem().transform(usersResource -> usersResource.stream().map(UserResource::getFiscalCode).toList()); - } - - @Override - public Uni onboardingGet(OnboardingGetFilters filters) { - Document sort = QueryUtils.buildSortDocument(Onboarding.Fields.createdAt.name(), SortEnum.DESC); - Map queryParameter = QueryUtils.createMapForOnboardingQueryParameter(filters); - Document query = QueryUtils.buildQuery(queryParameter); - - return Uni.combine().all().unis( - runQuery(query, sort).page(filters.getPage(), filters.getSize()).list(), - runQuery(query, null).count() - ).asTuple() - .map(this::constructOnboardingGetResponse); - } - - private ReactivePanacheQuery runQuery(Document query, Document sort) { - return Onboarding.find(query, sort); - } - - private OnboardingGetResponse constructOnboardingGetResponse(Tuple2, Long> tuple) { - OnboardingGetResponse onboardingGetResponse = new OnboardingGetResponse(); - onboardingGetResponse.setCount(tuple.getItem2()); - onboardingGetResponse.setItems(convertOnboardingListToResponse(tuple.getItem1())); - return onboardingGetResponse; - } - - private List convertOnboardingListToResponse(List item1) { - return item1.stream() - .map(onboardingMapper::toGetResponse) - .toList(); - } - - @Override - public Uni rejectOnboarding(String onboardingId, String reasonForReject) { - return Onboarding.findById(onboardingId) - .onItem().transform(Onboarding.class::cast) - .onItem().transformToUni(onboardingGet -> OnboardingStatus.COMPLETED.equals(onboardingGet.getStatus()) - ? Uni.createFrom().failure(new InvalidRequestException(String.format("Onboarding with id %s is COMPLETED!", onboardingId))) - : Uni.createFrom().item(onboardingGet)) - .onItem().transformToUni(id -> updateReasonForRejectAndUpdateStatus(onboardingId, reasonForReject)) - // Start async activity if onboardingOrchestrationEnabled is true - .onItem().transformToUni(onboarding -> onboardingOrchestrationEnabled - ? orchestrationApi.apiStartOnboardingOrchestrationGet(onboardingId, "60") + : Uni.createFrom().item(onboarding)); + } + + private Uni uploadSignedContractAndUpdateToken(String onboardingId, FormItem formItem) { + return retrieveToken(onboardingId) + .onItem() + .transformToUni( + token -> + Uni.createFrom() + .item( + Unchecked.supplier( + () -> { + final String path = + String.format("%s%s", pathContracts, onboardingId); + final String signedContractExtension = + getFileExtension(formItem.getFileName()); + final String persistedContractFileName = + Optional.ofNullable(token.getContractFilename()) + .orElse(onboardingId); + final String signedContractFileName = + replaceFileExtension( + persistedContractFileName, signedContractExtension); + final String filename = + String.format("signed_%s", signedContractFileName); + + try { + return azureBlobClient.uploadFile( + path, + filename, + Files.readAllBytes(formItem.getFile().toPath())); + } catch (IOException e) { + throw new OnboardingNotAllowedException( + GENERIC_ERROR.getCode(), + "Error on upload contract for onboarding with id " + + onboardingId); + } + })) + .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) + .onItem() + .transformToUni( + filepath -> + Token.update("contractSigned", filepath) + .where("_id", token.getId()) + .replaceWith(filepath))); + } + + private String getFileExtension(String name) { + String[] parts = name.split("\\."); + String ext = ""; + + if (parts.length == 2) { + return parts[1]; + } + + if (parts.length > 2) { + // join all parts except the first one + ext = String.join(".", Arrays.copyOfRange(parts, 1, parts.length)); + } + + return ext; + } + + private String replaceFileExtension(String originalFilename, String newExtension) { + int lastIndexOf = originalFilename.lastIndexOf("."); + if (lastIndexOf == -1) { + return originalFilename + newExtension; + } else { + return originalFilename.substring(0, lastIndexOf) + "." + newExtension; + } + } + + private Uni retrieveOnboardingAndCheckIfExpired(String onboardingId) { + // Retrieve Onboarding if exists + return Onboarding.findByIdOptional(onboardingId) + .onItem() + .transformToUni( + opt -> + opt + // I must cast to Onboarding because findByIdOptional return a generic + // ReactiveEntity + .map(Onboarding.class::cast) + // Check if onboarding is expired + .filter(onboarding -> !isOnboardingExpired(onboarding.getExpiringDate())) + .map(onboarding -> Uni.createFrom().item(onboarding)) + .orElse( + Uni.createFrom() + .failure( + new InvalidRequestException( + String.format( + ONBOARDING_EXPIRED.getMessage(), + onboardingId, + ONBOARDING_EXPIRED.getCode()))))); + } + + private Uni checkIfToBeValidated(Onboarding onboarding) { + return OnboardingStatus.TOBEVALIDATED.equals(onboarding.getStatus()) + ? Uni.createFrom().item(onboarding) + : Uni.createFrom() + .failure( + new InvalidRequestException( + String.format( + ONBOARDING_NOT_TO_BE_VALIDATED.getMessage(), + onboarding.getId(), + ONBOARDING_NOT_TO_BE_VALIDATED.getCode()))); + } + + public static boolean isOnboardingExpired(LocalDateTime dateTime) { + LocalDateTime now = LocalDateTime.now(); + return Objects.nonNull(dateTime) && (now.isEqual(dateTime) || now.isAfter(dateTime)); + } + + private Uni retrieveContractDigest(String onboardingId) { + return retrieveToken(onboardingId).map(Token::getChecksum); + } + + private Uni retrieveToken(String onboardingId) { + return Token.list("onboardingId", onboardingId) + .map(tokens -> tokens.stream().findFirst().map(token -> (Token) token).orElseThrow()); + } + + private Uni> retrieveOnboardingUserFiscalCodeList(Onboarding onboarding) { + return Multi.createFrom() + .iterable( + onboarding.getUsers().stream() + .filter(user -> PartyRole.MANAGER.equals(user.getRole())) + .map(User::getId) + .toList()) + .onItem() + .transformToUni(userId -> userRegistryApi.findByIdUsingGET(USERS_FIELD_TAXCODE, userId)) + .merge() + .collect() + .asList() + .onItem() + .transform( + usersResource -> usersResource.stream().map(UserResource::getFiscalCode).toList()); + } + + @Override + public Uni onboardingGet(OnboardingGetFilters filters) { + Document sort = QueryUtils.buildSortDocument(Onboarding.Fields.createdAt.name(), SortEnum.DESC); + Map queryParameter = QueryUtils.createMapForOnboardingQueryParameter(filters); + Document query = QueryUtils.buildQuery(queryParameter); + + return Uni.combine() + .all() + .unis( + runQuery(query, sort).page(filters.getPage(), filters.getSize()).list(), + runQuery(query, null).count()) + .asTuple() + .map(this::constructOnboardingGetResponse); + } + + private ReactivePanacheQuery runQuery(Document query, Document sort) { + return Onboarding.find(query, sort); + } + + private OnboardingGetResponse constructOnboardingGetResponse( + Tuple2, Long> tuple) { + OnboardingGetResponse onboardingGetResponse = new OnboardingGetResponse(); + onboardingGetResponse.setCount(tuple.getItem2()); + onboardingGetResponse.setItems(convertOnboardingListToResponse(tuple.getItem1())); + return onboardingGetResponse; + } + + private List convertOnboardingListToResponse(List item1) { + return item1.stream().map(onboardingMapper::toGetResponse).toList(); + } + + @Override + public Uni rejectOnboarding(String onboardingId, String reasonForReject) { + return Onboarding.findById(onboardingId) + .onItem() + .transform(Onboarding.class::cast) + .onItem() + .transformToUni( + onboardingGet -> + OnboardingStatus.COMPLETED.equals(onboardingGet.getStatus()) + ? Uni.createFrom() + .failure( + new InvalidRequestException( + String.format("Onboarding with id %s is COMPLETED!", onboardingId))) + : Uni.createFrom().item(onboardingGet)) + .onItem() + .transformToUni(id -> updateReasonForRejectAndUpdateStatus(onboardingId, reasonForReject)) + // Start async activity if onboardingOrchestrationEnabled is true + .onItem() + .transformToUni( + onboarding -> + onboardingOrchestrationEnabled + ? orchestrationApi + .apiStartOnboardingOrchestrationGet(onboardingId, "60") .map(ignore -> onboarding) - : Uni.createFrom().item(onboarding)); - } - - /** - * Returns an onboarding record by its ID only if its status is PENDING. - * This feature is crucial for ensuring that the onboarding process can be completed only when - * the onboarding status is appropriately set to PENDING. - * - * @param onboardingId String - * @return OnboardingGet - */ - @Override - public Uni onboardingPending(String onboardingId) { - return onboardingGet(onboardingId) - .flatMap(onboardingGet -> OnboardingStatus.PENDING.name().equals(onboardingGet.getStatus()) + : Uni.createFrom().item(onboarding)); + } + + /** + * Returns an onboarding record by its ID only if its status is PENDING. This feature is crucial + * for ensuring that the onboarding process can be completed only when the onboarding status is + * appropriately set to PENDING. + * + * @param onboardingId String + * @return OnboardingGet + */ + @Override + public Uni onboardingPending(String onboardingId) { + return onboardingGet(onboardingId) + .flatMap( + onboardingGet -> + OnboardingStatus.PENDING.name().equals(onboardingGet.getStatus()) || OnboardingStatus.TOBEVALIDATED.name().equals(onboardingGet.getStatus()) - ? Uni.createFrom().item(onboardingGet) - : Uni.createFrom().failure(new ResourceNotFoundException(String.format("Onboarding with id %s not found or not in PENDING status!", onboardingId)))); - } - - @Override - public Uni> institutionOnboardings(String taxCode, String subunitCode, String origin, String originId, OnboardingStatus status) { - Map queryParameter = QueryUtils.createMapForInstitutionOnboardingsQueryParameter(taxCode, subunitCode, origin, originId, status, null); - Document query = QueryUtils.buildQuery(queryParameter); - return Onboarding.find(query).stream() - .map(Onboarding.class::cast) - .map(onboardingMapper::toResponse) - .collect().asList(); - } - - @Override - public Uni> verifyOnboarding(String taxCode, String subunitCode, String origin, String originId, OnboardingStatus status, String productId) { - Map queryParameter = QueryUtils.createMapForInstitutionOnboardingsQueryParameter(taxCode, subunitCode, origin, originId, status, productId); - Document query = QueryUtils.buildQuery(queryParameter); - return Onboarding.find(query).stream() - .map(Onboarding.class::cast) - .map(onboardingMapper::toResponse) - .collect().asList(); - } - - @Override - public Uni onboardingGet(String onboardingId) { - return Onboarding.findByIdOptional(onboardingId) - .onItem().transformToUni(opt -> opt - //I must cast to Onboarding because findByIdOptional return a generic ReactiveEntity - .map(Onboarding.class::cast) - .map(onboardingMapper::toGetResponse) - .map(onboardingGet -> Uni.createFrom().item(onboardingGet)) - .orElse(Uni.createFrom().failure(new ResourceNotFoundException(String.format("Onboarding with id %s not found!", onboardingId))))); - } - - @Override - public Uni onboardingGetWithUserInfo(String onboardingId) { - return Onboarding.findByIdOptional(onboardingId) - .onItem().transformToUni(opt -> opt - //I must cast to Onboarding because findByIdOptional return a generic ReactiveEntity - .map(Onboarding.class::cast) - .map(onboardingGet -> Uni.createFrom().item(onboardingGet)) - .orElse(Uni.createFrom().failure(new ResourceNotFoundException(String.format("Onboarding with id %s not found!", onboardingId))))) - .flatMap(onboarding -> toUserResponseWithUserInfo(onboarding.getUsers()) - .onItem().transform(userResponses -> { - OnboardingGet onboardingGet = onboardingMapper.toGetResponse(onboarding); - onboardingGet.setUsers(userResponses); - return onboardingGet; + ? Uni.createFrom().item(onboardingGet) + : Uni.createFrom() + .failure( + new ResourceNotFoundException( + String.format( + "Onboarding with id %s not found or not in PENDING status!", + onboardingId)))); + } + + @Override + public Uni> institutionOnboardings( + String taxCode, String subunitCode, String origin, String originId, OnboardingStatus status) { + Map queryParameter = + QueryUtils.createMapForInstitutionOnboardingsQueryParameter( + taxCode, subunitCode, origin, originId, status, null); + Document query = QueryUtils.buildQuery(queryParameter); + return Onboarding.find(query).stream() + .map(Onboarding.class::cast) + .map(onboardingMapper::toResponse) + .collect() + .asList(); + } + + @Override + public Uni> verifyOnboarding( + String taxCode, + String subunitCode, + String origin, + String originId, + OnboardingStatus status, + String productId) { + Map queryParameter = + QueryUtils.createMapForInstitutionOnboardingsQueryParameter( + taxCode, subunitCode, origin, originId, status, productId); + Document query = QueryUtils.buildQuery(queryParameter); + return Onboarding.find(query).stream() + .map(Onboarding.class::cast) + .map(onboardingMapper::toResponse) + .collect() + .asList(); + } + + @Override + public Uni onboardingGet(String onboardingId) { + return Onboarding.findByIdOptional(onboardingId) + .onItem() + .transformToUni( + opt -> + opt + // I must cast to Onboarding because findByIdOptional return a generic + // ReactiveEntity + .map(Onboarding.class::cast) + .map(onboardingMapper::toGetResponse) + .map(onboardingGet -> Uni.createFrom().item(onboardingGet)) + .orElse( + Uni.createFrom() + .failure( + new ResourceNotFoundException( + String.format( + "Onboarding with id %s not found!", onboardingId))))); + } + + @Override + public Uni onboardingGetWithUserInfo(String onboardingId) { + return Onboarding.findByIdOptional(onboardingId) + .onItem() + .transformToUni( + opt -> + opt + // I must cast to Onboarding because findByIdOptional return a generic + // ReactiveEntity + .map(Onboarding.class::cast) + .map(onboardingGet -> Uni.createFrom().item(onboardingGet)) + .orElse( + Uni.createFrom() + .failure( + new ResourceNotFoundException( + String.format( + "Onboarding with id %s not found!", onboardingId))))) + .flatMap( + onboarding -> + toUserResponseWithUserInfo(onboarding.getUsers()) + .onItem() + .transform( + userResponses -> { + OnboardingGet onboardingGet = onboardingMapper.toGetResponse(onboarding); + onboardingGet.setUsers(userResponses); + return onboardingGet; })); - } - - private Uni> toUserResponseWithUserInfo(List users) { - return Multi.createFrom().iterable(users) - .onItem().transformToUni(user -> userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, user.getId()) - .onItem().transform(userResource -> { - UserResponse userResponse = userMapper.toUserResponse(user); - userMapper.fillUserResponse(userResource, userResponse); - - Optional.ofNullable(userResource.getWorkContacts()) - .filter(map -> map.containsKey(user.getUserMailUuid())) - .map(map -> map.get(user.getUserMailUuid())) - .filter(workContract -> StringUtils.isNotBlank(workContract.getEmail().getValue())) - .map(workContract -> workContract.getEmail().getValue()) - .ifPresent(userResponse::setEmail); - return userResponse; - }) - ) - .merge().collect().asList(); - } - - private Uni setInstitutionTypeAndBillingData(Onboarding onboarding) { - return institutionRegistryProxyApi.findInstitutionUsingGET(onboarding.getInstitution().getTaxCode(), null, null) - .onItem() - .invoke(proxyInstitution -> { - if (Objects.nonNull(proxyInstitution)) { - InstitutionType institutionType = proxyInstitution.getCategory().equalsIgnoreCase(GSP_CATEGORY_INSTITUTION_TYPE) ? InstitutionType.GSP : InstitutionType.PA; - onboarding.getInstitution().setInstitutionType(institutionType); - - Billing billing = new Billing(); - billing.setVatNumber(proxyInstitution.getTaxCode()); - billing.setRecipientCode(proxyInstitution.getOriginId()); - onboarding.setBilling(billing); - } else { - onboarding.getInstitution().setInstitutionType(InstitutionType.PA); - } - }) - .replaceWith(Uni.createFrom().item(onboarding)); - } - - private static Uni updateReasonForRejectAndUpdateStatus(String onboardingId, String reasonForReject) { - Map queryParameter = QueryUtils.createMapForOnboardingReject(reasonForReject, OnboardingStatus.REJECTED.name()); - Document query = QueryUtils.buildUpdateDocument(queryParameter); - return Onboarding.update(query) - .where("_id", onboardingId) - .onItem().transformToUni(updateItemCount -> { - if (updateItemCount == 0) { - return Uni.createFrom().failure(new InvalidRequestException(String.format(ONBOARDING_NOT_FOUND_OR_ALREADY_DELETED, onboardingId))); - } - return Uni.createFrom().item(updateItemCount); - }); - } - - private Uni getInstitutionFromUserRequest(OnboardingUserRequest request) { - Uni responseUni; - if (Objects.nonNull(request.getTaxCode()) && Objects.nonNull(request.getSubunitCode())) { - responseUni = institutionApi.getInstitutionsUsingGET(request.getTaxCode(), request.getSubunitCode(), null, null); - } else if (Objects.nonNull(request.getTaxCode())) { - responseUni = institutionApi.getInstitutionsUsingGET(request.getTaxCode(), null, null, null); - } else { - responseUni = institutionApi.getInstitutionsUsingGET(null, null, request.getOrigin(), request.getOriginId()); - } - return responseUni.onFailure(WebApplicationException.class).recoverWithUni(ex -> ((WebApplicationException) ex).getResponse().getStatus() == 404 - ? Uni.createFrom().failure(new ResourceNotFoundException( - String.format(INSTITUTION_NOT_FOUND.getMessage(), - request.getTaxCode(), request.getOrigin(), - request.getOriginId(), request.getSubunitCode() - ))) - : Uni.createFrom().failure(ex)) - .onItem().transformToUni(response -> { - if (Objects.isNull(response.getInstitutions()) || response.getInstitutions().size() > 1) { - return Uni.createFrom().failure(new ResourceNotFoundException( - String.format(INSTITUTION_NOT_FOUND.getMessage(), - request.getTaxCode(), request.getOrigin(), - request.getOriginId(), request.getSubunitCode() - ))); - } - return Uni.createFrom().item(response.getInstitutions().get(0)); - }); - } - - private Token getToken(Onboarding onboarding, Product product, OnboardingImportContract contractImported) { - var token = new Token(); - token.setId(onboarding.getId()); - token.setOnboardingId(onboarding.getId()); - token.setContractTemplate(product.getContractTemplatePath()); - token.setContractVersion(product.getContractTemplateVersion()); - token.setContractSigned(contractImported.getFilePath()); - token.setContractFilename(contractImported.getFileName()); - token.setCreatedAt(contractImported.getCreatedAt()); - token.setUpdatedAt(contractImported.getCreatedAt()); - token.setProductId(onboarding.getProductId()); - token.setType(TokenType.INSTITUTION); - return token; - } - - @Override - public Uni updateOnboarding(String onboardingId, Onboarding onboarding) { - return Onboarding.findById(onboardingId) - .onItem().transform(Onboarding.class::cast) - .onItem().transformToUni(onboardingGet -> Objects.isNull(onboardingGet) - ? Uni.createFrom().failure(new InvalidRequestException(String.format("Onboarding with id %s is not present!", onboardingId))) - : Uni.createFrom().item(onboardingGet)) - .onItem().transformToUni(id -> updateOnboardingValues(onboardingId, onboarding)); - - } - - @Override - public Uni checkManager(OnboardingUserRequest onboardingUserRequest) { - CheckManagerResponse response = new CheckManagerResponse(); - - String taxCodeManager = onboardingUserRequest.getUsers().stream() - .filter(user -> PartyRole.MANAGER == user.getRole()) - .map(UserRequest::getTaxCode) - .findFirst() - .orElseThrow(() -> new InvalidRequestException("At least one user should have role MANAGER")); - - return userRegistryApi.searchUsingPOST(USERS_FIELD_LIST, new UserSearchDto().fiscalCode(taxCodeManager)) - .onItem().transform(UserResource::getId) - .flatMap(uuid -> findOnboardingsByFilters(onboardingUserRequest) - .flatMap(onboardings -> { - if(CollectionUtils.isEmpty(onboardings)) { - LOG.debugf("Onboarding for taxCode %s, origin %s, originId %s, productId %s, subunitCode %s not found", - onboardingUserRequest.getTaxCode(), onboardingUserRequest.getOrigin(), - onboardingUserRequest.getOriginId(), onboardingUserRequest.getProductId(), - onboardingUserRequest.getSubunitCode()); - - response.setResponse(false); - return Uni.createFrom().item(response); - } - - String institutionId = onboardings.get(0).getInstitution().getId(); - return isUserActiveManager(institutionId, onboardingUserRequest.getProductId(), String.valueOf(uuid)) - .map(isActiveManager -> { - LOG.debugf("User with uuid %s is active manager: %s", uuid, isActiveManager); - response.setResponse(isActiveManager); - return response; - }); + } + + private Uni> toUserResponseWithUserInfo(List users) { + return Multi.createFrom() + .iterable(users) + .onItem() + .transformToUni( + user -> + userRegistryApi + .findByIdUsingGET(USERS_FIELD_LIST, user.getId()) + .onItem() + .transform( + userResource -> { + UserResponse userResponse = userMapper.toUserResponse(user); + userMapper.fillUserResponse(userResource, userResponse); + + Optional.ofNullable(userResource.getWorkContacts()) + .filter(map -> map.containsKey(user.getUserMailUuid())) + .map(map -> map.get(user.getUserMailUuid())) + .filter( + workContract -> + StringUtils.isNotBlank(workContract.getEmail().getValue())) + .map(workContract -> workContract.getEmail().getValue()) + .ifPresent(userResponse::setEmail); + return userResponse; })) - .onFailure().recoverWithUni(ex -> { - if (ex instanceof WebApplicationException && ((WebApplicationException) ex).getResponse().getStatus() == 404) { - LOG.debugf("User not found on user-registry", taxCodeManager); - response.setResponse(false); - return Uni.createFrom().item(response); - } - - //If the exception raised is for a different status code, let it propagate - return Uni.createFrom().failure(ex); - }); - } - - /** - * Retrieves the onboarding record by the given filters. - * - * @param onboardingUserRequest OnboardingUserRequest - * @return a Uni with the list of onboardings - * @throws ResourceNotFoundException if the onboarding record is not found - */ - private Uni> findOnboardingsByFilters(OnboardingUserRequest onboardingUserRequest) { - return getOnboardingByFilters( - onboardingUserRequest.getTaxCode(), - onboardingUserRequest.getSubunitCode(), - onboardingUserRequest.getOrigin(), - onboardingUserRequest.getOriginId(), - onboardingUserRequest.getProductId() - ) + .merge() .collect() .asList(); - } - - /** - * Checks if the user is an active manager within the institution for the given product invoking selfcare-user API. - * - * @param institutionId institution id - * @param productId product id - * @param uuid user uuid - * @return a Uni with the result of the check - */ - private Uni isUserActiveManager(String institutionId, String productId, String uuid) { - return userInstitutionApi.retrieveUserInstitutions( - institutionId, - null, - Objects.nonNull(productId) ? List.of(productId) : null, - List.of(String.valueOf(PartyRole.MANAGER)), - List.of(String.valueOf(OnboardedProductResponse.StatusEnum.ACTIVE)), - uuid - ).onFailure().invoke(e -> LOG.error("Error while checking if user is active manager", e)) - .onItem().transform(CollectionUtils::isNotEmpty); - } - - public Uni checkRecipientCode(String recipientCode, String originId) { - return onboardingUtils.getUoFromRecipientCode(recipientCode).onItem() - .transformToUni(uoResource -> - onboardingUtils.getValidationRecipientCodeError(originId, uoResource)); - } - - private static Uni updateOnboardingValues(String onboardingId, Onboarding onboarding) { - Map queryParameter = QueryUtils.createMapForOnboardingUpdate(onboarding); - Document query = QueryUtils.buildUpdateDocument(queryParameter); - return Onboarding.update(query) - .where("_id", onboardingId) - .onItem().transformToUni(updateItemCount -> { - if (updateItemCount == 0) { - return Uni.createFrom().failure(new InvalidRequestException(String.format(ONBOARDING_NOT_FOUND_OR_ALREADY_DELETED, onboardingId))); - } - return Uni.createFrom().item(updateItemCount); - }); - } - - private Uni> getOnboardingList(List onboardings) { - if (onboardings.isEmpty()) { - return Uni.createFrom().nullItem(); - } - return Uni.createFrom().item(onboardings); - } - - private Uni getUO(Onboarding onboarding) { - return uoApi.findByUnicodeUsingGET1(onboarding.getInstitution().getSubunitCode(), null) - .onFailure(WebApplicationException.class).recoverWithUni(ex -> ((WebApplicationException) ex).getResponse().getStatus() == 404 - ? Uni.createFrom().failure(new ResourceNotFoundException(String.format(UO_NOT_FOUND.getMessage(), onboarding.getInstitution().getSubunitCode()))) - : Uni.createFrom().failure(ex)) - .onItem().transformToUni(uoResource -> Uni.createFrom().item(OnboardingUtils.ProxyResource.builder() - .resource(uoResource) - .type(UO) - .build())); - } - - private Uni getAOO(Onboarding onboarding) { - return aooApi.findByUnicodeUsingGET(onboarding.getInstitution().getSubunitCode(), null) - .onFailure(WebApplicationException.class).recoverWithUni(ex -> ((WebApplicationException) ex).getResponse().getStatus() == 404 - ? Uni.createFrom().failure(new ResourceNotFoundException(String.format(AOO_NOT_FOUND.getMessage(), onboarding.getInstitution().getSubunitCode()))) - : Uni.createFrom().failure(ex)) - .onItem().transformToUni(aooResource -> Uni.createFrom().item(OnboardingUtils.ProxyResource.builder() - .resource(aooResource) - .type(AOO) - .build())); - } - - private Uni getEC() { - return Uni.createFrom().item(OnboardingUtils.ProxyResource.builder() - .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); - - 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( + } + + private Uni setInstitutionTypeAndBillingData(Onboarding onboarding) { + return institutionRegistryProxyApi + .findInstitutionUsingGET(onboarding.getInstitution().getTaxCode(), null, null) + .onItem() + .invoke( + proxyInstitution -> { + if (Objects.nonNull(proxyInstitution)) { + InstitutionType institutionType = + proxyInstitution.getCategory().equalsIgnoreCase(GSP_CATEGORY_INSTITUTION_TYPE) + ? InstitutionType.GSP + : InstitutionType.PA; + onboarding.getInstitution().setInstitutionType(institutionType); + + Billing billing = new Billing(); + billing.setVatNumber(proxyInstitution.getTaxCode()); + billing.setRecipientCode(proxyInstitution.getOriginId()); + onboarding.setBilling(billing); + } else { + onboarding.getInstitution().setInstitutionType(InstitutionType.PA); + } + }) + .replaceWith(Uni.createFrom().item(onboarding)); + } + + private static Uni updateReasonForRejectAndUpdateStatus( + String onboardingId, String reasonForReject) { + Map queryParameter = + QueryUtils.createMapForOnboardingReject(reasonForReject, OnboardingStatus.REJECTED.name()); + Document query = QueryUtils.buildUpdateDocument(queryParameter); + return Onboarding.update(query) + .where("_id", onboardingId) + .onItem() + .transformToUni( + updateItemCount -> { + if (updateItemCount == 0) { + return Uni.createFrom() + .failure( + new InvalidRequestException( + String.format(ONBOARDING_NOT_FOUND_OR_ALREADY_DELETED, onboardingId))); + } + return Uni.createFrom().item(updateItemCount); + }); + } + + private Uni getInstitutionFromUserRequest(OnboardingUserRequest request) { + Uni responseUni; + if (Objects.nonNull(request.getTaxCode()) && Objects.nonNull(request.getSubunitCode())) { + responseUni = + institutionApi.getInstitutionsUsingGET( + request.getTaxCode(), request.getSubunitCode(), null, null); + } else if (Objects.nonNull(request.getTaxCode())) { + responseUni = institutionApi.getInstitutionsUsingGET(request.getTaxCode(), null, null, null); + } else { + responseUni = + institutionApi.getInstitutionsUsingGET( + null, null, request.getOrigin(), request.getOriginId()); + } + return responseUni + .onFailure(WebApplicationException.class) + .recoverWithUni( + ex -> + ((WebApplicationException) ex).getResponse().getStatus() == 404 + ? Uni.createFrom() + .failure( + new ResourceNotFoundException( + String.format( + INSTITUTION_NOT_FOUND.getMessage(), + request.getTaxCode(), + request.getOrigin(), + request.getOriginId(), + request.getSubunitCode()))) + : Uni.createFrom().failure(ex)) + .onItem() + .transformToUni( + response -> { + if (Objects.isNull(response.getInstitutions()) + || response.getInstitutions().size() > 1) { + return Uni.createFrom() + .failure( + new ResourceNotFoundException( + String.format( + INSTITUTION_NOT_FOUND.getMessage(), + request.getTaxCode(), + request.getOrigin(), + request.getOriginId(), + request.getSubunitCode()))); + } + return Uni.createFrom().item(response.getInstitutions().get(0)); + }); + } + + private Token getToken( + Onboarding onboarding, Product product, OnboardingImportContract contractImported) { + ContractTemplate contractTemplate = + product.getInstitutionContractTemplate( + InstitutionUtils.getCurrentInstitutionType(onboarding)); + var token = new Token(); + token.setId(onboarding.getId()); + token.setOnboardingId(onboarding.getId()); + token.setContractTemplate(contractTemplate.getContractTemplatePath()); + token.setContractVersion(contractTemplate.getContractTemplateVersion()); + token.setContractSigned(contractImported.getFilePath()); + token.setContractFilename(contractImported.getFileName()); + token.setCreatedAt(contractImported.getCreatedAt()); + token.setUpdatedAt(contractImported.getCreatedAt()); + token.setProductId(onboarding.getProductId()); + token.setType(TokenType.INSTITUTION); + return token; + } + + @Override + public Uni updateOnboarding(String onboardingId, Onboarding onboarding) { + return Onboarding.findById(onboardingId) + .onItem() + .transform(Onboarding.class::cast) + .onItem() + .transformToUni( + onboardingGet -> + Objects.isNull(onboardingGet) + ? Uni.createFrom() + .failure( + new InvalidRequestException( + String.format( + "Onboarding with id %s is not present!", onboardingId))) + : Uni.createFrom().item(onboardingGet)) + .onItem() + .transformToUni(id -> updateOnboardingValues(onboardingId, onboarding)); + } + + @Override + public Uni checkManager(OnboardingUserRequest onboardingUserRequest) { + CheckManagerResponse response = new CheckManagerResponse(); + + String taxCodeManager = + onboardingUserRequest.getUsers().stream() + .filter(user -> PartyRole.MANAGER == user.getRole()) + .map(UserRequest::getTaxCode) + .findFirst() + .orElseThrow( + () -> new InvalidRequestException("At least one user should have role MANAGER")); + + return userRegistryApi + .searchUsingPOST(USERS_FIELD_LIST, new UserSearchDto().fiscalCode(taxCodeManager)) + .onItem() + .transform(UserResource::getId) + .flatMap( + uuid -> + findOnboardingsByFilters(onboardingUserRequest) + .flatMap( + onboardings -> { + if (CollectionUtils.isEmpty(onboardings)) { + LOG.debugf( + "Onboarding for taxCode %s, origin %s, originId %s, productId %s, subunitCode %s not found", + onboardingUserRequest.getTaxCode(), + onboardingUserRequest.getOrigin(), + onboardingUserRequest.getOriginId(), + onboardingUserRequest.getProductId(), + onboardingUserRequest.getSubunitCode()); + + response.setResponse(false); + return Uni.createFrom().item(response); + } + + String institutionId = onboardings.get(0).getInstitution().getId(); + return isUserActiveManager( + institutionId, + onboardingUserRequest.getProductId(), + String.valueOf(uuid)) + .map( + isActiveManager -> { + LOG.debugf( + "User with uuid %s is active manager: %s", + uuid, isActiveManager); + response.setResponse(isActiveManager); + return response; + }); + })) + .onFailure() + .recoverWithUni( + ex -> { + if (ex instanceof WebApplicationException + && ((WebApplicationException) ex).getResponse().getStatus() == 404) { + LOG.debugf("User not found on user-registry", taxCodeManager); + response.setResponse(false); + return Uni.createFrom().item(response); + } + + // If the exception raised is for a different status code, let it propagate + return Uni.createFrom().failure(ex); + }); + } + + /** + * Retrieves the onboarding record by the given filters. + * + * @param onboardingUserRequest OnboardingUserRequest + * @return a Uni with the list of onboardings + * @throws ResourceNotFoundException if the onboarding record is not found + */ + private Uni> findOnboardingsByFilters( + OnboardingUserRequest onboardingUserRequest) { + return getOnboardingByFilters( + onboardingUserRequest.getTaxCode(), + onboardingUserRequest.getSubunitCode(), + onboardingUserRequest.getOrigin(), + onboardingUserRequest.getOriginId(), + onboardingUserRequest.getProductId()) + .collect() + .asList(); + } + + /** + * Checks if the user is an active manager within the institution for the given product invoking + * selfcare-user API. + * + * @param institutionId institution id + * @param productId product id + * @param uuid user uuid + * @return a Uni with the result of the check + */ + private Uni isUserActiveManager(String institutionId, String productId, String uuid) { + return userInstitutionApi + .retrieveUserInstitutions( + institutionId, + null, + Objects.nonNull(productId) ? List.of(productId) : null, + List.of(String.valueOf(PartyRole.MANAGER)), + List.of(String.valueOf(OnboardedProductResponse.StatusEnum.ACTIVE)), + uuid) + .onFailure() + .invoke(e -> LOG.error("Error while checking if user is active manager", e)) + .onItem() + .transform(CollectionUtils::isNotEmpty); + } + + public Uni checkRecipientCode(String recipientCode, String originId) { + return onboardingUtils + .getUoFromRecipientCode(recipientCode) + .onItem() + .transformToUni( + uoResource -> onboardingUtils.getValidationRecipientCodeError(originId, uoResource)); + } + + private static Uni updateOnboardingValues(String onboardingId, Onboarding onboarding) { + Map queryParameter = QueryUtils.createMapForOnboardingUpdate(onboarding); + Document query = QueryUtils.buildUpdateDocument(queryParameter); + return Onboarding.update(query) + .where("_id", onboardingId) + .onItem() + .transformToUni( + updateItemCount -> { + if (updateItemCount == 0) { + return Uni.createFrom() + .failure( + new InvalidRequestException( + String.format(ONBOARDING_NOT_FOUND_OR_ALREADY_DELETED, onboardingId))); + } + return Uni.createFrom().item(updateItemCount); + }); + } + + private Uni> getOnboardingList(List onboardings) { + if (onboardings.isEmpty()) { + return Uni.createFrom().nullItem(); + } + return Uni.createFrom().item(onboardings); + } + + private Uni getUO(Onboarding onboarding) { + return uoApi + .findByUnicodeUsingGET1(onboarding.getInstitution().getSubunitCode(), null) + .onFailure(WebApplicationException.class) + .recoverWithUni( + ex -> + ((WebApplicationException) ex).getResponse().getStatus() == 404 + ? Uni.createFrom() + .failure( + new ResourceNotFoundException( + String.format( + UO_NOT_FOUND.getMessage(), + onboarding.getInstitution().getSubunitCode()))) + : Uni.createFrom().failure(ex)) + .onItem() + .transformToUni( + uoResource -> + Uni.createFrom() + .item( + OnboardingUtils.ProxyResource.builder() + .resource(uoResource) + .type(UO) + .build())); + } + + private Uni getAOO(Onboarding onboarding) { + return aooApi + .findByUnicodeUsingGET(onboarding.getInstitution().getSubunitCode(), null) + .onFailure(WebApplicationException.class) + .recoverWithUni( + ex -> + ((WebApplicationException) ex).getResponse().getStatus() == 404 + ? Uni.createFrom() + .failure( + new ResourceNotFoundException( + String.format( + AOO_NOT_FOUND.getMessage(), + onboarding.getInstitution().getSubunitCode()))) + : Uni.createFrom().failure(ex)) + .onItem() + .transformToUni( + aooResource -> + Uni.createFrom() + .item( + OnboardingUtils.ProxyResource.builder() + .resource(aooResource) + .type(AOO) + .build())); + } + + private Uni getEC() { + return Uni.createFrom().item(OnboardingUtils.ProxyResource.builder().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); + + 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(), - 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 isUserActiveManager(institutionId, currentOnboarding.getProductId(), newManagerId) - .flatMap(isActiveManager -> { - if (isActiveManager) { - throw new InvalidRequestException("User is already manager of the institution"); - } - return Uni.createFrom().voidItem(); - }); - } - - /** - * 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); - }); - } + 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 isUserActiveManager(institutionId, currentOnboarding.getProductId(), newManagerId) + .flatMap( + isActiveManager -> { + if (isActiveManager) { + throw new InvalidRequestException("User is already manager of the institution"); + } + return Uni.createFrom().voidItem(); + }); + } + + /** + * 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/java/it/pagopa/selfcare/onboarding/util/InstitutionUtils.java b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/util/InstitutionUtils.java new file mode 100644 index 000000000..8186a4b0d --- /dev/null +++ b/apps/onboarding-ms/src/main/java/it/pagopa/selfcare/onboarding/util/InstitutionUtils.java @@ -0,0 +1,21 @@ +package it.pagopa.selfcare.onboarding.util; + +import it.pagopa.selfcare.onboarding.entity.Onboarding; +import it.pagopa.selfcare.product.entity.Product; +import java.util.Objects; + +public class InstitutionUtils { + + public InstitutionUtils() {} + + public static String getCurrentInstitutionType(Onboarding onboarding) { + String institutionType = Product.CONTRACT_TYPE_DEFAULT; + + if (Objects.isNull(onboarding.getInstitution()) + || Objects.isNull(onboarding.getInstitution().getInstitutionType())) { + institutionType = onboarding.getInstitution().getInstitutionType().name(); + } + + return institutionType; + } +} diff --git a/apps/pom.xml b/apps/pom.xml index 598e5873f..62180cdae 100644 --- a/apps/pom.xml +++ b/apps/pom.xml @@ -16,7 +16,7 @@ it.pagopa.selfcare onboarding-sdk-common - 0.3.5 + 0.4.0 diff --git a/libs/onboarding-sdk-azure-storage/pom.xml b/libs/onboarding-sdk-azure-storage/pom.xml index 0b9f1f501..e0b53e6d5 100644 --- a/libs/onboarding-sdk-azure-storage/pom.xml +++ b/libs/onboarding-sdk-azure-storage/pom.xml @@ -6,7 +6,7 @@ it.pagopa.selfcare onboarding-sdk-pom - 0.3.5 + 0.4.0 ../onboarding-sdk-pom diff --git a/libs/onboarding-sdk-common/pom.xml b/libs/onboarding-sdk-common/pom.xml index 18b0a8328..8d59ebe10 100644 --- a/libs/onboarding-sdk-common/pom.xml +++ b/libs/onboarding-sdk-common/pom.xml @@ -4,7 +4,7 @@ it.pagopa.selfcare onboarding-sdk-pom - 0.3.5 + 0.4.0 ../onboarding-sdk-pom onboarding-sdk-common diff --git a/libs/onboarding-sdk-crypto/pom.xml b/libs/onboarding-sdk-crypto/pom.xml index e174fafe1..0ee3014cd 100644 --- a/libs/onboarding-sdk-crypto/pom.xml +++ b/libs/onboarding-sdk-crypto/pom.xml @@ -4,7 +4,7 @@ it.pagopa.selfcare onboarding-sdk-pom - 0.3.5 + 0.4.0 ../onboarding-sdk-pom onboarding-sdk-crypto diff --git a/libs/onboarding-sdk-pom/pom.xml b/libs/onboarding-sdk-pom/pom.xml index 6621094e6..78e65a0aa 100644 --- a/libs/onboarding-sdk-pom/pom.xml +++ b/libs/onboarding-sdk-pom/pom.xml @@ -6,7 +6,7 @@ onboarding-sdk-pom pom onboarding-sdk-pom - 0.3.5 + 0.4.0 17 diff --git a/libs/onboarding-sdk-product/pom.xml b/libs/onboarding-sdk-product/pom.xml index 10f6492e6..d54bbc7b7 100644 --- a/libs/onboarding-sdk-product/pom.xml +++ b/libs/onboarding-sdk-product/pom.xml @@ -4,16 +4,17 @@ it.pagopa.selfcare onboarding-sdk-pom - 0.3.5 + 0.4.0 ../onboarding-sdk-pom onboarding-sdk-product onboarding-sdk-product - 0.3.5 + 0.4.0 2.15.2 0.2.2 + 3.12.0 @@ -28,12 +29,27 @@ onboarding-sdk-azure-storage ${project.version}
- it.pagopa.selfcare onboarding-sdk-common ${project.version} + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + + + org.mockito mockito-inline @@ -51,11 +67,6 @@ 8.6.6 test - - com.fasterxml.jackson.datatype - jackson-datatype-jdk8 - - diff --git a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/ContractStorage.java b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/ContractStorage.java deleted file mode 100644 index 3818d4df0..000000000 --- a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/ContractStorage.java +++ /dev/null @@ -1,34 +0,0 @@ -package it.pagopa.selfcare.product.entity; - -import java.time.Instant; - -public class ContractStorage { - - private Instant contractTemplateUpdatedAt; - private String contractTemplatePath; - private String contractTemplateVersion; - - public Instant getContractTemplateUpdatedAt() { - return contractTemplateUpdatedAt; - } - - public void setContractTemplateUpdatedAt(Instant contractTemplateUpdatedAt) { - this.contractTemplateUpdatedAt = contractTemplateUpdatedAt; - } - - public String getContractTemplatePath() { - return contractTemplatePath; - } - - public void setContractTemplatePath(String contractTemplatePath) { - this.contractTemplatePath = contractTemplatePath; - } - - public String getContractTemplateVersion() { - return contractTemplateVersion; - } - - public void setContractTemplateVersion(String contractTemplateVersion) { - this.contractTemplateVersion = contractTemplateVersion; - } -} diff --git a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/ContractTemplate.java b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/ContractTemplate.java new file mode 100644 index 000000000..f5beb0cfc --- /dev/null +++ b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/ContractTemplate.java @@ -0,0 +1,23 @@ +package it.pagopa.selfcare.product.entity; + +public class ContractTemplate { + + private String contractTemplatePath; + private String contractTemplateVersion; + + public String getContractTemplatePath() { + return contractTemplatePath; + } + + public void setContractTemplatePath(String contractTemplatePath) { + this.contractTemplatePath = contractTemplatePath; + } + + public String getContractTemplateVersion() { + return contractTemplateVersion; + } + + public void setContractTemplateVersion(String contractTemplateVersion) { + this.contractTemplateVersion = contractTemplateVersion; + } +} diff --git a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/Product.java b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/Product.java index b8e52c3d0..f926b4374 100644 --- a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/Product.java +++ b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/entity/Product.java @@ -1,319 +1,350 @@ package it.pagopa.selfcare.product.entity; import it.pagopa.selfcare.onboarding.common.PartyRole; - import java.time.Instant; import java.util.*; +import org.apache.commons.lang3.StringUtils; public class Product { - private String id; - private String logo; - private String depictImageUrl; - private String title; - private String logoBgColor; - private String description; - private String urlPublic; - private String urlBO; - private Instant createdAt; - private String createdBy; - private Instant modifiedAt; - private String modifiedBy; - private Map roleMappings; - private Map> roleMappingsByInstitutionType; - private String roleManagementURL; - private Instant contractTemplateUpdatedAt; - private String contractTemplatePath; - private String contractTemplateVersion; - private Map institutionContractMappings; - private boolean enabled = true; - private boolean delegable; - private boolean invoiceable; - private ProductStatus status; - private String parentId; - private List testEnvProductIds; - private String identityTokenAudience; - private Map backOfficeEnvironmentConfigurations; - private Product parent; - private List consumers; - private String userContractTemplatePath; - private String userContractTemplateVersion; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getLogo() { - return logo; - } - - public void setLogo(String logo) { - this.logo = logo; - } - - public String getDepictImageUrl() { - return depictImageUrl; - } - - public void setDepictImageUrl(String depictImageUrl) { - this.depictImageUrl = depictImageUrl; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getLogoBgColor() { - return logoBgColor; - } - - public void setLogoBgColor(String logoBgColor) { - this.logoBgColor = logoBgColor; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getUrlPublic() { - return urlPublic; - } - - public void setUrlPublic(String urlPublic) { - this.urlPublic = urlPublic; - } - - public String getUrlBO() { - return urlBO; - } - - public void setUrlBO(String urlBO) { - this.urlBO = urlBO; - } - - public Instant getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Instant createdAt) { - this.createdAt = createdAt; - } - - public String getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(String createdBy) { - this.createdBy = createdBy; - } - - public Instant getModifiedAt() { - return modifiedAt; - } - - public void setModifiedAt(Instant modifiedAt) { - this.modifiedAt = modifiedAt; - } - - public String getModifiedBy() { - return modifiedBy; - } - - public void setModifiedBy(String modifiedBy) { - this.modifiedBy = modifiedBy; - } - - /** - * This method returns roleMappings associate with a specific InstitutionType on roleMappingsByInstitutionType map. - * In case none InstitutionType exists on roleMappingsByInstitutionType, it returns roleMappings. - * @param institutionType InstitutionType - * @return Map - */ - public Map getRoleMappings(String institutionType) { - if(Objects.nonNull(institutionType) && Objects.nonNull(roleMappingsByInstitutionType) - && roleMappingsByInstitutionType.containsKey(institutionType)){ - return roleMappingsByInstitutionType.get(institutionType); - } - return roleMappings; - } - - public Map> getAllRoleMappings() { - Map> roleInfoMap = new HashMap<>(); - Optional.ofNullable(roleMappings) - .ifPresent(roleMappings -> roleMappings.forEach((key, value) -> { - List productRoles = new ArrayList<>(); - productRoles.add(value); - roleInfoMap.put(key, productRoles); - })); - Optional.ofNullable(roleMappingsByInstitutionType) - .map(Map::values) - .ifPresent(items -> items.stream() - .map(Map::entrySet) - .forEach(item -> item.forEach(entry -> { - List productRoles = roleInfoMap.getOrDefault(entry.getKey(), new ArrayList<>()); - productRoles.add(entry.getValue()); - roleInfoMap.put(entry.getKey(), productRoles); - } ))); - return roleInfoMap; - } - - public void setRoleMappings(Map roleMappings) { - this.roleMappings = roleMappings; - } - - public void setRoleMappingsByInstitutionType(Map> roleMappingsByInstitutionType) { - this.roleMappingsByInstitutionType = roleMappingsByInstitutionType; - } - - public String getRoleManagementURL() { - return roleManagementURL; - } - - public void setRoleManagementURL(String roleManagementURL) { - this.roleManagementURL = roleManagementURL; - } - - public Instant getContractTemplateUpdatedAt() { - return contractTemplateUpdatedAt; - } - - public void setContractTemplateUpdatedAt(Instant contractTemplateUpdatedAt) { - this.contractTemplateUpdatedAt = contractTemplateUpdatedAt; - } - - public String getContractTemplatePath() { - return contractTemplatePath; - } - - public void setContractTemplatePath(String contractTemplatePath) { - this.contractTemplatePath = contractTemplatePath; - } - - public String getContractTemplateVersion() { - return contractTemplateVersion; - } - - public void setContractTemplateVersion(String contractTemplateVersion) { - this.contractTemplateVersion = contractTemplateVersion; - } - - public Map getInstitutionContractMappings() { - return institutionContractMappings; - } - - public void setInstitutionContractMappings(Map institutionContractMappings) { - this.institutionContractMappings = institutionContractMappings; - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public boolean isDelegable() { - return delegable; - } - - public void setDelegable(boolean delegable) { - this.delegable = delegable; - } - - public boolean isInvoiceable() { - return invoiceable; - } - - public void setInvoiceable(boolean invoiceable) { - this.invoiceable = invoiceable; - } - - public ProductStatus getStatus() { - return status; - } - - public void setStatus(ProductStatus status) { - this.status = status; - } - - public String getParentId() { - return parentId; - } - - public void setParentId(String parentId) { - this.parentId = parentId; - } - - public String getIdentityTokenAudience() { - return identityTokenAudience; - } - - public void setIdentityTokenAudience(String identityTokenAudience) { - this.identityTokenAudience = identityTokenAudience; - } - - public Map getBackOfficeEnvironmentConfigurations() { - return backOfficeEnvironmentConfigurations; - } - - public void setBackOfficeEnvironmentConfigurations(Map backOfficeEnvironmentConfigurations) { - this.backOfficeEnvironmentConfigurations = backOfficeEnvironmentConfigurations; - } - - public Product getParent() { - return parent; - } - - public void setParent(Product parent) { - this.parent = parent; - } - - public List getTestEnvProductIds() { - return testEnvProductIds; - } - - public void setTestEnvProductIds(List testEnvProductIds) { - this.testEnvProductIds = testEnvProductIds; - } - - public List getConsumers() { - return consumers; - } - - public void setConsumers(List consumers) { - this.consumers = consumers; - } - - public String getUserContractTemplatePath() { - return userContractTemplatePath; - } - - public void setUserContractTemplatePath(String userContractTemplatePath) { - this.userContractTemplatePath = userContractTemplatePath; - } - - public String getUserContractTemplateVersion() { - return userContractTemplateVersion; - } - - public void setUserContractTemplateVersion(String userContractTemplateVersion) { - this.userContractTemplateVersion = userContractTemplateVersion; - } - - public boolean canAddAdmin() { - return Objects.nonNull(userContractTemplateVersion); - } + public static final String CONTRACT_TYPE_DEFAULT = "default"; + private String id; + private String alias; + private String logo; + private String depictImageUrl; + private String title; + private String logoBgColor; + private String description; + private String urlPublic; + private String urlBO; + private Instant createdAt; + private String createdBy; + private Instant modifiedAt; + private String modifiedBy; + private Map roleMappings; + private Map> roleMappingsByInstitutionType; + private String roleManagementURL; + private boolean enabled = true; + private boolean delegable; + private boolean invoiceable; + private ProductStatus status; + private String parentId; + private List testEnvProductIds; + private String identityTokenAudience; + private Map backOfficeEnvironmentConfigurations; + private Product parent; + private List consumers; + private Map institutionContractMappings; + private Map userContractMappings; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLogo() { + return logo; + } + + public void setLogo(String logo) { + this.logo = logo; + } + + public String getDepictImageUrl() { + return depictImageUrl; + } + + public void setDepictImageUrl(String depictImageUrl) { + this.depictImageUrl = depictImageUrl; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getLogoBgColor() { + return logoBgColor; + } + + public void setLogoBgColor(String logoBgColor) { + this.logoBgColor = logoBgColor; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getUrlPublic() { + return urlPublic; + } + + public void setUrlPublic(String urlPublic) { + this.urlPublic = urlPublic; + } + + public String getUrlBO() { + return urlBO; + } + + public void setUrlBO(String urlBO) { + this.urlBO = urlBO; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public Instant getModifiedAt() { + return modifiedAt; + } + + public void setModifiedAt(Instant modifiedAt) { + this.modifiedAt = modifiedAt; + } + + public String getModifiedBy() { + return modifiedBy; + } + + public void setModifiedBy(String modifiedBy) { + this.modifiedBy = modifiedBy; + } + + /** + * This method returns roleMappings associate with a specific InstitutionType on + * roleMappingsByInstitutionType map. In case none InstitutionType exists on + * roleMappingsByInstitutionType, it returns roleMappings. + * + * @param institutionType InstitutionType + * @return Map + */ + public Map getRoleMappings(String institutionType) { + if (Objects.nonNull(institutionType) + && Objects.nonNull(roleMappingsByInstitutionType) + && roleMappingsByInstitutionType.containsKey(institutionType)) { + return roleMappingsByInstitutionType.get(institutionType); + } + return roleMappings; + } + + public Map> getAllRoleMappings() { + Map> roleInfoMap = new HashMap<>(); + Optional.ofNullable(roleMappings) + .ifPresent( + roleMappings -> + roleMappings.forEach( + (key, value) -> { + List productRoles = new ArrayList<>(); + productRoles.add(value); + roleInfoMap.put(key, productRoles); + })); + Optional.ofNullable(roleMappingsByInstitutionType) + .map(Map::values) + .ifPresent( + items -> + items.stream() + .map(Map::entrySet) + .forEach( + item -> + item.forEach( + entry -> { + List productRoles = + roleInfoMap.getOrDefault(entry.getKey(), new ArrayList<>()); + productRoles.add(entry.getValue()); + roleInfoMap.put(entry.getKey(), productRoles); + }))); + return roleInfoMap; + } + + public void setRoleMappings(Map roleMappings) { + this.roleMappings = roleMappings; + } + + public void setRoleMappingsByInstitutionType( + Map> roleMappingsByInstitutionType) { + this.roleMappingsByInstitutionType = roleMappingsByInstitutionType; + } + + public String getRoleManagementURL() { + return roleManagementURL; + } + + public void setRoleManagementURL(String roleManagementURL) { + this.roleManagementURL = roleManagementURL; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isDelegable() { + return delegable; + } + + public void setDelegable(boolean delegable) { + this.delegable = delegable; + } + + public boolean isInvoiceable() { + return invoiceable; + } + + public void setInvoiceable(boolean invoiceable) { + this.invoiceable = invoiceable; + } + + public ProductStatus getStatus() { + return status; + } + + public void setStatus(ProductStatus status) { + this.status = status; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public String getIdentityTokenAudience() { + return identityTokenAudience; + } + + public void setIdentityTokenAudience(String identityTokenAudience) { + this.identityTokenAudience = identityTokenAudience; + } + + public Map getBackOfficeEnvironmentConfigurations() { + return backOfficeEnvironmentConfigurations; + } + + public void setBackOfficeEnvironmentConfigurations( + Map backOfficeEnvironmentConfigurations) { + this.backOfficeEnvironmentConfigurations = backOfficeEnvironmentConfigurations; + } + + public Product getParent() { + return parent; + } + + public void setParent(Product parent) { + this.parent = parent; + } + + public List getTestEnvProductIds() { + return testEnvProductIds; + } + + public void setTestEnvProductIds(List testEnvProductIds) { + this.testEnvProductIds = testEnvProductIds; + } + + public List getConsumers() { + return consumers; + } + + public void setConsumers(List consumers) { + this.consumers = consumers; + } + + public boolean canAddAdmin() { + ContractTemplate userContractTemplate = getUserContractTemplate(CONTRACT_TYPE_DEFAULT); + return StringUtils.isNotEmpty(userContractTemplate.getContractTemplatePath()) + && StringUtils.isNotEmpty(userContractTemplate.getContractTemplateVersion()); + } + + public Map getInstitutionContractMappings() { + return institutionContractMappings; + } + + public void setInstitutionContractMappings( + Map institutionContractMappings) { + this.institutionContractMappings = institutionContractMappings; + } + + public Map getUserContractMappings() { + return userContractMappings; + } + + public void setUserContractMappings(Map userContractMappings) { + this.userContractMappings = userContractMappings; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + /** + * This method returns contractStorage associate with a specific InstitutionType. In case none + * InstitutionType exists on contractMapping, it returns a valid ContractTemplate. + * + * @param institutionType InstitutionType + * @return ContractTemplate + */ + public ContractTemplate getUserContractTemplate(String institutionType) { + ContractTemplate userContractTemplate = new ContractTemplate(); + if (Objects.nonNull(getUserContractMappings())) { + if (Objects.nonNull(institutionType) + && getUserContractMappings().containsKey(institutionType)) { + userContractTemplate = getUserContractMappings().get(institutionType); + } else if (getUserContractMappings().containsKey(CONTRACT_TYPE_DEFAULT)) { + userContractTemplate = getUserContractMappings().get(CONTRACT_TYPE_DEFAULT); + } + } + return userContractTemplate; + } + + /** + * This method returns contractStorage associate with a specific InstitutionType. In case none + * InstitutionType exists on contractMapping, it returns a valid ContractTemplate. + * + * @param institutionType InstitutionType + * @return ContractTemplate + */ + public ContractTemplate getInstitutionContractTemplate(String institutionType) { + ContractTemplate contractTemplate = new ContractTemplate(); + if (Objects.nonNull(getInstitutionContractMappings())) { + if (Objects.nonNull(institutionType) + && getInstitutionContractMappings().containsKey(institutionType)) { + contractTemplate = getInstitutionContractMappings().get(institutionType); + } else if (getInstitutionContractMappings().containsKey(CONTRACT_TYPE_DEFAULT)) { + contractTemplate = getInstitutionContractMappings().get(CONTRACT_TYPE_DEFAULT); + } + } + return contractTemplate; + } } diff --git a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductService.java b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductService.java index c34b09b6a..ef2059e8e 100644 --- a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductService.java +++ b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductService.java @@ -1,12 +1,10 @@ package it.pagopa.selfcare.product.service; -import it.pagopa.selfcare.onboarding.common.InstitutionType; import it.pagopa.selfcare.onboarding.common.PartyRole; import it.pagopa.selfcare.product.entity.Product; import it.pagopa.selfcare.product.entity.ProductRole; import it.pagopa.selfcare.product.entity.ProductRoleInfo; - import java.util.List; import java.util.Map; @@ -18,9 +16,7 @@ public interface ProductService { Product getProduct(String productId); Product getProductRaw(String productId); - - void fillContractTemplatePathAndVersion(Product product, InstitutionType institutionType); - + Product getProductIsValid(String productId); ProductRole validateProductRole(String productId, String productRole, PartyRole role); diff --git a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceCacheable.java b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceCacheable.java index fa09bbe20..378fb02fc 100644 --- a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceCacheable.java +++ b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceCacheable.java @@ -3,97 +3,89 @@ import com.fasterxml.jackson.core.JsonProcessingException; import it.pagopa.selfcare.azurestorage.AzureBlobClient; import it.pagopa.selfcare.azurestorage.AzureBlobClientDefault; -import it.pagopa.selfcare.onboarding.common.InstitutionType; import it.pagopa.selfcare.onboarding.common.PartyRole; import it.pagopa.selfcare.product.entity.Product; import it.pagopa.selfcare.product.entity.ProductRole; import it.pagopa.selfcare.product.entity.ProductRoleInfo; import it.pagopa.selfcare.product.exception.ProductNotFoundException; - import java.time.LocalDateTime; import java.util.List; import java.util.Map; -public class ProductServiceCacheable implements ProductService{ - LocalDateTime productLastModifiedDate; - private final AzureBlobClient azureBlobClient; - private ProductServiceDefault productService; - final String filePath; +public class ProductServiceCacheable implements ProductService { + protected LocalDateTime productLastModifiedDate; + private final AzureBlobClient azureBlobClient; + private ProductServiceDefault productService; + final String filePath; + public ProductServiceCacheable(String connectionString, String containerName, String filePath) { + this.azureBlobClient = new AzureBlobClientDefault(connectionString, containerName); + this.filePath = filePath; + refreshProduct(); + } - public ProductServiceCacheable(String connectionString, String containerName, String filePath ){ - this.azureBlobClient = new AzureBlobClientDefault(connectionString, containerName); - this.filePath = filePath; - refreshProduct(); - } + public ProductServiceCacheable(AzureBlobClient azureBlobClient, String filePath) { + this.azureBlobClient = azureBlobClient; + this.filePath = filePath; + refreshProduct(); + } - public ProductServiceCacheable(AzureBlobClient azureBlobClient, String filePath) { - this.azureBlobClient = azureBlobClient; - this.filePath = filePath; - refreshProduct(); + public void refreshProduct() { + LocalDateTime currentLastModifiedDate = + azureBlobClient.getProperties(filePath).getLastModified().toLocalDateTime(); + if (productLastModifiedDate == null + || currentLastModifiedDate.isAfter(productLastModifiedDate)) { + String productJsonString = azureBlobClient.getFileAsText(filePath); + try { + this.productService = new ProductServiceDefault(productJsonString); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(e.getMessage()); + } + this.productLastModifiedDate = currentLastModifiedDate; } + } - public void refreshProduct(){ - LocalDateTime currentLastModifiedDate = azureBlobClient.getProperties(filePath).getLastModified().toLocalDateTime(); - if(productLastModifiedDate == null || currentLastModifiedDate.isAfter(productLastModifiedDate)){ - String productJsonString = azureBlobClient.getFileAsText(filePath); - try{ - this.productService = new ProductServiceDefault(productJsonString); - }catch(JsonProcessingException e){ - throw new IllegalArgumentException(e.getMessage()); - } - this.productLastModifiedDate = currentLastModifiedDate; - } - } + @Override + public List getProducts(boolean rootOnly, boolean valid) { + refreshProduct(); + return productService.getProducts(rootOnly, valid); + } - @Override - public List getProducts(boolean rootOnly, boolean valid) { - refreshProduct(); - return productService.getProducts(rootOnly, valid); - } + @Override + public void validateRoleMappings(Map roleMappings) { + refreshProduct(); + productService.validateRoleMappings(roleMappings); + } - @Override - public void validateRoleMappings(Map roleMappings) { - refreshProduct(); - productService.validateRoleMappings(roleMappings); - } + @Override + public Product getProduct(String productId) { + refreshProduct(); + return productService.getProduct(productId); + } - @Override - public Product getProduct(String productId) { - refreshProduct(); - return productService.getProduct(productId); - } + /** + * Return a product present on the map by productId without any filter retrieving data from + * institutionContractMappings map + * + * @param productId String + * @return Product + * @throws IllegalArgumentException if @param id is null + * @throws ProductNotFoundException if product is not found + */ + @Override + public Product getProductRaw(String productId) { + return productService.getProduct(productId); + } + @Override + public Product getProductIsValid(String productId) { + refreshProduct(); + return productService.getProductIsValid(productId); + } - /** - * Return a product present on the map by productId without any filter - * retrieving data from institutionContractMappings map - * - * @param productId String - * @return Product - * @throws IllegalArgumentException if @param id is null - * @throws ProductNotFoundException if product is not found - */ - @Override - public Product getProductRaw(String productId) { - return productService.getProduct(productId); - } - - @Override - public void fillContractTemplatePathAndVersion(Product product, InstitutionType institutionType) { - refreshProduct(); - productService.fillContractTemplatePathAndVersion(product, institutionType); - } - - @Override - public Product getProductIsValid(String productId) { - refreshProduct(); - return productService.getProductIsValid(productId); - } - - @Override - public ProductRole validateProductRole(String productId, String productRole, PartyRole role) { - refreshProduct(); - return productService.validateProductRole(productId, productRole, role); - } + @Override + public ProductRole validateProductRole(String productId, String productRole, PartyRole role) { + refreshProduct(); + return productService.validateProductRole(productId, productRole, role); + } } diff --git a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceDefault.java b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceDefault.java index 650baab20..27ec31cef 100644 --- a/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceDefault.java +++ b/libs/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceDefault.java @@ -1,5 +1,7 @@ package it.pagopa.selfcare.product.service; +import static it.pagopa.selfcare.product.utils.ProductUtils.getProductRole; + import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -8,12 +10,10 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import it.pagopa.selfcare.onboarding.common.InstitutionType; import it.pagopa.selfcare.onboarding.common.PartyRole; import it.pagopa.selfcare.product.entity.*; import it.pagopa.selfcare.product.exception.InvalidRoleMappingException; import it.pagopa.selfcare.product.exception.ProductNotFoundException; - import java.util.List; import java.util.Map; import java.util.Objects; @@ -21,8 +21,6 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static it.pagopa.selfcare.product.utils.ProductUtils.getProductRole; - public class ProductServiceDefault implements ProductService { protected static final String REQUIRED_PRODUCT_ID_MESSAGE = "A product id is required"; @@ -151,24 +149,6 @@ private Product getProduct(String productId, boolean filterValid) { return product; } - /** - * Fills contractTemplatePath and ContractTemplateVersion based on @param institutionType. - * If institutionContractMappings contains institutionType, it take value from that setting inside - * contractTemplatePath and contractTemplateVersion of product - * - * @param product Product - * @param institutionType InstitutionType - */ - @Override - public void fillContractTemplatePathAndVersion(Product product, InstitutionType institutionType) { - if (Objects.nonNull(institutionType) && Objects.nonNull(product.getInstitutionContractMappings()) - && product.getInstitutionContractMappings().containsKey(institutionType.name())) { - product.setContractTemplatePath(product.getInstitutionContractMappings().get(institutionType.name()).getContractTemplatePath()); - product.setContractTemplateVersion(product.getInstitutionContractMappings().get(institutionType.name()).getContractTemplateVersion()); - } - } - - /** * Returns the information for a single product if it has not PHASE_OUT,INACTIVE status and its parent has not PHASE_OUT,INACTIVE status * diff --git a/libs/onboarding-sdk-product/src/test/java/it/pagopa/selfcare/product/entity/ProductTest.java b/libs/onboarding-sdk-product/src/test/java/it/pagopa/selfcare/product/entity/ProductTest.java index e3c9e6923..68a129af5 100644 --- a/libs/onboarding-sdk-product/src/test/java/it/pagopa/selfcare/product/entity/ProductTest.java +++ b/libs/onboarding-sdk-product/src/test/java/it/pagopa/selfcare/product/entity/ProductTest.java @@ -1,127 +1,280 @@ package it.pagopa.selfcare.product.entity; +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import it.pagopa.selfcare.onboarding.common.InstitutionType; import it.pagopa.selfcare.onboarding.common.PartyRole; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.*; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +public class ProductTest { -import static org.junit.jupiter.api.Assertions.*; + private static final Logger log = LoggerFactory.getLogger(ProductTest.class); -public class ProductTest { + ProductRoleInfo dummmyProductRoleInfo(PartyRole partyRole) { + ProductRoleInfo productRoleInfo = new ProductRoleInfo(); + ProductRole productRole = new ProductRole(); + productRole.setCode(partyRole.name()); + productRoleInfo.setRoles(List.of(productRole)); + return productRoleInfo; + } - ProductRoleInfo dummmyProductRoleInfo(PartyRole partyRole) { - ProductRoleInfo productRoleInfo = new ProductRoleInfo(); - ProductRole productRole = new ProductRole(); - productRole.setCode(partyRole.name()); - productRoleInfo.setRoles(List.of(productRole)); - return productRoleInfo; - } + @Test + @DisplayName("Test when only roleMappings is non-null") + public void testGetAllRoleMappings_OnlyRoleMappings() { + Map roleMappings = new HashMap<>(); + ProductRoleInfo manager = dummmyProductRoleInfo(PartyRole.MANAGER); + ProductRoleInfo operator = dummmyProductRoleInfo(PartyRole.OPERATOR); + roleMappings.put(PartyRole.MANAGER, manager); + roleMappings.put(PartyRole.OPERATOR, operator); - @Test - @DisplayName("Test when only roleMappings is non-null") - public void testGetAllRoleMappings_OnlyRoleMappings() { - Map roleMappings = new HashMap<>(); - ProductRoleInfo manager = dummmyProductRoleInfo(PartyRole.MANAGER); - ProductRoleInfo operator = dummmyProductRoleInfo(PartyRole.OPERATOR); - roleMappings.put(PartyRole.MANAGER, manager); - roleMappings.put(PartyRole.OPERATOR, operator); + Product product = new Product(); + product.setRoleMappings(roleMappings); + product.setRoleMappingsByInstitutionType(null); - Product product = new Product(); - product.setRoleMappings(roleMappings); - product.setRoleMappingsByInstitutionType(null); + Map> result = product.getAllRoleMappings(); - Map> result = product.getAllRoleMappings(); + assertEquals(2, result.size(), "Map should contain 2 keys"); + assertEquals(List.of(manager), result.get(PartyRole.MANAGER)); + assertEquals(List.of(operator), result.get(PartyRole.OPERATOR)); + } - assertEquals(2, result.size(), "Map should contain 2 keys"); - assertEquals(List.of(manager), result.get(PartyRole.MANAGER)); - assertEquals(List.of(operator), result.get(PartyRole.OPERATOR)); - } + @Test + @DisplayName("Test when only roleMappingsByInstitutionType is non-null") + public void testGetAllRoleMappings_OnlyRoleMappingsByInstitutionType() { + Map> roleMappingsByInstitutionType = new HashMap<>(); - @Test - @DisplayName("Test when only roleMappingsByInstitutionType is non-null") - public void testGetAllRoleMappings_OnlyRoleMappingsByInstitutionType() { - Map> roleMappingsByInstitutionType = new HashMap<>(); + Map institution1 = new HashMap<>(); + institution1.put(PartyRole.MANAGER, dummmyProductRoleInfo(PartyRole.MANAGER)); + institution1.put(PartyRole.OPERATOR, dummmyProductRoleInfo(PartyRole.OPERATOR)); - Map institution1 = new HashMap<>(); - institution1.put(PartyRole.MANAGER, dummmyProductRoleInfo(PartyRole.MANAGER)); - institution1.put(PartyRole.OPERATOR, dummmyProductRoleInfo(PartyRole.OPERATOR)); + Map institution2 = new HashMap<>(); + institution2.put(PartyRole.DELEGATE, dummmyProductRoleInfo(PartyRole.DELEGATE)); + institution2.put(PartyRole.OPERATOR, dummmyProductRoleInfo(PartyRole.OPERATOR)); - Map institution2 = new HashMap<>(); - institution2.put(PartyRole.DELEGATE, dummmyProductRoleInfo(PartyRole.DELEGATE)); - institution2.put(PartyRole.OPERATOR, dummmyProductRoleInfo(PartyRole.OPERATOR)); + roleMappingsByInstitutionType.put("Institution1", institution1); + roleMappingsByInstitutionType.put("Institution2", institution2); - roleMappingsByInstitutionType.put("Institution1", institution1); - roleMappingsByInstitutionType.put("Institution2", institution2); + Product product = new Product(); + product.setRoleMappings(null); + product.setRoleMappingsByInstitutionType(roleMappingsByInstitutionType); - Product product = new Product(); - product.setRoleMappings(null); - product.setRoleMappingsByInstitutionType(roleMappingsByInstitutionType); + Map> result = product.getAllRoleMappings(); - Map> result = product.getAllRoleMappings(); + assertEquals(3, result.size(), "Map should contain 3 keys"); + assertEquals(List.of(dummmyProductRoleInfo(PartyRole.MANAGER)), result.get(PartyRole.MANAGER)); + assertEquals( + List.of(dummmyProductRoleInfo(PartyRole.DELEGATE)), result.get(PartyRole.DELEGATE)); + assertEquals( + Arrays.asList( + dummmyProductRoleInfo(PartyRole.OPERATOR), dummmyProductRoleInfo(PartyRole.OPERATOR)), + result.get(PartyRole.OPERATOR)); + } - assertEquals(3, result.size(), "Map should contain 3 keys"); - assertEquals(List.of(dummmyProductRoleInfo(PartyRole.MANAGER)), result.get(PartyRole.MANAGER)); - assertEquals(List.of(dummmyProductRoleInfo(PartyRole.DELEGATE)), result.get(PartyRole.DELEGATE)); - assertEquals(Arrays.asList( - dummmyProductRoleInfo(PartyRole.OPERATOR), - dummmyProductRoleInfo(PartyRole.OPERATOR) - ), result.get(PartyRole.OPERATOR)); - } + @Test + @DisplayName("Test when both maps are non-null with overlapping keys") + public void testGetAllRoleMappings_BothMapsNonNullWithOverlap() { + // Setup roleMappings + Map roleMappings = new HashMap<>(); + roleMappings.put(PartyRole.MANAGER, dummmyProductRoleInfo(PartyRole.MANAGER)); + roleMappings.put(PartyRole.DELEGATE, dummmyProductRoleInfo(PartyRole.OPERATOR)); + + // Setup roleMappingsByInstitutionType + Map> roleMappingsByInstitutionType = new HashMap<>(); + + Map institution1 = new HashMap<>(); + institution1.put(PartyRole.MANAGER, dummmyProductRoleInfo(PartyRole.MANAGER)); + institution1.put(PartyRole.OPERATOR, dummmyProductRoleInfo(PartyRole.OPERATOR)); + + Map institution2 = new HashMap<>(); + institution2.put(PartyRole.DELEGATE, dummmyProductRoleInfo(PartyRole.DELEGATE)); + institution2.put(PartyRole.OPERATOR, dummmyProductRoleInfo(PartyRole.OPERATOR)); + + roleMappingsByInstitutionType.put("Institution1", institution1); + roleMappingsByInstitutionType.put("Institution2", institution2); + + Product product = new Product(); + product.setRoleMappings(roleMappings); + product.setRoleMappingsByInstitutionType(roleMappingsByInstitutionType); + + Map> result = product.getAllRoleMappings(); + + assertEquals(3, result.size(), "Map should contain 3 keys"); + + // Verify MANAGER + List adminList = result.get(PartyRole.MANAGER); + assertNotNull(adminList, "List for MANAGER should not be null"); + assertEquals(2, adminList.size(), "List for MANAGER should contain 2 elements"); + assertTrue(adminList.contains(dummmyProductRoleInfo(PartyRole.MANAGER))); + assertTrue(adminList.contains(dummmyProductRoleInfo(PartyRole.MANAGER))); + + // Verify DELEGATE + List userList = result.get(PartyRole.DELEGATE); + assertNotNull(userList, "List for DELEGATE should not be null"); + assertEquals(2, userList.size(), "List for DELEGATE should contain 2 elements"); + assertTrue(userList.contains(dummmyProductRoleInfo(PartyRole.DELEGATE))); + assertTrue(userList.contains(dummmyProductRoleInfo(PartyRole.DELEGATE))); + + // Verify OPERATOR + List guestList = result.get(PartyRole.OPERATOR); + assertNotNull(guestList, "List for OPERATOR should not be null"); + assertEquals(2, guestList.size(), "List for OPERATOR should contain 2 elements"); + assertTrue(guestList.contains(dummmyProductRoleInfo(PartyRole.OPERATOR))); + assertTrue(guestList.contains(dummmyProductRoleInfo(PartyRole.OPERATOR))); + } + + @Test + @DisplayName("Test for Institution when only institutionType is into the map") + public void getUserContractMappingsByKeyTest() { + + // given + InstitutionType institutionType = InstitutionType.PSP; + + ContractTemplate ContractTemplate = new ContractTemplate(); + ContractTemplate.setContractTemplatePath("test"); + ContractTemplate.setContractTemplateVersion("test-version"); + + Map mapTest = new HashMap<>(); + mapTest.put(institutionType.toString(), ContractTemplate); + + Product product = new Product(); + product.setUserContractMappings(mapTest); + + // when + ContractTemplate result = product.getUserContractTemplate(institutionType.toString()); + + // then + assertNotNull(result); + assertTrue(Objects.nonNull(result)); + assertTrue(StringUtils.isNotEmpty(result.getContractTemplatePath())); + assertTrue(StringUtils.isNotEmpty(result.getContractTemplateVersion())); + } + + @Test + @DisplayName("Test for User when only institutionType is not into the map") + public void getUserContractMappingsByKeyTest_KO() { + + // given + InstitutionType institutionType = InstitutionType.PSP; + + ContractTemplate ContractTemplate = new ContractTemplate(); + ContractTemplate.setContractTemplatePath("test"); + ContractTemplate.setContractTemplateVersion("test-version"); + + Map mapTest = new HashMap<>(); + mapTest.put(institutionType.toString(), ContractTemplate); + mapTest.put("default", ContractTemplate); + + Product product = new Product(); + product.setUserContractMappings(mapTest); + + // when + ContractTemplate result = product.getUserContractTemplate(InstitutionType.PRV.toString()); + + // then + assertNotNull(result); + assertTrue(StringUtils.isNotEmpty(result.getContractTemplatePath())); + assertTrue(StringUtils.isNotEmpty(result.getContractTemplateVersion())); + } + + @Test + @DisplayName("Test for Institution when only institutionType is into the map") + public void getInstitutionContractMappingsByKeyTest() { + + // given + InstitutionType institutionType = InstitutionType.PSP; + + ContractTemplate contractTemplate = new ContractTemplate(); + contractTemplate.setContractTemplatePath("test"); + contractTemplate.setContractTemplateVersion("test-version"); + + Map mapTest = new HashMap<>(); + mapTest.put(institutionType.toString(), contractTemplate); + + Product product = new Product(); + product.setInstitutionContractMappings(mapTest); + + // when + ContractTemplate result = product.getInstitutionContractTemplate(institutionType.toString()); + + // then + assertNotNull(result); + assertTrue(Objects.nonNull(result)); + assertTrue(StringUtils.isNotEmpty(result.getContractTemplatePath())); + assertTrue(StringUtils.isNotEmpty(result.getContractTemplateVersion())); + } + + @Test + @DisplayName("Test for Institution when only institutionType is not into the map") + public void getInstitutionContractMappingsByKeyTest_KO() { + + // given + InstitutionType institutionType = InstitutionType.PSP; + + ContractTemplate contractTemplate = new ContractTemplate(); + contractTemplate.setContractTemplatePath("test"); + contractTemplate.setContractTemplateVersion("test-version"); + + Map mapTest = new HashMap<>(); + mapTest.put(institutionType.toString(), contractTemplate); + mapTest.put("default", contractTemplate); + + Product product = new Product(); + product.setInstitutionContractMappings(mapTest); + + // when + ContractTemplate result = + product.getInstitutionContractTemplate(InstitutionType.PRV.toString()); + + // then + assertNotNull(result); + assertTrue(StringUtils.isNotEmpty(result.getContractTemplatePath())); + assertTrue(StringUtils.isNotEmpty(result.getContractTemplateVersion())); + } + + @Test + void productTest() { + // given + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + Product product = new Product(); + JsonNode jsonNode = null; + + // when + try { + product = objectMapper.readValue(new File("src/test/resources/product.json"), Product.class); + + FileInputStream fis = new FileInputStream("src/test/resources/product.json"); + String data = IOUtils.toString(fis, "UTF-8"); + + jsonNode = objectMapper.readTree(data); - @Test - @DisplayName("Test when both maps are non-null with overlapping keys") - public void testGetAllRoleMappings_BothMapsNonNullWithOverlap() { - // Setup roleMappings - Map roleMappings = new HashMap<>(); - roleMappings.put(PartyRole.MANAGER, dummmyProductRoleInfo(PartyRole.MANAGER)); - roleMappings.put(PartyRole.DELEGATE, dummmyProductRoleInfo(PartyRole.OPERATOR)); - - // Setup roleMappingsByInstitutionType - Map> roleMappingsByInstitutionType = new HashMap<>(); - - Map institution1 = new HashMap<>(); - institution1.put(PartyRole.MANAGER, dummmyProductRoleInfo(PartyRole.MANAGER)); - institution1.put(PartyRole.OPERATOR, dummmyProductRoleInfo(PartyRole.OPERATOR)); - - Map institution2 = new HashMap<>(); - institution2.put(PartyRole.DELEGATE, dummmyProductRoleInfo(PartyRole.DELEGATE)); - institution2.put(PartyRole.OPERATOR, dummmyProductRoleInfo(PartyRole.OPERATOR)); - - roleMappingsByInstitutionType.put("Institution1", institution1); - roleMappingsByInstitutionType.put("Institution2", institution2); - - Product product = new Product(); - product.setRoleMappings(roleMappings); - product.setRoleMappingsByInstitutionType(roleMappingsByInstitutionType); - - Map> result = product.getAllRoleMappings(); - - assertEquals(3, result.size(), "Map should contain 3 keys"); - - // Verify MANAGER - List adminList = result.get(PartyRole.MANAGER); - assertNotNull(adminList, "List for MANAGER should not be null"); - assertEquals(2, adminList.size(), "List for MANAGER should contain 2 elements"); - assertTrue(adminList.contains(dummmyProductRoleInfo(PartyRole.MANAGER))); - assertTrue(adminList.contains(dummmyProductRoleInfo(PartyRole.MANAGER))); - - // Verify DELEGATE - List userList = result.get(PartyRole.DELEGATE); - assertNotNull(userList, "List for DELEGATE should not be null"); - assertEquals(2, userList.size(), "List for DELEGATE should contain 2 elements"); - assertTrue(userList.contains(dummmyProductRoleInfo(PartyRole.DELEGATE))); - assertTrue(userList.contains(dummmyProductRoleInfo(PartyRole.DELEGATE))); - - // Verify OPERATOR - List guestList = result.get(PartyRole.OPERATOR); - assertNotNull(guestList, "List for OPERATOR should not be null"); - assertEquals(2, guestList.size(), "List for OPERATOR should contain 2 elements"); - assertTrue(guestList.contains(dummmyProductRoleInfo(PartyRole.OPERATOR))); - assertTrue(guestList.contains(dummmyProductRoleInfo(PartyRole.OPERATOR))); + } catch (IOException e) { + log.error("", e); } + // then + assertNotNull(jsonNode); + assertEquals(product.getAlias(), jsonNode.get("alias").asText()); + assertEquals(product.getId(), jsonNode.get("id").asText()); + assertEquals( + product.getInstitutionContractMappings().get("default").getContractTemplatePath(), + jsonNode + .get("institutionContractMappings") + .get("default") + .get("contractTemplatePath") + .asText()); + assertEquals(product.getStatus().toString(), jsonNode.get("status").asText()); + } } diff --git a/libs/onboarding-sdk-product/src/test/java/it/pagopa/selfcare/product/service/ProductServiceCacheableTest.java b/libs/onboarding-sdk-product/src/test/java/it/pagopa/selfcare/product/service/ProductServiceCacheableTest.java index 973c53bab..3d223d4d5 100644 --- a/libs/onboarding-sdk-product/src/test/java/it/pagopa/selfcare/product/service/ProductServiceCacheableTest.java +++ b/libs/onboarding-sdk-product/src/test/java/it/pagopa/selfcare/product/service/ProductServiceCacheableTest.java @@ -1,5 +1,9 @@ package it.pagopa.selfcare.product.service; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + import com.azure.storage.blob.models.BlobProperties; import it.pagopa.selfcare.azurestorage.AzureBlobClient; import it.pagopa.selfcare.onboarding.common.PartyRole; @@ -8,17 +12,12 @@ import it.pagopa.selfcare.product.entity.ProductRoleInfo; import it.pagopa.selfcare.product.exception.InvalidRoleMappingException; import it.pagopa.selfcare.product.exception.ProductNotFoundException; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - import java.time.OffsetDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; class ProductServiceCacheableTest { private static final String PRODUCT_JSON_STRING = "[{\"id\":\"prod-test-parent\",\"status\":\"ACTIVE\"}," + diff --git a/libs/onboarding-sdk-product/src/test/resources/product.json b/libs/onboarding-sdk-product/src/test/resources/product.json new file mode 100644 index 000000000..af6bfdc1e --- /dev/null +++ b/libs/onboarding-sdk-product/src/test/resources/product.json @@ -0,0 +1,56 @@ +{ + "alias": "prod-test", + "backOfficeEnvironmentConfigurations": { + "Locale": { + "identityTokenAudience": "locale", + "url": "test_url" + } + }, + "consumers": [ + "Standard" + ], + "createdAt": "2021-12-15T15:54:55.190Z", + "createdBy": "example", + "delegable": false, + "depictImageUrl": "test_url", + "description": "test_json", + "enabled": true, + "id": "prod-test", + "identityTokenAudience": "www.google.it", + "institutionContractMappings": { + "default": { + "contractTemplatePath": "path_template", + "contractTemplateVersion": "1.0.0" + } + }, + "invoiceable": true, + "logo": "test_url", + "logoBgColor": "#00000", + "modifiedAt": "2022-11-18T17:59:27.744Z", + "modifiedBy": "d0aaccddfdfdfdfdf418f01b130b", + "roleMappings": { + "OPERATOR": { + "multiroleAllowed": false, + "phasesAdditionAllowed": [ + "onboarding" + ], + "roles": [ + { + "code": "tes", + "description": "test", + "label": "test" + } + ] + } + }, + "status": "ACTIVE", + "title": "prova", + "urlBO": "test_url", + "urlPublic": "prova", + "userContractMappings": { + "default": { + "contractTemplatePath": "path_test", + "contractTemplateVersion": "1.0.0" + } + } +} \ No newline at end of file diff --git a/test-coverage/pom.xml b/test-coverage/pom.xml index dc7aec695..b42a7533a 100644 --- a/test-coverage/pom.xml +++ b/test-coverage/pom.xml @@ -77,22 +77,22 @@ it.pagopa.selfcare onboarding-sdk-product - 0.3.5 + 0.4.0 it.pagopa.selfcare onboarding-sdk-common - 0.3.5 + 0.4.0 it.pagopa.selfcare onboarding-sdk-azure-storage - 0.3.5 + 0.4.0 it.pagopa.selfcare onboarding-sdk-crypto - 0.3.5 + 0.4.0