Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -431,6 +431,8 @@ public class PCSCase {
)
private List<ListValue<AdditionalDocument>> additionalDocuments;

private List<ListValue<UploadedDocument>> uploadedAdditionalDocuments;

@CCD(
label = "Are you planning to make an application at the same time as your claim?",
hint = "After you’ve submitted your claim, there will be instructions on how to make an application"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public enum EventId {
createCaseLink,
maintainCaseLink,
dashboardView,
confirmEviction
confirmEviction,
uploadDocuments
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package uk.gov.hmcts.reform.pcs.ccd.event.citizen;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import uk.gov.hmcts.ccd.sdk.api.CCDConfig;
import uk.gov.hmcts.ccd.sdk.api.DecentralisedConfigBuilder;
import uk.gov.hmcts.ccd.sdk.api.EventPayload;
import uk.gov.hmcts.ccd.sdk.api.Permission;
import uk.gov.hmcts.ccd.sdk.api.callback.SubmitResponse;
import uk.gov.hmcts.reform.pcs.ccd.ShowConditions;
import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole;
import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase;
import uk.gov.hmcts.reform.pcs.ccd.domain.State;
import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity;
import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity;
import uk.gov.hmcts.reform.pcs.ccd.service.PcsCaseService;
import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentService;
import uk.gov.hmcts.reform.pcs.ccd.service.party.PartyService;
import uk.gov.hmcts.reform.pcs.security.SecurityContextService;

import java.util.UUID;

import static uk.gov.hmcts.reform.pcs.ccd.event.EventId.uploadDocuments;

@Slf4j
@Component
@AllArgsConstructor
public class UploadDocuments implements CCDConfig<PCSCase, State, UserRole> {

private final PcsCaseService pcsCaseService;
private final PartyService partyService;
private final SecurityContextService securityContextService;
private final DocumentService documentService;

@Override
public void configureDecentralised(DecentralisedConfigBuilder<PCSCase, State, UserRole> configBuilder) {
configBuilder
.decentralisedEvent(uploadDocuments.name(), this::submit)
.forStates(State.CASE_ISSUED)
.name("Upload additional documents")
.showCondition(ShowConditions.NEVER_SHOW)
.grant(Permission.CR, UserRole.DEFENDANT);
}

private SubmitResponse<State> submit(EventPayload<PCSCase, State> eventPayload) {
long caseReference = eventPayload.caseReference();
PCSCase caseData = eventPayload.caseData();

PcsCaseEntity pcsCaseEntity = pcsCaseService.loadCase(caseReference);
PartyEntity uploadingParty = getCurrentPartyEntity(caseReference);

documentService.createAdditionalDocumentsForParty(
caseData.getUploadedAdditionalDocuments(),
pcsCaseEntity,
uploadingParty
);

return SubmitResponse.<State>builder().build();
}

private PartyEntity getCurrentPartyEntity(long caseReference) {
UUID currentUserId = securityContextService.getCurrentUserId();
return partyService.getPartyEntityByIdamId(currentUserId, caseReference);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@AllArgsConstructor
Expand Down Expand Up @@ -180,6 +182,51 @@ private DocumentType mapAdditionalDocumentTypeToDocumentType(AdditionalDocumentT
};
}

public List<DocumentEntity> createAdditionalDocumentsForParty(
List<ListValue<UploadedDocument>> uploadedDocuments,
PcsCaseEntity pcsCase,
PartyEntity party
) {
if (CollectionUtils.isEmpty(uploadedDocuments)) {
log.info("No additional documents to save for case {}", pcsCase.getCaseReference());
return Collections.emptyList();
}

Set<String> existingUrls = pcsCase.getDocuments().stream()
.map(DocumentEntity::getUrl)
.filter(Objects::nonNull)
.collect(Collectors.toSet());

List<DocumentEntity> documentEntities = uploadedDocuments.stream()
.map(ListValue::getValue)
.filter(Objects::nonNull)
.filter(uploaded -> uploaded.getDocument() != null)
.filter(uploaded -> !existingUrls.contains(uploaded.getDocument().getUrl()))
.map(uploaded -> DocumentEntity.builder()
.pcsCase(pcsCase)
.party(party)
.url(uploaded.getDocument().getUrl())
.fileName(uploaded.getDocument().getFilename())
.displayFileName(uploaded.getDocument().getFilename())
.binaryUrl(uploaded.getDocument().getBinaryUrl())
.contentType(uploaded.getContentType())
.size(uploaded.getSizeInBytes())
.type(DocumentType.OTHER)
.build())
.toList();

if (documentEntities.isEmpty()) {
log.info("All additional documents for case {} already persisted; nothing to save",
pcsCase.getCaseReference());
return Collections.emptyList();
}

List<DocumentEntity> saved = documentRepository.saveAll(documentEntities);
log.info("Saved {} additional documents for case {} and party {}",
saved.size(), pcsCase.getCaseReference(), party.getId());
return saved;
}

public List<DocumentEntity> createDefendantUploadedDocuments(
List<ListValue<UploadedDocument>> defendantDocuments,
DefendantResponseEntity defendantResponse,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package uk.gov.hmcts.reform.pcs.ccd.event.citizen;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import uk.gov.hmcts.ccd.sdk.type.Document;
import uk.gov.hmcts.ccd.sdk.type.ListValue;
import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase;
import uk.gov.hmcts.reform.pcs.ccd.domain.UploadedDocument;
import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity;
import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity;
import uk.gov.hmcts.reform.pcs.ccd.event.BaseEventTest;
import uk.gov.hmcts.reform.pcs.ccd.service.PcsCaseService;
import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentService;
import uk.gov.hmcts.reform.pcs.ccd.service.party.PartyService;
import uk.gov.hmcts.reform.pcs.security.SecurityContextService;

import java.util.List;
import java.util.UUID;

import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

@ExtendWith(MockitoExtension.class)
class UploadDocumentsTest extends BaseEventTest {

@Mock
private PcsCaseService pcsCaseService;
@Mock
private PartyService partyService;
@Mock
private SecurityContextService securityContextService;
@Mock
private DocumentService documentService;

@BeforeEach
void setUp() {
UploadDocuments underTest = new UploadDocuments(pcsCaseService, partyService,
securityContextService, documentService);
setEventUnderTest(underTest);
}

@Nested
@DisplayName("Submit event tests")
class SubmitTests {

@Mock
private PcsCaseEntity pcsCaseEntity;

@BeforeEach
void setUp() {
given(pcsCaseService.loadCase(TEST_CASE_REFERENCE)).willReturn(pcsCaseEntity);
}

@Test
void shouldPersistUploadedDocumentsForCurrentParty() {
// Given
UploadedDocument uploaded = UploadedDocument.builder()
.document(Document.builder()
.url("url-1").filename("file-1.pdf").binaryUrl("bin-1").build())
.build();

List<ListValue<UploadedDocument>> uploadedDocs = List.of(
ListValue.<UploadedDocument>builder().id("1").value(uploaded).build()
);

PCSCase caseData = PCSCase.builder()
.uploadedAdditionalDocuments(uploadedDocs)
.build();

PartyEntity currentParty = stubCurrentUserParty();

// When
callSubmitHandler(caseData);

// Then
verify(documentService).createAdditionalDocumentsForParty(uploadedDocs, pcsCaseEntity, currentParty);
}

@Test
void shouldDelegateEvenWhenNoDocumentsSent() {
// Given
PCSCase caseData = PCSCase.builder().build();
PartyEntity currentParty = stubCurrentUserParty();

// When
callSubmitHandler(caseData);

// Then — service handles null/empty; event passes through
verify(documentService).createAdditionalDocumentsForParty(null, pcsCaseEntity, currentParty);
}

private PartyEntity stubCurrentUserParty() {
PartyEntity currentUserParty = mock(PartyEntity.class);
UUID currentUserId = UUID.randomUUID();
given(securityContextService.getCurrentUserId()).willReturn(currentUserId);
given(partyService.getPartyEntityByIdamId(currentUserId, TEST_CASE_REFERENCE))
.willReturn(currentUserParty);
return currentUserParty;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,124 @@ void shouldSaveDefendantEvidenceWithNullMetadata() {
assertThat(entities.getFirst().getSize()).isNull();
}

@Test
void shouldSaveAdditionalDocumentsForPartyAsOtherType() {
// Given
PcsCaseEntity pcsCase = mock(PcsCaseEntity.class);
PartyEntity party = mock(PartyEntity.class);
when(pcsCase.getDocuments()).thenReturn(new ArrayList<>());

UploadedDocument uploaded = UploadedDocument.builder()
.document(Document.builder()
.url("url-new").filename("file-new.pdf").binaryUrl("bin-new").build())
.contentType("application/pdf")
.sizeInBytes(123L)
.build();

List<ListValue<UploadedDocument>> uploadedDocs = List.of(
ListValue.<UploadedDocument>builder().id("1").value(uploaded).build()
);

when(documentRepository.saveAll(anyList())).thenAnswer(inv -> inv.getArgument(0));

// When
underTest.createAdditionalDocumentsForParty(uploadedDocs, pcsCase, party);

// Then
verify(documentRepository).saveAll(documentEntityListCaptor.capture());
List<DocumentEntity> entities = documentEntityListCaptor.getValue();
assertThat(entities).hasSize(1);
DocumentEntity entity = entities.getFirst();
assertThat(entity.getType()).isEqualTo(DocumentType.OTHER);
assertThat(entity.getCategoryId()).isNull();
assertThat(entity.getUrl()).isEqualTo("url-new");
assertThat(entity.getFileName()).isEqualTo("file-new.pdf");
assertThat(entity.getDisplayFileName()).isEqualTo("file-new.pdf");
assertThat(entity.getBinaryUrl()).isEqualTo("bin-new");
assertThat(entity.getContentType()).isEqualTo("application/pdf");
assertThat(entity.getSize()).isEqualTo(123L);
assertThat(entity.getPcsCase()).isSameAs(pcsCase);
assertThat(entity.getParty()).isSameAs(party);
}

@Test
void shouldSkipAdditionalDocumentsAlreadyPersistedByUrl() {
// Given
PcsCaseEntity pcsCase = mock(PcsCaseEntity.class);

DocumentEntity existing = DocumentEntity.builder().url("url-existing").build();
List<DocumentEntity> existingDocs = new ArrayList<>();
existingDocs.add(existing);
when(pcsCase.getDocuments()).thenReturn(existingDocs);

UploadedDocument duplicate = UploadedDocument.builder()
.document(Document.builder()
.url("url-existing").filename("dup.pdf").binaryUrl("bin-dup").build())
.build();
UploadedDocument fresh = UploadedDocument.builder()
.document(Document.builder()
.url("url-new").filename("new.pdf").binaryUrl("bin-new").build())
.build();

List<ListValue<UploadedDocument>> uploadedDocs = List.of(
ListValue.<UploadedDocument>builder().id("1").value(duplicate).build(),
ListValue.<UploadedDocument>builder().id("2").value(fresh).build()
);

when(documentRepository.saveAll(anyList())).thenAnswer(inv -> inv.getArgument(0));

PartyEntity party = mock(PartyEntity.class);

// When
underTest.createAdditionalDocumentsForParty(uploadedDocs, pcsCase, party);

// Then
verify(documentRepository).saveAll(documentEntityListCaptor.capture());
List<DocumentEntity> entities = documentEntityListCaptor.getValue();
assertThat(entities).hasSize(1);
assertThat(entities.getFirst().getUrl()).isEqualTo("url-new");
}

@Test
void shouldNotCallRepositoryWhenAllAdditionalDocumentsAreDuplicates() {
// Given
PcsCaseEntity pcsCase = mock(PcsCaseEntity.class);
PartyEntity party = mock(PartyEntity.class);

List<DocumentEntity> existingDocs = new ArrayList<>();
existingDocs.add(DocumentEntity.builder().url("url-existing").build());
when(pcsCase.getDocuments()).thenReturn(existingDocs);

UploadedDocument duplicate = UploadedDocument.builder()
.document(Document.builder()
.url("url-existing").filename("dup.pdf").binaryUrl("bin-dup").build())
.build();

List<ListValue<UploadedDocument>> uploadedDocs = List.of(
ListValue.<UploadedDocument>builder().id("1").value(duplicate).build()
);

// When
underTest.createAdditionalDocumentsForParty(uploadedDocs, pcsCase, party);

// Then
verify(documentRepository, never()).saveAll(anyList());
}

@Test
void shouldReturnEmptyListWhenAdditionalDocumentsInputIsNullOrEmpty() {
// Given
PcsCaseEntity pcsCase = mock(PcsCaseEntity.class);
PartyEntity party = mock(PartyEntity.class);

// When
underTest.createAdditionalDocumentsForParty(null, pcsCase, party);
underTest.createAdditionalDocumentsForParty(Collections.emptyList(), pcsCase, party);

// Then
verify(documentRepository, never()).saveAll(anyList());
}

private static Stream<Arguments> additionalDocumentCategoryScenarios() {
return Stream.of(
Arguments.of(
Expand Down
Loading