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 14 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,129 @@
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 applicant1 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 (!validApplicant1ContactDetails(caseDataBefore, caseData)) {
pallavijustice marked this conversation as resolved.
Show resolved Hide resolved

return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.errors(singletonList("Please use the 'Update offline status' event before removing the email address."))
.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());

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

boolean shouldSendInviteToApp1 = shouldSendInvite(caseData);

if (!caseData.getApplicant1().isRepresented() && shouldSendInviteToApp1) {
log.info("Sending new invite to applicant/applicant1 for Case Id: {}", details.getId());
final CaseDetails<CaseData, State> result = emailUpdateService.processUpdateForApplicant1(details);
String newEmail = caseData.getApplicant1().getEmail();

if (caseDataBefore.getApplicant1().getEmail() != null
&& !caseDataBefore.getApplicant1().getEmail().isBlank()) {
emailUpdateService.sendNotificationToOldEmail(beforeDetails, newEmail, true);
}
pallavijustice marked this conversation as resolved.
Show resolved Hide resolved
return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.data(result.getData())
.build();
}

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

private boolean validApplicant1ContactDetails(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;
}
}
return true;
}

private boolean shouldSendInvite(CaseData caseData) {
if (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,133 @@
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";

@Override
public void configure(final ConfigBuilder<CaseData, State, UserRole> configBuilder) {
new PageBuilder(configBuilder
.event(CASEWORKER_UPDATE_APP2_EMAIL)
.forStates(POST_SUBMISSION_STATES)
.name("Update applicant2 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 (!validApplicant2ContactDetails(caseDataBefore, caseData)) {

return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.errors(singletonList("Please use the 'Update offline status' event before removing the email address."))
.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());

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

boolean shouldSendInviteToApp2 = shouldSendInvite(caseData);

if (!caseData.getApplicant2().isRepresented() && shouldSendInviteToApp2) {
log.info("Sending new invite to applicant2/respondent for Case Id: {}", details.getId());
final CaseDetails<CaseData, State> result = emailUpdateService.processUpdateForApplicant2(details);
String newEmail = caseData.getApplicant2().getEmail();

if (caseDataBefore.getApplicant2().getEmail() != null
&& !caseDataBefore.getApplicant2().getEmail().isBlank()) {
emailUpdateService.sendNotificationToOldEmail(beforeDetails, newEmail, false);
}
return AboutToStartOrSubmitResponse.<CaseData, State>builder()
.data(result.getData())
.build();
}

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

private boolean validApplicant2ContactDetails(CaseData caseDataBefore, CaseData caseData) {

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 shouldSendInvite(CaseData caseData) {
if (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank()) {
return false;
}

if (caseData.getApplicationType().isSole() && (caseData.getApplication().getIssueDate() == null)) {
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
Loading
Loading