Skip to content
Merged
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 @@ -10,6 +10,10 @@
},
"documentType": {
"type": "string"
},
"documentUri": {
"type": "string",
"format": "uri"
}
},
"required": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"id": "fc3f8d82-d200-4cab-a492-2796e6fdf42c",
"materialId": "87a321ef-ea34-47d7-865d-7f430b4775be",
"documentType": "SJPN"
"documentType": "SJPN",
"documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/sjpn.pdf"
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
},
"documentType": {
"type": "string"
},
"documentUri": {
"type": "string",
"format": "uri"
}
},
"required": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"caseId": "6ac98e9c-66b2-4363-8a78-641dbec8bbd2",
"id": "fc3f8d82-d200-4cab-a492-2796e6fdf42c",
"materialId": "87a321ef-ea34-47d7-865d-7f430b4775be",
"documentType": "SJPN"
"documentType": "SJPN",
"documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/sjpn.pdf"
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ private CaseDocument caseDocumentFrom(final JsonObject payload) {
UUID.fromString(payload.getString("id")),
UUID.fromString(payload.getString("materialId")),
payload.getString("documentType", null),
clock.now());
clock.now(),
payload.getString("documentUri", null));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,35 @@
@ServiceComponent(Component.COMMAND_HANDLER)
public class UploadCaseDocumentHandler extends CaseCommandHandler {

static final String CASE_DOCUMENT = "caseDocument";
static final String CASE_DOCUMENT_URI = "caseDocumentUri";

@Inject
private Clock clock;
@Handles("sjp.command.upload-case-document")
public void handle(JsonEnvelope command) throws EventStreamException {
JsonObject payload = command.payloadAsJsonObject();

final UUID caseId = getCaseId(payload);
final UUID caseDocumentReference = UUID.fromString(payload.getString("caseDocument"));
final String caseDocumentType = payload.getString("caseDocumentType");

// Exactly one of a file service id (caseDocument) or a blob uri (caseDocumentUri) is
// present - the command schema's oneOf enforces that, and JsonSchemaValidationInterceptor
// applies it on the way in, so this only has to pick whichever arrived.
final String caseDocumentReference = valueOrNull(payload, CASE_DOCUMENT);
final String caseDocumentUri = valueOrNull(payload, CASE_DOCUMENT_URI);

final UUID fileServiceId = caseDocumentReference == null ? null : UUID.fromString(caseDocumentReference);

if(isNull(getUserId(command))){
command = JsonEnvelope.envelopeFrom(metadataFrom(command.metadata()).withUserId(getUserIdFromCaseAggregate(caseId)),payload);
}
applyToCaseAggregate(command,
aCase -> aCase.uploadCaseDocument(caseId, caseDocumentReference, caseDocumentType)
aCase -> aCase.uploadCaseDocument(caseId, fileServiceId, caseDocumentUri, caseDocumentType)
);
}

private static String valueOrNull(final JsonObject payload, final String field) {
return payload.containsKey(field) && !payload.isNull(field) ? payload.getString(field) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
},
"documentType": {
"type": "string"
},
"documentUri": {
"type": "string",
"format": "uri"
}
},
"required": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"caseId": "6ac98e9c-66b2-4363-8a78-641dbec8bbd2",
"id": "fc3f8d82-d200-4cab-a492-2796e6fdf42c",
"materialId": "87a321ef-ea34-47d7-865d-7f430b4775be",
"documentType": "SJPN"
"documentType": "SJPN",
"documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/sjpn.pdf"
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public class AddCaseDocumentHandlerTest {
@InjectMocks
private AddCaseDocumentHandler addCaseDocumentHandler;

private static final String DOCUMENT_URI = "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/sjpn.pdf";

@Spy
private Clock clock = new UtcClock();

Expand Down Expand Up @@ -124,9 +126,10 @@ public void testAddCaseDocument_triggersCaseDocumentAddedEvent() throws Exceptio
public void testAddCaseDocument_whenCaseDocumentAlreadyExists_ReturnCaseDocumentAlreadyExistsEvent() throws Exception {

JsonEnvelope addCaseDocumentCommand = anAddCaseDocumentCommand()
.withDocumentUri(DOCUMENT_URI)
.build();
caseAggregate.receiveCase(CaseBuilder.aDefaultSjpCase().build(), clock.now());
caseAggregate.addCaseDocument(CASE_ID, aCaseDocument().build());
caseAggregate.addCaseDocument(CASE_ID, aCaseDocument().withDocumentUri(DOCUMENT_URI).build());

addCaseDocumentHandler.addCaseDocument(addCaseDocumentCommand);

Expand All @@ -135,7 +138,47 @@ public void testAddCaseDocument_whenCaseDocumentAlreadyExists_ReturnCaseDocument
jsonEnvelope(
withMetadataEnvelopedFrom(addCaseDocumentCommand)
.withName("sjp.events.case-document-addition-failed"),
payloadIsJson(withJsonPath("$.documentId", is(CASE_DOCUMENT_ID_STR)))))));
payloadIsJson(allOf(
withJsonPath("$.caseId", is(CASE_ID_STR)),
withJsonPath("$.documentId", is(CASE_DOCUMENT_ID_STR)),
withJsonPath("$.documentUri", is(DOCUMENT_URI))))))));
}

@Test
public void testAddCaseDocument_whenBlobAddressed_carriesTheUriOnTheAddedEvent() throws Exception {
final JsonEnvelope addCaseDocumentCommand = anAddCaseDocumentCommand()
.withDocumentUri(DOCUMENT_URI)
.build();
caseAggregate.receiveCase(CaseBuilder.aDefaultSjpCase().build(), clock.now());

addCaseDocumentHandler.addCaseDocument(addCaseDocumentCommand);

assertThat(eventStream, eventStreamAppendedWith(
streamContaining(
jsonEnvelope(
withMetadataEnvelopedFrom(addCaseDocumentCommand)
.withName("sjp.events.case-document-added"),
payloadIsJson(allOf(
withJsonPath("$.caseId", is(CASE_ID_STR)),
withJsonPath("$.caseDocument.id", is(CASE_DOCUMENT_ID_STR)),
withJsonPath("$.caseDocument.documentUri", is(DOCUMENT_URI))
))))));
}

@Test
public void testAddCaseDocument_whenFileServiceAddressed_omitsTheUriEntirely() throws Exception {
final JsonEnvelope addCaseDocumentCommand = anAddCaseDocumentCommand().build();
caseAggregate.receiveCase(CaseBuilder.aDefaultSjpCase().build(), clock.now());

addCaseDocumentHandler.addCaseDocument(addCaseDocumentCommand);

// Absent rather than null: the framework serialises with NON_ABSENT.
assertThat(eventStream, eventStreamAppendedWith(
streamContaining(
jsonEnvelope(
withMetadataEnvelopedFrom(addCaseDocumentCommand)
.withName("sjp.events.case-document-added"),
payloadIsJson(withoutJsonPath("$.caseDocument.documentUri"))))));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import uk.gov.moj.cpp.sjp.event.CaseStarted;

import java.util.UUID;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

import javax.json.JsonObjectBuilder;
Expand All @@ -48,6 +49,7 @@ public class UploadCaseDocumentHandlerTest {

private static final String CASE_ID_PROPERTY = "caseId";
private static final String CASE_DOCUMENT_REFERENCE_PROPERTY = "caseDocument";
private static final String CASE_DOCUMENT_URI_PROPERTY = "caseDocumentUri";
private static final String CASE_DOCUMENT_TYPE_PROPERTY = "caseDocumentType";

@Mock
Expand Down Expand Up @@ -81,6 +83,7 @@ public class UploadCaseDocumentHandlerTest {
private static final UUID CASE_ID = randomUUID();
private static final UUID DOCUMENT_REFERENCE = randomUUID();
private static final String DOCUMENT_TYPE = "PLEA";
private static final String DOCUMENT_URI = "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/doc.pdf";


@BeforeEach
Expand Down Expand Up @@ -124,8 +127,8 @@ public void shouldRejectUploadOfCaseDocumentWhenCaseIsInReferredToCourtState() t
when(caseAggregate1.getState()).thenReturn(state);
when(state.getMetadataUserId()).thenReturn(randomUUID());

final CaseDocumentUploadRejected caseDocumentUploadRejected = new CaseDocumentUploadRejected(DOCUMENT_REFERENCE, "");
when(caseAggregate1.uploadCaseDocument(CASE_ID, DOCUMENT_REFERENCE, DOCUMENT_TYPE)).thenReturn(Stream.of(caseDocumentUploadRejected));
final CaseDocumentUploadRejected caseDocumentUploadRejected = new CaseDocumentUploadRejected(DOCUMENT_REFERENCE, null, "");
when(caseAggregate1.uploadCaseDocument(CASE_ID, DOCUMENT_REFERENCE, null, DOCUMENT_TYPE)).thenReturn(Stream.of(caseDocumentUploadRejected));

uploadCaseDocumentHandler.handle(command);

Expand All @@ -143,6 +146,29 @@ public void shouldRejectUploadOfCaseDocumentWhenCaseIsInReferredToCourtState() t
}


@Test
public void shouldRaiseUploadedEventCarryingTheUriWhenDocumentIsBlobAddressed() throws EventStreamException {
final JsonEnvelope command = createCommand(payload -> payload.add(CASE_DOCUMENT_URI_PROPERTY, DOCUMENT_URI));
when(eventSource.getStreamById(CASE_ID)).thenReturn(eventStream);
when(aggregateService.get(eventStream, CaseAggregate.class)).thenReturn(caseAggregate);

uploadCaseDocumentHandler.handle(command);

assertThat(eventStream, eventStreamAppendedWith(
streamContaining(
jsonEnvelope(
withMetadataEnvelopedFrom(command)
.withName("sjp.events.case-document-uploaded"),
payloadIsJson(allOf(
withJsonPath("$.caseId", equalTo(CASE_ID.toString())),
withJsonPath("$.documentReferenceUri", equalTo(DOCUMENT_URI)),
withJsonPath("$.documentType", equalTo(DOCUMENT_TYPE))
)))
)));
}



private JsonEnvelope createCaseDocumentUploadCommand(final UUID caseId, final UUID caseDocumentReference, final String documentType) {
final JsonObjectBuilder payload = createObjectBuilder()
.add(CASE_ID_PROPERTY, caseId.toString())
Expand All @@ -153,4 +179,14 @@ private JsonEnvelope createCaseDocumentUploadCommand(final UUID caseId, final UU
metadataOf(randomUUID(), "sjp.command.upload-case-document"),
payload.build());
}

private JsonEnvelope createCommand(final UnaryOperator<JsonObjectBuilder> withReference) {
final JsonObjectBuilder payload = createObjectBuilder()
.add(CASE_ID_PROPERTY, CASE_ID.toString())
.add(CASE_DOCUMENT_TYPE_PROPERTY, DOCUMENT_TYPE);

return envelopeFrom(
metadataOf(randomUUID(), "sjp.command.upload-case-document"),
withReference.apply(payload).build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class AddCaseDocumentCommandBuilder {
private UUID id = CASE_DOCUMENT_ID;
private UUID materialId = CASE_DOCUMENT_MATERIAL_ID;
private String documentType;
private String documentUri;

private AddCaseDocumentCommandBuilder() {
}
Expand All @@ -38,6 +39,11 @@ public AddCaseDocumentCommandBuilder withDocumentType(String documentType) {
return this;
}

public AddCaseDocumentCommandBuilder withDocumentUri(String documentUri) {
this.documentUri = documentUri;
return this;
}

public AddCaseDocumentCommandBuilder withId(UUID id) {
this.id = id;
return this;
Expand All @@ -58,6 +64,10 @@ public JsonEnvelope build() {
victim.add("documentType", documentType);
}

if (this.documentUri != null) {
victim.add("documentUri", documentUri);
}

return envelopeFrom(
metadataOf(UUID.randomUUID(), "sjp.command.add-case-document"),
victim.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,11 @@ public Stream<Object> resolveCaseAOCPEligibility(final UUID caseId, final boolea
surchargeAmountMin, surchargeAmountMax, surchargeFinePercentage, surchargeAmount));
}

public Stream<Object> uploadCaseDocument(final UUID caseId, final UUID documentReference, final String documentType) {
return apply(CaseDocumentHandler.INSTANCE.uploadCaseDocument(caseId, documentReference, documentType, state));
public Stream<Object> uploadCaseDocument(final UUID caseId,
final UUID documentReference,
final String documentReferenceUri,
final String documentType) {
return apply(CaseDocumentHandler.INSTANCE.uploadCaseDocument(caseId, documentReference, documentReferenceUri, documentType, state));
}

public Stream<Object> updateHearingRequirements(final UUID userId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public Stream<Object> addCaseDocument(final UUID caseId,
if (state.getCaseDocuments().containsKey(caseDocument.getId())) {
LOGGER.warn("Case Document already exists with ID {}", caseDocument.getId());
return Stream.of(
new CaseDocumentAlreadyExists(caseDocument.getId(), "Add Case Document"));
new CaseDocumentAlreadyExists(caseId, caseDocument.getId(), caseDocument.getDocumentUri(), "Add Case Document"));
}

final int documentCount = state.getDocumentCountByDocumentType()
Expand All @@ -45,25 +45,35 @@ public Stream<Object> addCaseDocument(final UUID caseId,
return Stream.of(new CaseDocumentAdded(caseId, caseDocument, documentCount + 1));
}

/**
* @param documentReference file service id of the document, or null when it is addressed by
* {@code documentReferenceUri}
* @param documentReferenceUri blob uri of the document, or null when it is addressed by
* {@code documentReference}. Exactly one of the two is set; the
* caller validates that.
*/
public Stream<Object> uploadCaseDocument(final UUID caseId,
final UUID documentReference,
final String documentReferenceUri,
final String documentType,
final CaseAggregateState state) {

if (!state.hasGrantedApplication()) {
final Object reference = nonNull(documentReference) ? documentReference : documentReferenceUri;

if (state.isCaseReferredForCourtHearing()) {
LOGGER.warn("Case Document Upload rejected as case is referred to court for hearing: {}", documentReference);
final String description = format("Case Document %s Upload rejected as case %s is referred to court for hearing", documentReference, caseId);
return Stream.of(new CaseDocumentUploadRejected(documentReference, description));
LOGGER.warn("Case Document Upload rejected as case is referred to court for hearing: {}", reference);
final String description = format("Case Document %s Upload rejected as case %s is referred to court for hearing", reference, caseId);
return Stream.of(new CaseDocumentUploadRejected(documentReference, documentReferenceUri, description));
}

if (!state.isManagedByAtcm()) {
LOGGER.warn("Case Document Upload rejected as case is no longer managed by ATCM: {}", documentReference);
final String description = format("Case Document %s Upload rejected as case %s is not managed by ATCM", documentReference, caseId);
return Stream.of(new CaseDocumentUploadRejected(documentReference, description));
LOGGER.warn("Case Document Upload rejected as case is no longer managed by ATCM: {}", reference);
final String description = format("Case Document %s Upload rejected as case %s is not managed by ATCM", reference, caseId);
return Stream.of(new CaseDocumentUploadRejected(documentReference, documentReferenceUri, description));
}
}
return Stream.of(new CaseDocumentUploaded(caseId, documentReference, documentType));
return Stream.of(new CaseDocumentUploaded(caseId, documentReference, documentReferenceUri, documentType));
}

public Stream<Object> deleteCaseDocument(final CaseAggregateState caseAggregateState, final UUID documentId) {
Expand Down
Loading
Loading