Skip to content

Commit

Permalink
[SELC-349] Enhanced onboarding flow with admin approval in FOR_APPROV…
Browse files Browse the repository at this point in the history
…E workflows (#90)

* TO_BE_VALIDATED status

* send confirmation when PT

* workflow approve when TO_BE_VALIDATED

* fix switch onboarding status

* unit test

* default properties

* next onboarding status

* TO_BE_VALIDATED also for PT

* unit test
  • Loading branch information
manuraf authored Jan 17, 2024
1 parent 848c5cc commit b2be39b
Show file tree
Hide file tree
Showing 13 changed files with 155 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public interface MailTemplatePathConfig {

String completePath();
String completePathFd();
String completePathPt();

String autocompletePath();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.microsoft.durabletask.azurefunctions.DurableClientInput;
import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger;
import it.pagopa.selfcare.onboarding.common.OnboardingStatus;
import it.pagopa.selfcare.onboarding.common.WorkflowType;
import it.pagopa.selfcare.onboarding.config.RetryPolicyConfig;
import it.pagopa.selfcare.onboarding.entity.Onboarding;
import it.pagopa.selfcare.onboarding.exception.ResourceNotFoundException;
Expand All @@ -39,6 +40,8 @@ public class OnboardingFunctions {
public static final String SAVE_TOKEN_WITH_CONTRACT_ACTIVITY_NAME = "SaveTokenWithContract";
public static final String BUILD_CONTRACT_ACTIVITY_NAME = "BuildContract";
public static final String SEND_MAIL_REGISTRATION_WITH_CONTRACT_ACTIVITY = "SendMailRegistrationWithContract";

public static final String SEND_MAIL_REGISTRATION_WITH_CONTRACT_WHEN_APPROVE_ACTIVITY = "SendMailRegistrationWithContractWhenApprove";
public static final String SEND_MAIL_REGISTRATION_REQUEST_ACTIVITY = "SendMailRegistrationRequest";
public static final String SEND_MAIL_REGISTRATION_APPROVE_ACTIVITY = "SendMailRegistrationApprove";
public static final String SEND_MAIL_ONBOARDING_APPROVE_ACTIVITY = "SendMailOnboardingApprove";
Expand Down Expand Up @@ -93,13 +96,15 @@ public void onboardingsOrchestrator(

switch (onboarding.getWorkflowType()) {
case CONTRACT_REGISTRATION -> workflowContractRegistration(ctx, onboardingString);
case FOR_APPROVE -> workflowForApprove(ctx, onboardingString);
case FOR_APPROVE -> workflowForApprove(ctx, onboardingString, onboarding.getStatus());
case FOR_APPROVE_PT -> workflowRegistrationRequestAndApprove(ctx, onboardingString);
case CONFIRMATION -> workflowForConfirmation(ctx, onboardingString);
}

//Last activity consist of saving pending status
String saveOnboardingStatusInput = SaveOnboardingStatusInput.buildAsJsonString(onboardingId, OnboardingStatus.PENDING.name());
OnboardingStatus nextStatus = nextProcessOnboardingStatus(onboarding.getStatus(), onboarding.getWorkflowType());
String saveOnboardingStatusInput = SaveOnboardingStatusInput.buildAsJsonString(onboardingId, nextStatus.name());

ctx.callActivity(SAVE_ONBOARDING_STATUS_ACTIVITY, saveOnboardingStatusInput, optionsRetry, String.class).await();
}

Expand All @@ -109,8 +114,14 @@ private void workflowContractRegistration(TaskOrchestrationContext ctx, String o
ctx.callActivity(SEND_MAIL_REGISTRATION_WITH_CONTRACT_ACTIVITY, onboardingString, optionsRetry, String.class).await();
}

private void workflowForApprove(TaskOrchestrationContext ctx, String onboardingString){
ctx.callActivity(SEND_MAIL_ONBOARDING_APPROVE_ACTIVITY, onboardingString, optionsRetry, String.class).await();
private void workflowForApprove(TaskOrchestrationContext ctx, String onboardingString, OnboardingStatus onboardingStatus){
if (OnboardingStatus.REQUEST.equals(onboardingStatus)) {
ctx.callActivity(SEND_MAIL_ONBOARDING_APPROVE_ACTIVITY, onboardingString, optionsRetry, String.class).await();
} else if (OnboardingStatus.TO_BE_VALIDATED.equals(onboardingStatus)) {
ctx.callActivity(BUILD_CONTRACT_ACTIVITY_NAME, onboardingString, optionsRetry, String.class).await();
ctx.callActivity(SAVE_TOKEN_WITH_CONTRACT_ACTIVITY_NAME, onboardingString, optionsRetry, String.class).await();
ctx.callActivity(SEND_MAIL_REGISTRATION_WITH_CONTRACT_WHEN_APPROVE_ACTIVITY, onboardingString, optionsRetry, String.class).await();
}
}

private void workflowRegistrationRequestAndApprove(TaskOrchestrationContext ctx, String onboardingString){
Expand Down Expand Up @@ -148,6 +159,11 @@ public void sendMailRegistrationWithContract(@DurableActivityTrigger(name = "onb
context.getLogger().info(String.format(FORMAT_LOGGER_ONBOARDING_STRING, SEND_MAIL_REGISTRATION_WITH_CONTRACT_ACTIVITY, onboardingString));
service.sendMailRegistrationWithContract(readOnboardingValue(objectMapper, onboardingString));
}
@FunctionName(SEND_MAIL_REGISTRATION_WITH_CONTRACT_WHEN_APPROVE_ACTIVITY)
public void sendMailRegistrationWithContractWhenApprove(@DurableActivityTrigger(name = "onboardingString") String onboardingString, final ExecutionContext context) {
context.getLogger().info(String.format(FORMAT_LOGGER_ONBOARDING_STRING, SEND_MAIL_REGISTRATION_WITH_CONTRACT_WHEN_APPROVE_ACTIVITY, onboardingString));
service.sendMailRegistrationWithContractWhenApprove(readOnboardingValue(objectMapper, onboardingString));
}

@FunctionName(SEND_MAIL_REGISTRATION_REQUEST_ACTIVITY)
public void sendMailRegistration(@DurableActivityTrigger(name = "onboardingString") String onboardingString, final ExecutionContext context) {
Expand Down Expand Up @@ -175,4 +191,13 @@ public String sendMailConfirmation(@DurableActivityTrigger(name = "onboardingStr
context.getLogger().info(String.format(FORMAT_LOGGER_ONBOARDING_STRING, SEND_MAIL_CONFIRMATION_ACTIVITY, onboardingString));
return onboardingString;
}

private OnboardingStatus nextProcessOnboardingStatus(OnboardingStatus previous, WorkflowType workflowType) {
if(OnboardingStatus.REQUEST.equals(previous) &&
(WorkflowType.FOR_APPROVE.equals(workflowType) || WorkflowType.FOR_APPROVE_PT.equals(workflowType))) {
return OnboardingStatus.TO_BE_VALIDATED;
}

return OnboardingStatus.PENDING;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ private boolean isGspAndProdInterop(InstitutionType institutionType, String prod
@Override
public void sendCompletedEmail(Onboarding onboarding) {

String workContractId = String.format("obg_%s", onboarding.getOnboardingId());
String workContractId = workContactsKey.apply(onboarding.getOnboardingId());

List<String> destinationMails = onboarding.getUsers().stream()
.filter(user -> MANAGER.equals(user.getRole()))
Expand All @@ -152,7 +152,7 @@ public void sendCompletedEmail(Onboarding onboarding) {

Product product = productService.getProductIsValid(onboarding.getProductId());

notificationService.sendCompletedEmail(destinationMails, product);
notificationService.sendCompletedEmail(destinationMails, product, onboarding.getInstitution().getInstitutionType());
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package it.pagopa.selfcare.onboarding.service;


import it.pagopa.selfcare.onboarding.common.InstitutionType;
import it.pagopa.selfcare.product.entity.Product;

import java.util.List;
Expand All @@ -15,5 +16,5 @@ public interface NotificationService {

void sendMailRegistrationWithContract(String onboardingId, String destination, String name, String username, String productName);

void sendCompletedEmail(List<String> destinationMails, Product product);
void sendCompletedEmail(List<String> destinationMails, Product product, InstitutionType institutionType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.quarkus.mailer.Mail;
import io.quarkus.mailer.Mailer;
import it.pagopa.selfcare.azurestorage.AzureBlobClient;
import it.pagopa.selfcare.onboarding.common.InstitutionType;
import it.pagopa.selfcare.onboarding.config.MailTemplatePathConfig;
import it.pagopa.selfcare.onboarding.config.MailTemplatePlaceholdersConfig;
import it.pagopa.selfcare.onboarding.entity.MailTemplate;
Expand Down Expand Up @@ -136,11 +137,17 @@ public void sendMailRegistrationWithContract(String onboardingId, String destina
}

@Override
public void sendCompletedEmail(List<String> destinationMails, Product product) {
public void sendCompletedEmail(List<String> destinationMails, Product product, InstitutionType institutionType) {

String templatePath = product.getId().equals(PROD_FD.getValue())||product.getId().equals(PROD_FD_GARANTITO.getValue())
? templatePathConfig.completePathFd()
: templatePathConfig.completePath();
String templatePath;

if(InstitutionType.PT.equals(institutionType)) {
templatePath = templatePathConfig.completePathPt();
} else {
templatePath =product.getId().equals(PROD_FD.getValue()) || product.getId().equals(PROD_FD_GARANTITO.getValue())
? templatePathConfig.completePathFd()
: templatePathConfig.completePath();
}

byte[] logoData = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ public void sendMailRegistrationWithContract(Onboarding onboarding) {
sendMailInput.product.getTitle());
}

public void sendMailRegistrationWithContractWhenApprove(Onboarding onboarding) {

SendMailInput sendMailInput = builderWithProductAndUserRequest(onboarding);

notificationService.sendMailRegistrationWithContract(onboarding.getOnboardingId(),
onboarding.getInstitution().getDigitalAddress(),
onboarding.getInstitution().getDescription(), "",
sendMailInput.product.getTitle());
}

public void sendMailRegistrationApprove(Onboarding onboarding) {

SendMailInput sendMailInput = builderWithProductAndUserRequest(onboarding);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ onboarding-functions.logo-path = ${PAGOPA_LOGO_URL:resources/logo.png}
## MAIL TEMPLATE
onboarding-functions.mail-template.path.onboarding.complete-path = ${MAIL_TEMPLATE_COMPLETE_PATH:contracts/template/mail/onboarding-complete/1.0.0.json}
onboarding-functions.mail-template.path.onboarding.complete-path-fd = ${MAIL_TEMPLATE_FD_COMPLETE_NOTIFICATION_PATH:contracts/template/mail/onboarding-complete-fd/1.0.0.json}
onboarding-functions.mail-template.path.onboarding.complete-path-pt = ${MAIL_TEMPLATE_PT_COMPLETE_PATH:contracts/template/mail/onboarding-complete-pt/1.0.0.json}

onboarding-functions.mail-template.path.onboarding.autocomplete-path = ${MAIL_TEMPLATE_AUTOCOMPLETE_PATH:default}
onboarding-functions.mail-template.path.onboarding.delegation-notification-path = ${MAIL_TEMPLATE_DELEGATION_NOTIFICATION_PATH:default}
onboarding-functions.mail-template.path.onboarding.registration-path = ${MAIL_TEMPLATE_REGISTRATION_PATH:contracts/template/mail/1.0.0.json}

onboarding-functions.mail-template.path.onboarding.onboarding-approve-path = ${MAIL_TEMPLATE_NOTIFICATION_PATH:default}
onboarding-functions.mail-template.path.onboarding.registration-request-path = ${MAIL_TEMPLATE_REGISTRATION_REQUEST_PT_PATH:default}
onboarding-functions.mail-template.path.onboarding.registration-approve-path = ${MAIL_TEMPLATE_REGISTRATION_NOTIFICATION_ADMIN_PATH:default}
onboarding-functions.mail-template.path.onboarding.registration-request-path = ${MAIL_TEMPLATE_REGISTRATION_REQUEST_PT_PATH:contracts/template/mail/registration-request-pt/1.0.0.json}
onboarding-functions.mail-template.path.onboarding.registration-approve-path = ${MAIL_TEMPLATE_REGISTRATION_NOTIFICATION_ADMIN_PATH:contracts/template/mail/registration-notification-admin/1.0.0.json}

onboarding-functions.mail-template.path.onboarding.reject-path = ${MAIL_TEMPLATE_REJECT_PATH:default}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.microsoft.durabletask.azurefunctions.DurableClientContext;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import it.pagopa.selfcare.onboarding.common.OnboardingStatus;
import it.pagopa.selfcare.onboarding.common.WorkflowType;
import it.pagopa.selfcare.onboarding.entity.Onboarding;
import it.pagopa.selfcare.onboarding.exception.ResourceNotFoundException;
Expand Down Expand Up @@ -130,6 +131,7 @@ void onboardingsOrchestratorContractRegistration() {
void onboardingsOrchestratorForApprove() {
Onboarding onboarding = new Onboarding();
onboarding.setOnboardingId("onboardingId");
onboarding.setStatus(OnboardingStatus.REQUEST);
onboarding.setWorkflowType(WorkflowType.FOR_APPROVE);

TaskOrchestrationContext orchestrationContext = mockTaskOrchestrationContext(onboarding);
Expand All @@ -143,6 +145,26 @@ void onboardingsOrchestratorForApprove() {
assertEquals(SAVE_ONBOARDING_STATUS_ACTIVITY, captorActivity.getAllValues().get(1));
}

@Test
void onboardingsOrchestratorForApproveWhenToBeValidated() {
Onboarding onboarding = new Onboarding();
onboarding.setOnboardingId("onboardingId");
onboarding.setStatus(OnboardingStatus.TO_BE_VALIDATED);
onboarding.setWorkflowType(WorkflowType.FOR_APPROVE);

TaskOrchestrationContext orchestrationContext = mockTaskOrchestrationContext(onboarding);

function.onboardingsOrchestrator(orchestrationContext);

ArgumentCaptor<String> captorActivity = ArgumentCaptor.forClass(String.class);
Mockito.verify(orchestrationContext, times(4))
.callActivity(captorActivity.capture(), any(), any(),any());
assertEquals(BUILD_CONTRACT_ACTIVITY_NAME, captorActivity.getAllValues().get(0));
assertEquals(SAVE_TOKEN_WITH_CONTRACT_ACTIVITY_NAME, captorActivity.getAllValues().get(1));
assertEquals(SEND_MAIL_REGISTRATION_WITH_CONTRACT_WHEN_APPROVE_ACTIVITY, captorActivity.getAllValues().get(2));
assertEquals(SAVE_ONBOARDING_STATUS_ACTIVITY, captorActivity.getAllValues().get(3));
}

@Test
void onboardingsOrchestratorConfirmation() {
Onboarding onboarding = new Onboarding();
Expand Down Expand Up @@ -260,4 +282,16 @@ void sendMailOnboardingApprove() {
Mockito.verify(service, times(1))
.sendMailOnboardingApprove(any());
}

@Test
void sendMailRegistrationWithContractWhenApprove() {
ExecutionContext executionContext = mock(ExecutionContext.class);
when(executionContext.getLogger()).thenReturn(Logger.getGlobal());
doNothing().when(service).sendMailRegistrationWithContractWhenApprove(any());

function.sendMailRegistrationWithContractWhenApprove(onboardinString, executionContext);

Mockito.verify(service, times(1))
.sendMailRegistrationWithContractWhenApprove(any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,12 @@ void sendCompletedEmail() {
.thenReturn(product);
when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, user.getId()))
.thenReturn(userResource);
doNothing().when(notificationService).sendCompletedEmail(any(), any());
doNothing().when(notificationService).sendCompletedEmail(any(), any(), any());

completionServiceDefault.sendCompletedEmail(onboarding);

Mockito.verify(notificationService, times(1))
.sendCompletedEmail(any(), any());
.sendCompletedEmail(any(), any(), any());
}

private InstitutionResponse dummyInstitutionResponse() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import it.pagopa.selfcare.azurestorage.AzureBlobClient;
import it.pagopa.selfcare.onboarding.common.InstitutionType;
import it.pagopa.selfcare.onboarding.config.MailTemplatePathConfig;
import it.pagopa.selfcare.onboarding.config.MailTemplatePlaceholdersConfig;
import it.pagopa.selfcare.product.entity.Product;
Expand Down Expand Up @@ -129,7 +130,7 @@ void sendCompletedEmail() {
.thenReturn(mailTemplate);
Mockito.doNothing().when(mailer).send(any());

notificationService.sendCompletedEmail(List.of(destination), product);
notificationService.sendCompletedEmail(List.of(destination), product, InstitutionType.PA);

Mockito.verify(azureBlobClient, Mockito.times(1))
.getFileAsText(any());
Expand Down Expand Up @@ -162,4 +163,26 @@ void sendMailRegistrationApprove() {
assertEquals(notificationAdminMail, mailArgumentCaptor.getValue().getTo().get(0));
}


@Test
void sendMailOnboardingApprove() {

final String mailTemplate = "{\"subject\":\"example\",\"body\":\"example\"}";
final String institutionName = "institutionName";

Mockito.when(azureBlobClient.getFileAsText(templatePathConfig.onboardingApprovePath()))
.thenReturn(mailTemplate);
Mockito.doNothing().when(mailer).send(any());

notificationService.sendMailOnboardingApprove(institutionName, "name","username","product","token");

Mockito.verify(azureBlobClient, Mockito.times(1))
.getFileAsText(any());

ArgumentCaptor<Mail> mailArgumentCaptor = ArgumentCaptor.forClass(Mail.class);
Mockito.verify(mailer, Mockito.times(1))
.send(mailArgumentCaptor.capture());
assertEquals(notificationAdminMail, mailArgumentCaptor.getValue().getTo().get(0));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ private Onboarding createOnboarding() {
onboarding.setOnboardingId(onboarding.getId().toHexString());
onboarding.setProductId(productId);
onboarding.setUsers(List.of());
onboarding.setInstitution(new Institution());
Institution institution = new Institution();
institution.setDescription("description");
onboarding.setInstitution(institution);
onboarding.setUserRequestUid("example-uid");
return onboarding;
}
Expand Down Expand Up @@ -240,6 +242,38 @@ void sendMailRegistrationWithContract() {
}


@Test
void sendMailRegistrationWithContractWhenApprove() {

Onboarding onboarding = createOnboarding();
Product product = createDummyProduct();
UserResource userResource = createUserResource();
Token token = new Token();
token.setId(ObjectId.get());

when(tokenRepository.findByOnboardingId(onboarding.getOnboardingId()))
.thenReturn(Optional.of(token));
when(productService.getProduct(onboarding.getProductId()))
.thenReturn(product);

when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST, onboarding.getUserRequestUid()))
.thenReturn(userResource);
doNothing().when(notificationService)
.sendMailRegistrationWithContract(onboarding.getOnboardingId(),
onboarding.getInstitution().getDigitalAddress(),
onboarding.getInstitution().getDescription(), "",
product.getTitle());

onboardingService.sendMailRegistrationWithContractWhenApprove(onboarding);

Mockito.verify(notificationService, times(1))
.sendMailRegistrationWithContract(onboarding.getOnboardingId(),
onboarding.getInstitution().getDigitalAddress(),
onboarding.getInstitution().getDescription(), "",
product.getTitle());
}


@Test
void sendMailRegistrationWithContract_throwExceptionWhenTokenIsNotPresent() {
Onboarding onboarding = createOnboarding();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public class OnboardingServiceDefault implements OnboardingService {

public static final String USERS_FIELD_LIST = "fiscalCode,familyName,name,workContacts";
public static final String USERS_FIELD_TAXCODE = "fiscalCode";
public static final String UNABLE_TO_COMPLETE_THE_ONBOARDING_FOR_INSTITUTION_ALREADY_ONBOARDED = "Unable to complete the onboarding for institution with taxCode '%s' to product '%s'.";
public static final String UNABLE_TO_COMPLETE_THE_ONBOARDING_FOR_INSTITUTION_ALREADY_ONBOARDED = "Unable to complete the onboarding for institution with taxCode '%s' to product '%s' because is already onboarded.";


public static final Function<String, String> workContactsKey = onboardingId -> String.format("obg_%s", onboardingId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package it.pagopa.selfcare.onboarding.common;
public enum OnboardingStatus {
REQUEST,
TO_BE_VALIDATED,
PENDING,
COMPLETED,
FAILED,
Expand Down

0 comments on commit b2be39b

Please sign in to comment.