Skip to content

Commit

Permalink
DIAC-546 - Revert DIAC-546 new event to pull notification data given …
Browse files Browse the repository at this point in the history
…a notification ID" (#2433)

* Revert "Revert "DIAC-546 Revert "DIAC-546 Revert "DIAC-546 new event to pull …"

This reverts commit ba09aac.

* DIAC-546 - update values preview
  • Loading branch information
KleoG authored Dec 5, 2024
1 parent ba09aac commit acc8964
Show file tree
Hide file tree
Showing 20 changed files with 1,104 additions and 398 deletions.
2 changes: 2 additions & 0 deletions Jenkinsfile_CNP
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ def secrets = [
secret('em-stitching-enabled', 'IA_EM_STITCHING_ENABLED'),
secret('submit-hearing-requirements-enabled', 'IA_SUBMIT_HEARING_REQUIREMENTS_ENABLED'),
secret('launch-darkly-sdk-key', 'LAUNCH_DARKLY_SDK_KEY'),
secret('ia-gov-notify-key', 'IA_GOV_NOTIFY_KEY'),
secret('ia-bail-gov-notify-key', 'IA_BAIL_GOV_NOTIFY_KEY'),
secret('app-insights-connection-string', 'app-insights-connection-string'),
secret('postgres-auth-values-password', 'PG_AUTH_VALUES_YAML_PASS'),
secret('generic-values-preview-password', 'GENERIC_VALUES_PREVIEW_YAML_PASS'),
Expand Down
2 changes: 2 additions & 0 deletions Jenkinsfile_nightly
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def secrets = [
secret('em-stitching-enabled', 'IA_EM_STITCHING_ENABLED'),
secret('submit-hearing-requirements-enabled', 'IA_SUBMIT_HEARING_REQUIREMENTS_ENABLED'),
secret('launch-darkly-sdk-key', 'LAUNCH_DARKLY_SDK_KEY'),
secret('ia-gov-notify-key', 'IA_GOV_NOTIFY_KEY'),
secret('ia-bail-gov-notify-key', 'IA_BAIL_GOV_NOTIFY_KEY'),
secret('app-insights-connection-string', 'app-insights-connection-string'),
secret('postgres-auth-values-password', 'PG_AUTH_VALUES_YAML_PASS'),
secret('generic-values-preview-password', 'GENERIC_VALUES_PREVIEW_YAML_PASS'),
Expand Down
2 changes: 2 additions & 0 deletions Jenkinsfile_parameterized
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def secrets = [
secret('s2s-microservice', 'IA_S2S_MICROSERVICE'),
secret('prof-ref-data-url', 'PROF_REF_DATA_URL'),
secret('launch-darkly-sdk-key', 'LAUNCH_DARKLY_SDK_KEY'),
secret('ia-gov-notify-key', 'IA_GOV_NOTIFY_KEY'),
secret('ia-bail-gov-notify-key', 'IA_BAIL_GOV_NOTIFY_KEY'),
secret('app-insights-connection-string', 'app-insights-connection-string'),
secret('postgres-auth-values-password', 'PG_AUTH_VALUES_YAML_PASS'),
secret('generic-values-preview-password', 'GENERIC_VALUES_PREVIEW_YAML_PASS'),
Expand Down
2 changes: 1 addition & 1 deletion charts/ia-case-api/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v2
name: ia-case-api
home: https://github.com/hmcts/ia-case-api
version: 0.0.55
version: 0.0.56
description: Immigration & Asylum Case API
maintainers:
- name: HMCTS Immigration & Asylum Team
Expand Down
14 changes: 10 additions & 4 deletions charts/ia-case-api/values.preview.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,16 @@ java:
FEES_REGISTER_API_URL: "http://fees-register-api-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal"
DUMMY: true
PAYMENT_REMINDER_DUE_IN_MINUTES: 1
IA_GOV_NOTIFY_KEY: ${IA_GOV_NOTIFY_KEY}
IA_BAIL_GOV_NOTIFY_KEY: ${IA_BAIL_GOV_NOTIFY_KEY}
keyVaults:
ia:
resourceGroup: ia
secrets:
- name: ia-gov-notify-key
alias: IA_GOV_NOTIFY_KEY
- name: ia-bail-gov-notify-key
alias: IA_BAIL_GOV_NOTIFY_KEY
- name: docmosis-enabled
alias: IA_DOCMOSIS_ENABLED
- name: em-stitching-enabled
Expand Down Expand Up @@ -200,7 +206,7 @@ ia-case-notifications-api:
enabled: true #set to true if testing against a local branch
java:
imagePullPolicy: Always
image: hmctspublic.azurecr.io/ia/case-notifications-api:latest
image: hmctspublic.azurecr.io/ia/case-notifications-api:pr-1343
releaseNameOverride: ${SERVICE_NAME}-case-notifications-api
ingressHost: ${SERVICE_NAME}-notifications-api.preview.platform.hmcts.net
devcpuRequests: 500m
Expand All @@ -209,8 +215,8 @@ ia-case-notifications-api:
devmemoryLimits: 4Gi
environment:
IA_HOME_OFFICE_GOV_NOTIFY_ENABLED: true
IA_GOV_NOTIFY_KEY: "test_key-7f72d0fb-2bc4-421b-bceb-1bf5bf350ff9-3df5a74b-f25b-4052-b00f-3f71d33cd0eb"
IA_BAIL_GOV_NOTIFY_KEY: "testkey-b9593914-99e1-484e-ad63-a51d7b4c164f-b817a2e5-7a30-4938-98f0-6099110c2879"
IA_GOV_NOTIFY_KEY: ${IA_GOV_NOTIFY_KEY}
IA_BAIL_GOV_NOTIFY_KEY: ${IA_BAIL_GOV_NOTIFY_KEY}
IA_EXUI_FRONTEND_URL: https://manage-case.{{ .Values.global.environment }}.platform.hmcts.net/
IA_AIP_FRONTEND_URL: https://immigration-appeal.{{ .Values.global.environment }}.platform.hmcts.net/
DM_URL: http://dm-store-aat.service.core-compute-aat.internal
Expand Down Expand Up @@ -451,7 +457,6 @@ ia-timed-event-service:
java:
imagePullPolicy: Always
image: hmctspublic.azurecr.io/ia/timed-event-service:latest
# image: hmctspublic.azurecr.io/ia/timed-event-service:pr-95
releaseNameOverride: ${SERVICE_NAME}-timed-event-service
ingressHost: ${SERVICE_NAME}-timed-event-service.preview.platform.hmcts.net
devcpuRequests: 500m
Expand Down Expand Up @@ -571,6 +576,7 @@ ia-aip-frontend:
nodejs:
imagePullPolicy: Always
image: hmctspublic.azurecr.io/ia/aip-frontend:latest
# image: hmctspublic.azurecr.io/ia/aip-frontend:pr-972
ingressHost: ${SERVICE_NAME}-aip-frontend.preview.platform.hmcts.net # override in public facing environments
applicationPort: 3000
devcpuRequests: 500m
Expand Down
6 changes: 6 additions & 0 deletions charts/ia-case-api/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ java:
PAYMENT_EA_HU_NO_REMISSION_DUE_IN_MINUTES: 20160
ADA_CASE_LISTED_DIRECTION_DUE_IN_DAYS: 15
PAYMENT_REMINDER_DUE_IN_MINUTES: 10080
IA_GOV_NOTIFY_KEY: ${IA_GOV_NOTIFY_KEY}
IA_BAIL_GOV_NOTIFY_KEY: ${IA_BAIL_GOV_NOTIFY_KEY}
postgresql:
auth:
username: ia_case_api
Expand All @@ -57,6 +59,10 @@ java:
ia:
resourceGroup: ia
secrets:
- name: ia-gov-notify-key
alias: IA_GOV_NOTIFY_KEY
- name: ia-bail-gov-notify-key
alias: IA_BAIL_GOV_NOTIFY_KEY
- name: docmosis-enabled
alias: IA_DOCMOSIS_ENABLED
- name: em-stitching-enabled
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ services:
IA_SUBMIT_HEARING_REQUIREMENTS_ENABLED:
CASE_DOCUMENT_AM_URL: http://ccd-case-document-am-api:4455
IA_HEARINGS_API_URL: http://ia-hearings-api:8100
IA_GOV_NOTIFY_KEY:
IA_BAIL_GOV_NOTIFY_KEY:
external_links:
- idam-api
- dm-store
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2735,6 +2735,9 @@ public enum AsylumCaseFieldDefinition {

IS_REMOTE_HEARING("isRemoteHearing", new TypeReference<YesOrNo>(){}),

NOTIFICATIONS("notifications", new TypeReference<List<IdValue<StoredNotification>>>(){}),
NOTIFICATIONS_SENT("notificationsSent", new TypeReference<List<IdValue<String>>>(){}),

REQUEST_FEE_REMISSION_DATE(
"requestFeeRemissionDate", new TypeReference<String>(){}),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package uk.gov.hmcts.reform.iacaseapi.domain.entities;

import lombok.*;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.ccd.field.Document;

@EqualsAndHashCode
@ToString
@Getter
@Builder
@AllArgsConstructor
public class StoredNotification {
@NonNull private String notificationId;
@NonNull private String notificationDateSent;
@NonNull private String notificationSentTo;
@NonNull private String notificationBody;
@Setter private Document notificationDocument;
@NonNull private String notificationMethod;
@NonNull private String notificationStatus;
@NonNull private String notificationReference;
@NonNull private String notificationSubject;
private String notificationErrorMessage;
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ public enum Event {
LIST_ASSIST_INTEGRATION("listAssistIntegration"),
TRIGGER_CMR_LISTED("triggerCmrListed"),
DECISION_WITHOUT_HEARING_LISTED("decisionWithoutHearingListed"),
SAVE_NOTIFICATIONS_TO_DATA("saveNotificationsToData"),

@JsonEnumDefaultValue
UNKNOWN("unknown");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ public boolean canHandle(
Event.REINSTATE_APPEAL,
Event.GENERATE_UPPER_TRIBUNAL_BUNDLE,
Event.MANAGE_FEE_UPDATE,
Event.UPDATE_TRIBUNAL_DECISION);
Event.UPDATE_TRIBUNAL_DECISION,
Event.SAVE_NOTIFICATIONS_TO_DATA);
if (isEmStitchingEnabled) {
allowedEvents.add(Event.SUBMIT_CASE);
if (!isSaveAndContinueEnabled) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package uk.gov.hmcts.reform.iacaseapi.domain.handlers.presubmit;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.AsylumCase;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.StoredNotification;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.ccd.Event;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.ccd.callback.Callback;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.ccd.callback.PreSubmitCallbackResponse;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.ccd.callback.PreSubmitCallbackStage;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.ccd.field.IdValue;
import uk.gov.hmcts.reform.iacaseapi.domain.handlers.PreSubmitCallbackHandler;
import uk.gov.hmcts.reform.iacaseapi.domain.service.Appender;
import uk.gov.service.notify.Notification;
import uk.gov.service.notify.NotificationClient;
import uk.gov.service.notify.NotificationClientException;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;

import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
import static uk.gov.hmcts.reform.iacaseapi.domain.entities.AsylumCaseFieldDefinition.NOTIFICATIONS;
import static uk.gov.hmcts.reform.iacaseapi.domain.entities.AsylumCaseFieldDefinition.NOTIFICATIONS_SENT;

@Slf4j
@Component
public class SaveNotificationsToDataHandler implements PreSubmitCallbackHandler<AsylumCase> {

private final NotificationClient notificationClient;
private final Appender<StoredNotification> notificationAppender;


public SaveNotificationsToDataHandler(
NotificationClient notificationClient,
Appender<StoredNotification> notificationAppender
) {
this.notificationClient = notificationClient;
this.notificationAppender = notificationAppender;
}

public boolean canHandle(
PreSubmitCallbackStage callbackStage,
Callback<AsylumCase> callback
) {
requireNonNull(callbackStage, "callbackStage must not be null");
requireNonNull(callback, "callback must not be null");

return callbackStage == PreSubmitCallbackStage.ABOUT_TO_SUBMIT
&& callback.getEvent() == Event.SAVE_NOTIFICATIONS_TO_DATA;
}

public PreSubmitCallbackResponse<AsylumCase> handle(
PreSubmitCallbackStage callbackStage,
Callback<AsylumCase> callback
) {
if (!canHandle(callbackStage, callback)) {
throw new IllegalStateException("Cannot handle callback");
}

AsylumCase asylumCase =
callback
.getCaseDetails()
.getCaseData();

Optional<List<IdValue<StoredNotification>>> maybeExistingNotifications =
asylumCase.read(NOTIFICATIONS);

Optional<List<IdValue<String>>> notificationsSent =
asylumCase.read(NOTIFICATIONS_SENT);

List<IdValue<StoredNotification>> allNotifications = maybeExistingNotifications.orElse(emptyList());
List<String> notificationIds = getUnstoredNotificationIds(allNotifications, notificationsSent.orElse(emptyList()));
if (!notificationIds.isEmpty()) {
for (String notificationId : notificationIds) {
try {
Notification notification = notificationClient.getNotificationById(notificationId);
StoredNotification storedNotification =
getStoredNotification(notificationId, notification);
allNotifications = notificationAppender.append(storedNotification, allNotifications);
} catch (NotificationClientException exception) {
log.warn("Notification client error on case "
+ callback.getCaseDetails().getId() + ": ", exception);
}
}
allNotifications = sortNotificationsByDate(allNotifications);
asylumCase.write(NOTIFICATIONS, allNotifications);
}

return new PreSubmitCallbackResponse<>(asylumCase);
}

private static StoredNotification getStoredNotification(String notificationId, Notification notification) {
String reference = notification.getReference().orElse(notificationId);
String notificationBody = "<div>" + notification.getBody()
.replace("\r\n", "<br>")
.replace("’", "'")
.replace("‘", "'")
+ "</div>";

String method = notification.getNotificationType();
String sentTo = switch (method) {
case "email" -> notification.getEmailAddress().orElse("N/A");
case "sms" -> notification.getPhoneNumber().orElse("N/A");
default -> "N/A";
};
String status = notification.getStatus();
List<String> failedStatus = List.of("permanent-failure", "temporary-failure", "technical-failure");
status = failedStatus.contains(status) ? "Failed" : StringUtils.capitalize(status);
ZonedDateTime zonedSentAt = notification.getSentAt().orElse(ZonedDateTime.now())
.withZoneSameInstant(ZoneId.of("Europe/London"));
String sentAt = zonedSentAt.toLocalDateTime().toString();
String subject = notification.getSubject().orElse("N/A");
return StoredNotification.builder()
.notificationId(notificationId)
.notificationDateSent(sentAt)
.notificationSentTo(sentTo)
.notificationBody(notificationBody)
.notificationMethod(StringUtils.capitalize(method))
.notificationStatus(status)
.notificationReference(reference)
.notificationSubject(subject)
.build();
}

private List<String> getUnstoredNotificationIds(List<IdValue<StoredNotification>> storedNotifications,
List<IdValue<String>> sentNotificationIds) {
List<String> storedNotificationIds = storedNotifications.stream()
.map(idValue -> idValue.getValue().getNotificationId())
.toList();
return sentNotificationIds.stream()
.filter(idValue -> !storedNotificationIds.contains(idValue.getValue()))
.map(IdValue::getValue)
.toList();
}

private List<IdValue<StoredNotification>> sortNotificationsByDate(List<IdValue<StoredNotification>> allNotifications) {
List<IdValue<StoredNotification>> mutableNotifications = new ArrayList<>(allNotifications);
mutableNotifications.sort(Comparator.comparing(notification ->
LocalDateTime.parse(notification.getValue().getNotificationDateSent()),
Comparator.reverseOrder()
));
return mutableNotifications;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,69 @@

import static java.util.Objects.requireNonNull;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import uk.gov.hmcts.reform.iacaseapi.domain.DateProvider;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.AsylumCase;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.ccd.Event;
import uk.gov.hmcts.reform.iacaseapi.domain.entities.ccd.callback.Callback;
import uk.gov.hmcts.reform.iacaseapi.domain.service.NotificationSender;
import uk.gov.hmcts.reform.iacaseapi.domain.service.Scheduler;
import uk.gov.hmcts.reform.iacaseapi.infrastructure.clients.model.TimedEvent;

import java.time.ZoneId;
import java.time.ZonedDateTime;

@Slf4j
@Service
public class AsylumCaseNotificationApiSender implements NotificationSender<AsylumCase> {

private final AsylumCaseCallbackApiDelegator asylumCaseCallbackApiDelegator;
private final String notificationsApiEndpoint;
private final String aboutToSubmitPath;
private final boolean timedEventServiceEnabled;
private final DateProvider dateProvider;
private final Scheduler scheduler;

public AsylumCaseNotificationApiSender(
AsylumCaseCallbackApiDelegator asylumCaseCallbackApiDelegator,
@Value("${notificationsApi.endpoint}") String notificationsApiEndpoint,
@Value("${notificationsApi.aboutToSubmitPath}") String aboutToSubmitPath
@Value("${notificationsApi.aboutToSubmitPath}") String aboutToSubmitPath,
@Value("${featureFlag.timedEventServiceEnabled}") boolean timedEventServiceEnabled,
DateProvider dateProvider,
Scheduler scheduler
) {
this.asylumCaseCallbackApiDelegator = asylumCaseCallbackApiDelegator;
this.notificationsApiEndpoint = notificationsApiEndpoint;
this.aboutToSubmitPath = aboutToSubmitPath;
this.timedEventServiceEnabled = timedEventServiceEnabled;
this.dateProvider = dateProvider;
this.scheduler = scheduler;
}

public AsylumCase send(
Callback<AsylumCase> callback
) {
requireNonNull(callback, "callback must not be null");

if (timedEventServiceEnabled) {
ZonedDateTime scheduledTime = ZonedDateTime.of(dateProvider.nowWithTime(), ZoneId.systemDefault())
.plusSeconds(15);
try {
scheduler.schedule(
new TimedEvent(
"",
Event.SAVE_NOTIFICATIONS_TO_DATA,
scheduledTime,
"IA",
"Asylum",
callback.getCaseDetails().getId()
)
);
} catch (AsylumCaseServiceResponseException e) {
log.error("Scheduling SAVE_NOTIFICATIONS_TO_DATA event failed: ", e);
}
}
return asylumCaseCallbackApiDelegator.delegate(
callback,
notificationsApiEndpoint + aboutToSubmitPath
Expand Down
Loading

0 comments on commit acc8964

Please sign in to comment.