Skip to content

Commit

Permalink
DIAC-546 converting notification body's to pdf (#939)
Browse files Browse the repository at this point in the history
* DIAC-546 temp commit to keep stuff saved

* DIAC-546 converting notification body to pdf and writing them in if valid and not already done

* DIAC-546 fixing sonar smells

* DIAC-546 fixing stored notification constructor and allowing event

* DIAC-546 fixing stored notification constructor and allowing event

* DIAC-546 upload potential fix

* DIAC-546 adding .PDF to end of filename

* DIAC-546 adding subject to the StoredNotification object and making it a builder

* DIAC-546 fixing tests

* DIAC-546 fixing tests

* DIAC-546 moving event access to admin and system

* DIAC-546 capitalising failed statuses

* DIAC-578 fix

* DIAC-546 - fix build failure

* DIAC-546 - fix unit test

---------

Co-authored-by: alivenichoppa <[email protected]>
Co-authored-by: KleoG <[email protected]>
Co-authored-by: kleog <[email protected]>
  • Loading branch information
4 people authored Oct 15, 2024
1 parent d967979 commit 1c2de41
Show file tree
Hide file tree
Showing 15 changed files with 599 additions and 40 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ dependencies {
compileOnly 'org.projectlombok:lombok:1.18.32'

testImplementation group: 'org.mockito', name: 'mockito-junit-jupiter', version: '3.12.4'
testImplementation group: 'org.mockito', name: 'mockito-inline', version: '3.4.0'
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
testImplementation group: 'org.springframework.security', name: 'spring-security-test', version: versions.springSecurity

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,8 @@ public enum AsylumCaseDefinition {
"customLatestRemittalDocs", new TypeReference<List<IdValue<DocumentWithDescription>>>(){}),

SOURCE_OF_REMITTAL(
"sourceOfRemittal", new TypeReference<String>(){}),;
"sourceOfRemittal", new TypeReference<String>(){}),
NOTIFICATIONS("notifications", new TypeReference<List<IdValue<StoredNotification>>>(){});

private final String value;
private final TypeReference typeReference;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities;

import lombok.*;
import uk.gov.hmcts.reform.iacasedocumentsapi.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 @@ -77,6 +77,7 @@ public enum Event {

UPDATE_HEARING_REQUIREMENTS("updateHearingRequirements", CaseType.ASYLUM),
UPDATE_HEARING_ADJUSTMENTS("updateHearingAdjustments", CaseType.ASYLUM),
SAVE_NOTIFICATIONS_TO_DATA("saveNotificationsToData", CaseType.ASYLUM),

@JsonEnumDefaultValue
UNKNOWN("unknown", CaseType.UNKNOWN);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package uk.gov.hmcts.reform.iacasedocumentsapi.domain.handlers.presubmit;

import org.springframework.stereotype.Component;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.AsylumCase;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.StoredNotification;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.CaseDetails;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.Event;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.callback.Callback;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.callback.PreSubmitCallbackResponse;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.callback.PreSubmitCallbackStage;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.field.Document;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.field.IdValue;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.handlers.PreSubmitCallbackHandler;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.service.SaveNotificationsToDataPdfService;

import java.util.ArrayList;
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.iacasedocumentsapi.domain.entities.AsylumCaseDefinition.NOTIFICATIONS;

@Component
public class SaveNotificationsToDataHandler implements PreSubmitCallbackHandler<AsylumCase> {

private final SaveNotificationsToDataPdfService saveNotificationsToDataPdfService;

public SaveNotificationsToDataHandler(
SaveNotificationsToDataPdfService saveNotificationsToDataPdfService
) {
this.saveNotificationsToDataPdfService = saveNotificationsToDataPdfService;
}

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");
}

final CaseDetails<AsylumCase> caseDetails = callback.getCaseDetails();
final AsylumCase asylumCase = caseDetails.getCaseData();

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

ArrayList<IdValue<StoredNotification>> newNotifications = new ArrayList<>();
List<String> invalidNotificationStatuses = List.of("Cancelled", "Failed", "Technical-failure",
"Temporary-failure", "Permanent-failure", "Validation-failed", "Virus-scan-failed");
for (IdValue<StoredNotification> notification : maybeExistingNotifications.orElse(emptyList())) {
StoredNotification storedNotification = notification.getValue();
if (storedNotification.getNotificationDocument() == null
&& !invalidNotificationStatuses.contains(storedNotification.getNotificationStatus())) {
String notificationBody = storedNotification.getNotificationBody();
String notificationReference = storedNotification.getNotificationReference();
Document notificationPdf =
saveNotificationsToDataPdfService.createPdf(notificationBody, notificationReference);
storedNotification.setNotificationDocument(notificationPdf);
notification = new IdValue<>(notification.getId(), storedNotification);
}
newNotifications.add(notification);
}
asylumCase.write(NOTIFICATIONS, newNotifications);

return new PreSubmitCallbackResponse<>(asylumCase);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,31 @@
import uk.gov.hmcts.reform.iacasedocumentsapi.infrastructure.clients.DocmosisDocumentConversionClient;

@Service
public class WordDocumentToPdfConverter {
public class DocumentToPdfConverter {

private final DocmosisDocumentConversionClient docmosisDocumentConversionClient;

public WordDocumentToPdfConverter(DocmosisDocumentConversionClient docmosisDocumentConversionClient) {
public DocumentToPdfConverter(DocmosisDocumentConversionClient docmosisDocumentConversionClient) {
this.docmosisDocumentConversionClient = docmosisDocumentConversionClient;
}

public File convertResourceToPdf(Resource resource) {
public File convertWordDocResourceToPdf(Resource resource) {
return convertResourceToPdf(resource, ".docx");
}

public File convertHtmlDocResourceToPdf(Resource resource) {
return convertResourceToPdf(resource, ".html");
}

private File convertResourceToPdf(Resource resource, String suffix) {

File tempPdfFile;

try (InputStream wordDocumentInputStream = resource.getInputStream()) {

File tempWordDocumentFile = createTempFile(
"tmp_",
".docx");
suffix);

Files.copy(
wordDocumentInputStream,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package uk.gov.hmcts.reform.iacasedocumentsapi.domain.service;

import org.apache.commons.io.FileUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.field.Document;

import java.io.File;
import java.io.IOException;

@Service
public class SaveNotificationsToDataPdfService {

private static final String PDF_CONTENT_TYPE = "application/pdf";

private final DocumentToPdfConverter documentToPdfConverter;
private final DocumentUploader documentUploader;

public SaveNotificationsToDataPdfService(
DocumentUploader documentUploader,
DocumentToPdfConverter documentToPdfConverter
) {
this.documentUploader = documentUploader;
this.documentToPdfConverter = documentToPdfConverter;
}

public Document createPdf(String notificationBody, String notificationReference) {

byte[] byteArray = notificationBody.getBytes();
Resource resource = new ByteArrayResource(byteArray);

File notificationPdf =
documentToPdfConverter.convertHtmlDocResourceToPdf(resource);

ByteArrayResource byteArrayResource = getByteArrayResource(
notificationPdf,
notificationReference + ".PDF"
);

return documentUploader.upload(byteArrayResource, PDF_CONTENT_TYPE);
}

private ByteArrayResource getByteArrayResource(File notificationPdf, String filename) {

byte[] byteArray;

try {
byteArray = FileUtils.readFileToByteArray(notificationPdf);

} catch (IOException e) {
throw new IllegalStateException("Error reading converted pdf");
}

return new ByteArrayResource(byteArray) {
@Override
public String getFilename() {
return filename;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.field.IdValue;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.field.PreviousDecisionDetails;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.service.DocumentUploader;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.service.WordDocumentToPdfConverter;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.service.DocumentToPdfConverter;
import uk.gov.hmcts.reform.iacasedocumentsapi.infrastructure.clients.DocumentDownloadClient;

import static uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.BailCaseFieldDefinition.PREVIOUS_DECISION_DETAILS;
Expand All @@ -29,18 +29,18 @@ public class UploadSignedDecisionPdfService {

private final DocumentDownloadClient documentDownloadClient;
private final DocumentUploader documentUploader;
private final WordDocumentToPdfConverter wordDocumentToPdfConverter;
private final DocumentToPdfConverter documentToPdfConverter;
private final String signedDecisionFinalPdfFilename;

public UploadSignedDecisionPdfService(
DocumentDownloadClient documentDownloadClient,
DocumentUploader documentUploader,
WordDocumentToPdfConverter wordDocumentToPdfConverter,
DocumentToPdfConverter documentToPdfConverter,
@Value("${decisionSignedDocumentFinalPdf.fileName}") String signedDecisionFinalPdfFilename
) {
this.documentDownloadClient = documentDownloadClient;
this.documentUploader = documentUploader;
this.wordDocumentToPdfConverter = wordDocumentToPdfConverter;
this.documentToPdfConverter = documentToPdfConverter;
this.signedDecisionFinalPdfFilename = signedDecisionFinalPdfFilename;
}

Expand All @@ -64,7 +64,7 @@ private Document createFinalPdf(BailCase bailCase) {
documentDownloadClient.download(signedDecisionDocument.getDocumentBinaryUrl());

File signedDecisionNoticePdf =
wordDocumentToPdfConverter.convertResourceToPdf(resource);
documentToPdfConverter.convertWordDocResourceToPdf(resource);

ByteArrayResource byteArrayResource = getByteArrayResource(
signedDecisionNoticePdf,
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ security:
- "buildCase"
- "caseListing"
- "asyncStitchingComplete"
- "saveNotificationsToData"
citizen:
- "startAppeal"
- "editAppeal"
Expand Down Expand Up @@ -634,6 +635,7 @@ security:
- "caseListing"
- "recordAdjournmentDetails"
- "requestHearingRequirementsFeature"
- "saveNotificationsToData"
caseworker-ia-homeofficelart:
- "uploadHomeOfficeAppealResponse"
- "uploadAdditionalEvidenceHomeOffice"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import uk.gov.hmcts.reform.iacasedocumentsapi.domain.entities.ccd.field.Document;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.mock;

class StoredNotificationTest {

private final String notificationId = "someId";
private final String notificationDateSent = "someDateSent";
private final String notificationSentTo = "someSentTo";
private final String notificationMethod = "someMethod";
private final String notificationStatus = "someStatus";
private final String notificationBody = "someBody";
private final String notificationReference = "someReference";
private final String notificationSubject = "someSubject";
private final Document document = mock(Document.class);

private StoredNotification storedNotification;

@BeforeEach
public void setUp() {
storedNotification =
StoredNotification.builder()
.notificationId(notificationId)
.notificationDateSent(notificationDateSent)
.notificationSentTo(notificationSentTo)
.notificationBody(notificationBody)
.notificationMethod(notificationMethod)
.notificationStatus(notificationStatus)
.notificationReference(notificationReference)
.notificationSubject(notificationSubject)
.build();
}

@Test
void should_hold_onto_values() {
assertThat(storedNotification.getNotificationId()).isEqualTo(notificationId);
assertThat(storedNotification.getNotificationMethod()).isEqualTo(notificationMethod);
assertThat(storedNotification.getNotificationSentTo()).isEqualTo(notificationSentTo);
assertThat(storedNotification.getNotificationStatus()).isEqualTo(notificationStatus);
assertThat(storedNotification.getNotificationBody()).isEqualTo(notificationBody);
assertThat(storedNotification.getNotificationDateSent()).isEqualTo(notificationDateSent);
assertThat(storedNotification.getNotificationReference()).isEqualTo(notificationReference);
assertThat(storedNotification.getNotificationSubject()).isEqualTo(notificationSubject);
assertThat(storedNotification.getNotificationDocument()).isNull();
storedNotification.setNotificationDocument(document);
assertThat(storedNotification.getNotificationDocument()).isEqualTo(document);
}

@Test
void should_not_allow_null_arguments_other_than_document() {

StoredNotification.StoredNotificationBuilder builder = StoredNotification.builder();
assertThatThrownBy(() -> builder.notificationId(null)).isExactlyInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> builder.notificationDateSent(null)).isExactlyInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> builder.notificationSentTo(null)).isExactlyInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> builder.notificationBody(null)).isExactlyInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> builder.notificationMethod(null)).isExactlyInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> builder.notificationStatus(null)).isExactlyInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> builder.notificationReference(null)).isExactlyInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> builder.notificationSubject(null)).isExactlyInstanceOf(NullPointerException.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,11 @@ public void has_correct_values() {
assertEquals("uploadAddendumEvidence", Event.UPLOAD_ADDENDUM_EVIDENCE.toString());
assertEquals("reinstateAppeal", Event.REINSTATE_APPEAL.toString());
assertEquals("updateHearingAdjustments", Event.UPDATE_HEARING_ADJUSTMENTS.toString());
assertEquals("saveNotificationsToData", Event.SAVE_NOTIFICATIONS_TO_DATA.toString());
}

@Test
public void if_this_test_fails_it_is_because_it_needs_updating_with_your_changes() {
assertEquals(69, Event.values().length);
assertEquals(70, Event.values().length);
}
}
Loading

0 comments on commit 1c2de41

Please sign in to comment.