Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NFDIV-4356 Send case invite to applicant when email address is updated #4146

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
18a1324
NFDIV-4356 Initial changes
pallavijustice Oct 29, 2024
f8dea1d
Merge branch 'master' into NFDIV-4356-backend
pallavijustice Nov 6, 2024
5a9a14a
merge from master
pallavijustice Nov 7, 2024
22e6a88
NFDIV-4356 Send invite to unrepresented applicant when email address …
pallavijustice Nov 8, 2024
7dc4c7f
Merge remote-tracking branch 'origin/NFDIV-4356-backend' into NFDIV-4…
pallavijustice Nov 8, 2024
0f65be5
Merge branch 'master' into NFDIV-4356-backend
pallavijustice Nov 8, 2024
97d04ee
NFDIV-4356 Remove unwanted classes.
pallavijustice Nov 8, 2024
9e821c2
NFDIV-4356 Remove unwanted classes.
pallavijustice Nov 8, 2024
c9a2888
NFDIV-4356 Fix test issues
pallavijustice Nov 8, 2024
ccd0eda
Merge branch 'master' into NFDIV-4356-backend
pallavijustice Nov 8, 2024
73af960
NFDIV-4356 Remove email address update from Update Contact details pa…
pallavijustice Nov 11, 2024
1c57597
NFDIV-4356 Send case invite only when certain conditions are met.
pallavijustice Nov 13, 2024
71aab0c
NFDIV-4356 Fix checkstyle
pallavijustice Nov 13, 2024
ccb865c
Merge branch 'master' into NFDIV-4356-backend
pallavijustice Nov 13, 2024
97c94f1
Merge branch 'master' into NFDIV-4356-backend
pallavijustice Nov 15, 2024
3d685c7
NFDIV-4356 Incorporate review comments and additional changes request…
pallavijustice Nov 15, 2024
86bdff0
NFDIV-4356 Check for applicant represented
pallavijustice Nov 15, 2024
912f24d
NFDIV-4521 - Change message when removing email. Also add hint to scr…
pallavijustice Nov 18, 2024
eb24513
Merge branch 'master' into NFDIV-4356-backend
pallavijustice Nov 18, 2024
f65fb03
NFDIV-4356 - Moving message to a constant
pallavijustice Nov 19, 2024
bf0ee4a
NFDIV-4356 - Send invite even when citizen marked as offline.
pallavijustice Nov 19, 2024
c7c0c13
Merge branch 'master' into NFDIV-4356-backend
pallavijustice Nov 19, 2024
6142d11
Merge branch 'master' into NFDIV-4356-backend
pallavijustice Nov 19, 2024
8078524
Update EmailUpdateService.java
pallavijustice Nov 20, 2024
cb7b6d0
Merge branch 'NFDIV-4356-backend' of github.com:hmcts/nfdiv-case-api …
pallavijustice Nov 20, 2024
5c75b89
NFDIV-4356 - Merge from master
pallavijustice Dec 30, 2024
adc17f3
NFDIV-4356 - Merge from master
pallavijustice Dec 30, 2024
ffb7462
Merge branch 'master' into NFDIV-4356-backend
pallavijustice Dec 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
import uk.gov.hmcts.divorce.idam.User;
import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator;
import uk.gov.hmcts.reform.ccd.client.CaseAssignmentApi;
import uk.gov.hmcts.reform.ccd.client.model.CaseAssignmentUserRole;
import uk.gov.hmcts.reform.ccd.client.model.CaseAssignmentUserRoleWithOrganisation;
import uk.gov.hmcts.reform.ccd.client.model.CaseAssignmentUserRolesRequest;
import uk.gov.hmcts.reform.ccd.client.model.CaseAssignmentUserRolesResource;
import uk.gov.hmcts.reform.idam.client.models.UserInfo;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
Expand Down Expand Up @@ -84,8 +88,23 @@ void shouldRetryRemovingRolesThreeTimesWhenRemovingRolesThrowsException() {
@Test
void shouldRetryAddCaseRolesThreeTimesWhenAddingCaseRolesThrowsException() {
when(idamService.retrieveUser(CASEWORKER_AUTH_TOKEN)).thenReturn(caseworkerUser());
when(idamService.retrieveSystemUpdateUserDetails()).thenReturn(caseworkerUser());
when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN);

var response = CaseAssignmentUserRolesResource.builder()
.caseAssignmentUserRoles(List.of(
CaseAssignmentUserRole.builder().userId("1").caseRole("NOT_THIS_ONE").build(),
CaseAssignmentUserRole.builder().userId("2").caseRole("NOT_THIS_ONE").build()
))
.build();

when(caseAssignmentApi.getUserRoles(
eq(CASEWORKER_AUTH_TOKEN),
eq(TEST_SERVICE_AUTH_TOKEN),
anyList()
)
).thenReturn(response);

doThrow(feignException(500, "some error"))
.when(caseAssignmentApi).addCaseUserRoles(
CASEWORKER_AUTH_TOKEN,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package uk.gov.hmcts.divorce.caseworker.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import uk.gov.hmcts.ccd.sdk.api.CCDConfig;
import uk.gov.hmcts.ccd.sdk.api.CaseDetails;
import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder;
import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse;
import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService;
import uk.gov.hmcts.divorce.common.ccd.PageBuilder;
import uk.gov.hmcts.divorce.divorcecase.model.Applicant;
import uk.gov.hmcts.divorce.divorcecase.model.CaseData;
import uk.gov.hmcts.divorce.divorcecase.model.State;
import uk.gov.hmcts.divorce.divorcecase.model.UserRole;

import static java.util.Collections.singletonList;
import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES;
import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER;
import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR;
import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SOLICITOR;
import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER;
import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE;

@Component
@Slf4j
public class CaseworkerUpdateApplicant1Email implements CCDConfig<CaseData, State, UserRole> {

public static final String CASEWORKER_UPDATE_APP1_EMAIL = "caseworker-update-app1-email";

@Autowired
private EmailUpdateService emailUpdateService;

private static final String EMAIL_LABEL = "${%s} email address";
private static final String APPLICANTS_OR_APPLICANT1S = "labelContentApplicantsOrApplicant1s";

@Override
public void configure(final ConfigBuilder<CaseData, State, UserRole> configBuilder) {
new PageBuilder(configBuilder
.event(CASEWORKER_UPDATE_APP1_EMAIL)
.forStates(POST_SUBMISSION_STATES)
.name("Update App or App1 Email")
.description("Update applicant/applicant1 email")
.aboutToSubmitCallback(this::aboutToSubmit)
.showSummary()
.showEventNotes()
.grant(CREATE_READ_UPDATE_DELETE, SUPER_USER, CASE_WORKER)
.grantHistoryOnly(
SOLICITOR,
LEGAL_ADVISOR))
.page("updateApp1Email")
.pageLabel("Update applicant/applicant1 email")
.complex(CaseData::getApplicant1)
.optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, APPLICANTS_OR_APPLICANT1S))
.done();
}

public AboutToStartOrSubmitResponse<CaseData, State> midEvent(final CaseDetails<CaseData, State> details,
final CaseDetails<CaseData, State> detailsBefore) {
log.info("midEvent callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP1_EMAIL, details.getId());

CaseData caseData = details.getData();
CaseData caseDataBefore = detailsBefore.getData();

if (!validApplicant1Update(caseDataBefore, caseData)) {

return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.errors(singletonList("You cannot leave the email field blank. "
+ "You can only use this event to update the email of the party."))
.build();
}

return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.data(caseData)
.build();
}

public AboutToStartOrSubmitResponse<CaseData, State> aboutToSubmit(
final CaseDetails<CaseData, State> details,
final CaseDetails<CaseData, State> beforeDetails
) {
log.info("{} aboutToSubmit callback invoked for Case Id: {}", CASEWORKER_UPDATE_APP1_EMAIL, details.getId());

final CaseDetails<CaseData, State> result = emailUpdateService.processEmailUpdate(details, beforeDetails, true);

return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.data(result.getData())
.build();
}

private boolean validApplicant1Update(CaseData caseDataBefore, CaseData caseData) {

if (caseDataBefore.getApplicant1().getEmail() != null && !caseDataBefore.getApplicant1().getEmail().isBlank()
&& (caseData.getApplicant1().getEmail() == null || caseData.getApplicant1().getEmail().isBlank())) {
return false;
}
return true;
}

private String getLabel(final String label, final Object... value) {
return String.format(label, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package uk.gov.hmcts.divorce.caseworker.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import uk.gov.hmcts.ccd.sdk.api.CCDConfig;
import uk.gov.hmcts.ccd.sdk.api.CaseDetails;
import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder;
import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse;
import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService;
import uk.gov.hmcts.divorce.common.ccd.PageBuilder;
import uk.gov.hmcts.divorce.divorcecase.model.Applicant;
import uk.gov.hmcts.divorce.divorcecase.model.CaseData;
import uk.gov.hmcts.divorce.divorcecase.model.State;
import uk.gov.hmcts.divorce.divorcecase.model.UserRole;

import static java.util.Collections.singletonList;
import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES;
import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER;
import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR;
import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SOLICITOR;
import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER;
import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE;

@Component
@Slf4j
public class CaseworkerUpdateApplicant2Email implements CCDConfig<CaseData, State, UserRole> {

public static final String CASEWORKER_UPDATE_APP2_EMAIL = "caseworker-update-app2-email";

@Autowired
private EmailUpdateService emailUpdateService;

private static final String EMAIL_LABEL = "${%s} email address";
private static final String RESPONDENTS_OR_APPLICANT2S = "labelContentRespondentsOrApplicant2s";
private static final String NEVER_SHOW = "applicant2Email=\"NEVER_SHOW\"";

@Override
public void configure(final ConfigBuilder<CaseData, State, UserRole> configBuilder) {
new PageBuilder(configBuilder
.event(CASEWORKER_UPDATE_APP2_EMAIL)
.forStates(POST_SUBMISSION_STATES)
.name("Update Resp or App 2 Email")
.description("Update respondent/applicant2 email")
.aboutToSubmitCallback(this::aboutToSubmit)
.showSummary()
.showEventNotes()
.grant(CREATE_READ_UPDATE_DELETE, SUPER_USER, CASE_WORKER)
.grantHistoryOnly(
SOLICITOR,
LEGAL_ADVISOR))
.page("updateApp2Email")
.pageLabel("Update respondent/applicant2 email")
.complex(CaseData::getApplicant2)
.optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, RESPONDENTS_OR_APPLICANT2S))
.done();
}

public AboutToStartOrSubmitResponse<CaseData, State> midEvent(final CaseDetails<CaseData, State> details,
final CaseDetails<CaseData, State> detailsBefore) {
log.info("midEvent callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP2_EMAIL, details.getId());

CaseData caseData = details.getData();
CaseData caseDataBefore = detailsBefore.getData();

if (!validApplicant2Update(caseDataBefore, caseData)) {

return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.errors(singletonList("You cannot leave the email field blank. "
+ "You can only use this event to update the email of the party."))
.build();
}

return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.data(caseData)
.build();
}

public AboutToStartOrSubmitResponse<CaseData, State> aboutToSubmit(
final CaseDetails<CaseData, State> details,
final CaseDetails<CaseData, State> beforeDetails
) {
log.info("{} aboutToSubmit callback invoked for Case Id: {}", CASEWORKER_UPDATE_APP2_EMAIL, details.getId());

final CaseDetails<CaseData, State> result = emailUpdateService.processEmailUpdate(details, beforeDetails, false);

return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.data(result.getData())
.build();
}

private boolean validApplicant2Update(CaseData caseDataBefore, CaseData caseData) {

if (caseDataBefore.getApplicant2().getEmail() != null && !caseDataBefore.getApplicant2().getEmail().isBlank()
&& (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank())) {
return false;
}
return true;
}

private String getLabel(final String label, final Object... value) {
return String.format(label, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ private void buildApplicant1Fields(final FieldCollectionBuilder<CaseData, State,
.complex(CaseData::getApplicant1)
.optionalWithLabel(Applicant::getAddressOverseas, getLabel(ADDRESS_OVERSEAS_LABEL, APPLICANTS_OR_APPLICANT1S))
.optionalWithLabel(Applicant::getAddress, getLabel(ADDRESS_LABEL, APPLICANTS_OR_APPLICANT1S))
.optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, APPLICANTS_OR_APPLICANT1S))
.optionalWithLabel(Applicant::getPhoneNumber, getLabel(PHONE_LABEL, APPLICANTS_OR_APPLICANT1S))
.label("LabelHorizontalLine1", HORIZONTAL_RULE)
.done();
Expand All @@ -188,7 +187,6 @@ private void buildApplicant2Fields(final FieldCollectionBuilder<CaseData, State,
getLabel(CONTACT_TYPE_LABEL, RESPONDENTS_OR_APPLICANT2S, THE_APPLICANT_OR_APPLICANT1))
.optionalWithLabel(Applicant::getAddressOverseas, getLabel(ADDRESS_OVERSEAS_LABEL, RESPONDENTS_OR_APPLICANT2S))
.optionalWithLabel(Applicant::getAddress, getLabel(ADDRESS_LABEL, RESPONDENTS_OR_APPLICANT2S))
.optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, RESPONDENTS_OR_APPLICANT2S))
.optionalWithLabel(Applicant::getPhoneNumber, getLabel(PHONE_LABEL, RESPONDENTS_OR_APPLICANT2S))
.label("LabelHorizontalLine2", HORIZONTAL_RULE)
.done();
Expand Down Expand Up @@ -227,13 +225,6 @@ public AboutToStartOrSubmitResponse<CaseData, State> midEvent(final CaseDetails<
.build();
}

if (!validApplicantContactDetails(caseDataBefore, caseData)) {

return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.errors(singletonList("Please use the 'Update offline status' event before removing the email address."))
.build();
}

if (!isValidCombination(caseData)) {
final String errorMessage = String.format(
"""
Expand Down Expand Up @@ -300,27 +291,6 @@ private List<String> validateSolicitorDetailsNotRemoved(Solicitor solicitorBefor
return solicitorDetailRemovedErrors;
}

private boolean validApplicantContactDetails(CaseData caseDataBefore, CaseData caseData) {

if (caseDataBefore.getApplicant1().getEmail() != null && !caseDataBefore.getApplicant1().getEmail().isBlank()) {
if (!caseDataBefore.getApplicant1().isRepresented()
&& !caseData.getApplicant1().isApplicantOffline()
&& (caseData.getApplicant1().getEmail() == null || caseData.getApplicant1().getEmail().isBlank())) {
return false;
}
}

if (caseDataBefore.getApplicant2().getEmail() != null && !caseDataBefore.getApplicant2().getEmail().isBlank()) {
if (!caseDataBefore.getApplicant2().isRepresented()
&& !caseData.getApplicant2().isApplicantOffline()
&& (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank())) {
return false;
}
}

return true;
}

private boolean isValidCombination(final CaseData caseData) {

final Gender applicant1Gender = caseData.getApplicant1().getGender();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package uk.gov.hmcts.divorce.caseworker.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import uk.gov.hmcts.ccd.sdk.api.CaseDetails;
import uk.gov.hmcts.divorce.common.notification.EmailUpdatedNotification;
import uk.gov.hmcts.divorce.common.notification.InviteApplicantToCaseNotification;
import uk.gov.hmcts.divorce.divorcecase.model.Applicant;
import uk.gov.hmcts.divorce.divorcecase.model.CaseData;
import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite;
import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1;
import uk.gov.hmcts.divorce.divorcecase.model.State;
import uk.gov.hmcts.divorce.notification.NotificationDispatcher;

@Service
@Slf4j
public class EmailUpdateService {

@Autowired
InviteApplicantToCaseNotification inviteApplicantToCaseNotification;
@Autowired
EmailUpdatedNotification emailUpdatedNotification;
@Autowired
NotificationDispatcher notificationDispatcher;

public CaseDetails<CaseData, State> processEmailUpdate(final CaseDetails<CaseData, State> caseDetails,
final CaseDetails<CaseData, State> beforeCaseDetails,
boolean isApplicant1) {

final CaseData data = caseDetails.getData();

Applicant applicant = isApplicant1 ? data.getApplicant1() : data.getApplicant2();

if (applicant.getEmail() == null || applicant.getEmail().isBlank()
|| applicant.isRepresented()) {
return caseDetails;
}

//Do not send invite to respondent if sole application and case hasn't been issued
if (data.getApplicationType().isSole() && (data.getApplication().getIssueDate() == null) && !isApplicant1) {
return caseDetails;
}

createCaseInvite(data, isApplicant1);

sendInviteToApplicantEmail(data, caseDetails.getId(), isApplicant1);

sendNotificationToOldEmail(beforeCaseDetails, applicant.getEmail(), isApplicant1);

return caseDetails;
}

public void createCaseInvite(final CaseData data, boolean isApplicant1) {
Applicant applicant = isApplicant1 ? data.getApplicant1() : data.getApplicant2();
if (isApplicant1) {
CaseInviteApp1 invite = CaseInviteApp1.builder()
.applicant1InviteEmailAddress(applicant.getEmail())
.build()
.generateAccessCode();
data.setCaseInviteApp1(invite);
} else {
CaseInvite invite = CaseInvite.builder()
.applicant2InviteEmailAddress(applicant.getEmail())
.build()
.generateAccessCode();
data.setCaseInvite(invite);
}
}

public void sendInviteToApplicantEmail(final CaseData caseData, Long id, boolean isApplicant1) {
inviteApplicantToCaseNotification.send(caseData, id, isApplicant1);
}

public void sendNotificationToOldEmail(final CaseDetails<CaseData, State> caseDetails,
String newEmail, boolean isApplicant1) {
emailUpdatedNotification.send(caseDetails.getData(), caseDetails.getId(), newEmail, isApplicant1);
}
}
Loading
Loading