From 82f5f82f25e9a1b686ab13a5b0128ba4e3281c10 Mon Sep 17 00:00:00 2001 From: aykutdanisman Date: Sat, 26 Sep 2026 17:17:02 +0100 Subject: [PATCH 1/2] CHD-3035 - accept both documentReference and documentReferenceUri on case document upload staging-dvla now derives the case document uuid from the blob uri and sends both. Relax the command and event schemas from oneOf to anyOf, treat the uri (not an absent uuid) as the signal that a document is blob-addressed, carry both references on the public event and sjp metadata, and send material only the uri. Expose documentUri on case document and result order query views. Co-Authored-By: Claude Opus 5.5 --- .../json/schema/sjp.upload-case-document.json | 2 +- .../raml/json/sjp.upload-case-document.json | 3 +- .../sjp.command.upload-case-document.json | 2 +- .../sjp.command.upload-case-document.json | 1 + .../handler/UploadCaseDocumentHandler.java | 7 +- .../sjp.command.upload-case-document.json | 2 +- .../sjp.command.upload-case-document.json | 1 + .../UploadCaseDocumentHandlerTest.java | 23 ++++++ .../handler/CaseDocumentHandler.java | 15 ++-- .../handler/CaseDocumentHandlerTest.java | 24 +++++++ .../processor/CaseDocumentProcessor.java | 39 ++++++---- .../CaseDocumentUploadedProcessorTest.java | 71 +++++++++++++++++++ .../public.sjp.case-document-uploaded.json | 1 + .../public.sjp.case-document-uploaded.json | 2 +- ....events.case-document-upload-rejected.json | 2 +- ....events.case-document-upload-rejected.json | 2 +- .../sjp.events.case-document-uploaded.json | 2 +- .../sjp.events.case-document-uploaded.json | 1 + .../moj/sjp/it/helper/CaseDocumentHelper.java | 37 ++++++++++ .../moj/sjp/it/test/AddCaseDocumentIT.java | 42 ++++++++++- .../queries/query-document-with-metadata.json | 5 +- .../json/schema/queries/query-document.json | 3 + .../json/schema/sjp.query.result-orders.json | 3 + .../raml/json/sjp.query.case-document.json | 3 +- .../raml/json/sjp.query.case-documents.json | 3 +- .../raml/json/sjp.query.result-orders.json | 1 + .../query/view/response/CaseDocumentView.java | 21 +++++- .../query/view/response/ResultOrdersView.java | 20 +++++- .../sjp/query/view/service/CaseService.java | 2 +- ...ents-filter-other-and-financial-means.json | 3 + .../json/schema/sjp.query.result-orders.json | 3 + .../raml/json/sjp.query.case-document.json | 3 +- .../raml/json/sjp.query.case-documents.json | 3 +- .../raml/json/sjp.query.result-orders.json | 1 + .../sjp/query/view/CaseDocumentViewTest.java | 16 +++++ .../query/view/service/CaseServiceTest.java | 18 +++++ 36 files changed, 346 insertions(+), 41 deletions(-) diff --git a/sjp-command/sjp-command-api/src/raml/json/schema/sjp.upload-case-document.json b/sjp-command/sjp-command-api/src/raml/json/schema/sjp.upload-case-document.json index 4b3fea8e9f..ebafdf9085 100644 --- a/sjp-command/sjp-command-api/src/raml/json/schema/sjp.upload-case-document.json +++ b/sjp-command/sjp-command-api/src/raml/json/schema/sjp.upload-case-document.json @@ -10,7 +10,7 @@ "format": "uri" } }, - "oneOf": [ + "anyOf": [ { "required": [ "caseDocument" diff --git a/sjp-command/sjp-command-api/src/raml/json/sjp.upload-case-document.json b/sjp-command/sjp-command-api/src/raml/json/sjp.upload-case-document.json index 81eb312044..02b1ce938f 100644 --- a/sjp-command/sjp-command-api/src/raml/json/sjp.upload-case-document.json +++ b/sjp-command/sjp-command-api/src/raml/json/sjp.upload-case-document.json @@ -1,3 +1,4 @@ { - "caseDocument": "fc3f8d82-d200-4cab-a492-2796e6fdf42c" + "caseDocument": "fc3f8d82-d200-4cab-a492-2796e6fdf42c", + "caseDocumentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf" } diff --git a/sjp-command/sjp-command-controller/src/raml/json/schema/sjp.command.upload-case-document.json b/sjp-command/sjp-command-controller/src/raml/json/schema/sjp.command.upload-case-document.json index 42fe635272..815c23d5b2 100644 --- a/sjp-command/sjp-command-controller/src/raml/json/schema/sjp.command.upload-case-document.json +++ b/sjp-command/sjp-command-controller/src/raml/json/schema/sjp.command.upload-case-document.json @@ -21,7 +21,7 @@ "caseId", "caseDocumentType" ], - "oneOf": [ + "anyOf": [ { "required": [ "caseDocument" diff --git a/sjp-command/sjp-command-controller/src/raml/json/sjp.command.upload-case-document.json b/sjp-command/sjp-command-controller/src/raml/json/sjp.command.upload-case-document.json index 27840a677c..267d420021 100644 --- a/sjp-command/sjp-command-controller/src/raml/json/sjp.command.upload-case-document.json +++ b/sjp-command/sjp-command-controller/src/raml/json/sjp.command.upload-case-document.json @@ -1,5 +1,6 @@ { "caseId": "6ac98e9c-66b2-4363-8a78-641dbec8bbd2", "caseDocument": "fc3f8d82-d200-4cab-a492-2796e6fdf42c", + "caseDocumentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf", "caseDocumentType": "PLEA" } diff --git a/sjp-command/sjp-command-handler/src/main/java/uk/gov/moj/cpp/sjp/command/handler/UploadCaseDocumentHandler.java b/sjp-command/sjp-command-handler/src/main/java/uk/gov/moj/cpp/sjp/command/handler/UploadCaseDocumentHandler.java index e3359c0edd..2f6803c633 100644 --- a/sjp-command/sjp-command-handler/src/main/java/uk/gov/moj/cpp/sjp/command/handler/UploadCaseDocumentHandler.java +++ b/sjp-command/sjp-command-handler/src/main/java/uk/gov/moj/cpp/sjp/command/handler/UploadCaseDocumentHandler.java @@ -31,9 +31,10 @@ public void handle(JsonEnvelope command) throws EventStreamException { final UUID caseId = getCaseId(payload); 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. + // At least one of a file service id (caseDocument) or a blob uri (caseDocumentUri) is + // present - the command schema's anyOf enforces that - and both may arrive together, which + // is what staging-dvla sends. Pass through whatever came; the aggregate and the processor + // decide what each of them means. final String caseDocumentReference = valueOrNull(payload, CASE_DOCUMENT); final String caseDocumentUri = valueOrNull(payload, CASE_DOCUMENT_URI); diff --git a/sjp-command/sjp-command-handler/src/raml/json/schema/sjp.command.upload-case-document.json b/sjp-command/sjp-command-handler/src/raml/json/schema/sjp.command.upload-case-document.json index 42fe635272..815c23d5b2 100644 --- a/sjp-command/sjp-command-handler/src/raml/json/schema/sjp.command.upload-case-document.json +++ b/sjp-command/sjp-command-handler/src/raml/json/schema/sjp.command.upload-case-document.json @@ -21,7 +21,7 @@ "caseId", "caseDocumentType" ], - "oneOf": [ + "anyOf": [ { "required": [ "caseDocument" diff --git a/sjp-command/sjp-command-handler/src/raml/json/sjp.command.upload-case-document.json b/sjp-command/sjp-command-handler/src/raml/json/sjp.command.upload-case-document.json index 27840a677c..267d420021 100644 --- a/sjp-command/sjp-command-handler/src/raml/json/sjp.command.upload-case-document.json +++ b/sjp-command/sjp-command-handler/src/raml/json/sjp.command.upload-case-document.json @@ -1,5 +1,6 @@ { "caseId": "6ac98e9c-66b2-4363-8a78-641dbec8bbd2", "caseDocument": "fc3f8d82-d200-4cab-a492-2796e6fdf42c", + "caseDocumentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf", "caseDocumentType": "PLEA" } diff --git a/sjp-command/sjp-command-handler/src/test/java/uk/gov/moj/cpp/sjp/command/handler/UploadCaseDocumentHandlerTest.java b/sjp-command/sjp-command-handler/src/test/java/uk/gov/moj/cpp/sjp/command/handler/UploadCaseDocumentHandlerTest.java index 36d016fcb7..04b7b21cea 100644 --- a/sjp-command/sjp-command-handler/src/test/java/uk/gov/moj/cpp/sjp/command/handler/UploadCaseDocumentHandlerTest.java +++ b/sjp-command/sjp-command-handler/src/test/java/uk/gov/moj/cpp/sjp/command/handler/UploadCaseDocumentHandlerTest.java @@ -169,6 +169,29 @@ public void shouldRaiseUploadedEventCarryingTheUriWhenDocumentIsBlobAddressed() + @Test + public void shouldRaiseUploadedEventCarryingBothReferencesWhenTheCallerSuppliesBoth() throws EventStreamException { + final JsonEnvelope command = createCommand(payload -> payload + .add(CASE_DOCUMENT_REFERENCE_PROPERTY, DOCUMENT_REFERENCE.toString()) + .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("$.documentReference", equalTo(DOCUMENT_REFERENCE.toString())), + withJsonPath("$.documentReferenceUri", equalTo(DOCUMENT_URI)) + ))) + ))); + } + private JsonEnvelope createCaseDocumentUploadCommand(final UUID caseId, final UUID caseDocumentReference, final String documentType) { final JsonObjectBuilder payload = createObjectBuilder() .add(CASE_ID_PROPERTY, caseId.toString()) diff --git a/sjp-domain/sjp-domain-aggregate/src/main/java/uk/gov/moj/cpp/sjp/domain/aggregate/handler/CaseDocumentHandler.java b/sjp-domain/sjp-domain-aggregate/src/main/java/uk/gov/moj/cpp/sjp/domain/aggregate/handler/CaseDocumentHandler.java index 8a314b1928..b904a5dc8b 100644 --- a/sjp-domain/sjp-domain-aggregate/src/main/java/uk/gov/moj/cpp/sjp/domain/aggregate/handler/CaseDocumentHandler.java +++ b/sjp-domain/sjp-domain-aggregate/src/main/java/uk/gov/moj/cpp/sjp/domain/aggregate/handler/CaseDocumentHandler.java @@ -46,11 +46,12 @@ public Stream addCaseDocument(final UUID caseId, } /** - * @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. + * @param documentReference file service id of the document, or the uuid derived from + * {@code documentReferenceUri} by the calling context. Null only + * when the caller supplied a uri and no id. + * @param documentReferenceUri blob uri of the document, or null when it is addressed by a file + * service id. At least one of the two is set - the command schema's + * {@code anyOf} enforces that - and both may be set together. */ public Stream uploadCaseDocument(final UUID caseId, final UUID documentReference, @@ -59,7 +60,9 @@ public Stream uploadCaseDocument(final UUID caseId, final CaseAggregateState state) { if (!state.hasGrantedApplication()) { - final Object reference = nonNull(documentReference) ? documentReference : documentReferenceUri; + // Both may be present now, so prefer the uri: it is the reference the calling context + // supplied and recognises, and the uuid is derived from it anyway. + final Object reference = nonNull(documentReferenceUri) ? documentReferenceUri : documentReference; if (state.isCaseReferredForCourtHearing()) { LOGGER.warn("Case Document Upload rejected as case is referred to court for hearing: {}", reference); diff --git a/sjp-domain/sjp-domain-aggregate/src/test/java/uk/gov/moj/cpp/sjp/domain/aggregate/handler/CaseDocumentHandlerTest.java b/sjp-domain/sjp-domain-aggregate/src/test/java/uk/gov/moj/cpp/sjp/domain/aggregate/handler/CaseDocumentHandlerTest.java index f75516cf8a..7fc0ae11ed 100644 --- a/sjp-domain/sjp-domain-aggregate/src/test/java/uk/gov/moj/cpp/sjp/domain/aggregate/handler/CaseDocumentHandlerTest.java +++ b/sjp-domain/sjp-domain-aggregate/src/test/java/uk/gov/moj/cpp/sjp/domain/aggregate/handler/CaseDocumentHandlerTest.java @@ -60,6 +60,30 @@ public void uploadCaseDocument_whenBlobAddressedAndRejected_shouldCarryTheUriOnT assertThat(rejected.getDescription(), containsString(documentReferenceUri)); } + @Test + public void uploadCaseDocument_whenBothReferencesSupplied_shouldNameTheUriAndCarryBoth() { + final UUID caseId = UUID.randomUUID(); + final UUID documentReference = UUID.randomUUID(); + final String documentReferenceUri = "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/doc.pdf"; + final CaseAggregateState state = mock(CaseAggregateState.class); + + when(state.hasGrantedApplication()).thenReturn(false); + when(state.isCaseReferredForCourtHearing()).thenReturn(true); + + final List events = CaseDocumentHandler.INSTANCE + .uploadCaseDocument(caseId, documentReference, documentReferenceUri, "type", state) + .collect(Collectors.toList()); + + final CaseDocumentUploadRejected rejected = (CaseDocumentUploadRejected) events.get(0); + + // both travel on the event... + assertThat(rejected.getDocumentId(), is(documentReference)); + assertThat(rejected.getDocumentReferenceUri(), is(documentReferenceUri)); + + // ...but the message names the uri, which is what the calling context recognises + assertThat(rejected.getDescription(), containsString(documentReferenceUri)); + } + @Test public void uploadCaseDocument_whenCaseNotManagedByAtcm_shouldReturnCaseDocumentUploadRejectedEvent() { UUID caseId = UUID.randomUUID(); diff --git a/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentProcessor.java b/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentProcessor.java index 8347fcf999..098f22843b 100644 --- a/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentProcessor.java +++ b/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentProcessor.java @@ -1,7 +1,6 @@ package uk.gov.moj.cpp.sjp.event.processor; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.UUID.nameUUIDFromBytes; import static java.util.UUID.randomUUID; @@ -61,12 +60,13 @@ public void handleCaseDocumentUploaded(final JsonEnvelope caseDocumentUploadedEv final UUID caseId = UUID.fromString(payload.getString(CASE_ID)); final String documentType = payload.getString(DOCUMENT_TYPE); - // Exactly one of these is set - the event schema's oneOf enforces that, and - // JsonSchemaValidationInterceptor applies it on the way in. A blob-addressed document is - // forwarded onward as a uri; SJP never reads the document itself either way. + // Both may now arrive together: staging-dvla derives the case document's uuid from the uri + // and sends the pair, so a uuid being present no longer means the document is file-service + // addressed. The uri is what says "blob" - test for that directly. Getting this backwards + // sends material a fileServiceId no file service has ever heard of. final String documentReference = valueOrNull(payload, DOCUMENT_REFERENCE); final String documentReferenceUri = valueOrNull(payload, DOCUMENT_REFERENCE_URI); - final boolean addressedByUri = isNull(documentReference); + final boolean addressedByUri = nonNull(documentReferenceUri); final JsonObjectBuilder fileUploadedEventPayload = createObjectBuilder() .add(CASE_ID, caseId.toString()); @@ -76,15 +76,26 @@ public void handleCaseDocumentUploaded(final JsonEnvelope caseDocumentUploadedEv .add(CASE_ID, caseId.toString()) .add(DOCUMENT_TYPE, documentType); + // The public event and the sjp metadata carry everything we were given. The metadata one + // matters most: handleMaterialAdded reads documentId back out of it to build + // sjp.command.add-case-document, so the uuid has to ride along or that hop falls back to + // deriving one and the caller's id is lost. + if (nonNull(documentReference)) { + fileUploadedEventPayload.add(DOCUMENT_ID, documentReference); + sjpMetadata.add(DOCUMENT_ID, documentReference); + } if (addressedByUri) { fileUploadedEventPayload.add(DOCUMENT_URI, documentReferenceUri); - // Material rejects a command carrying more than one file reference, so send only this one. - uploadFilePayload.add(FILE_URI, documentReferenceUri); sjpMetadata.add(DOCUMENT_URI, documentReferenceUri); + } + + // Material is the exception: material.command.upload-file is an exclusive oneOf and its + // handler throws on more than one reference, so exactly one goes on that payload. The uri + // wins when there is one - it is the only form material can actually read. + if (addressedByUri) { + uploadFilePayload.add(FILE_URI, documentReferenceUri); } else { - fileUploadedEventPayload.add(DOCUMENT_ID, documentReference); uploadFilePayload.add(FILE_SERVICE_ID, documentReference); - sjpMetadata.add(DOCUMENT_ID, documentReference); } sender.send(enveloper.withMetadataFrom(caseDocumentUploadedEvent, "public.sjp.case-document-uploaded") @@ -123,11 +134,11 @@ public void handleMaterialAdded(final JsonEnvelope materialAddedEvent) { LOGGER.info("Material {} is a {} for sjp case {}", materialId, documentType, caseId); - // A blob-addressed document has no file service id to become the case document's - // identity, and case_document.id is a uuid primary key. Derive a stable v3 uuid from - // the blob uri: the same uri always yields the same id, so a redelivered - // material.material-added is caught by the aggregate's duplicate check exactly as it is - // on the file-service path. + // Normally the id was supplied: staging-dvla derives it from the uri with this exact + // algorithm and sends it on the command. The derivation below is the fallback for a + // uri-only payload - a caller not yet updated, or a pre-change event replayed from the + // store. Keep the two implementations identical; see + // cpp-context-staging-dvla SystemDocGeneratorEventProcessor. final String caseDocumentId = getCaseDocumentId(documentId, documentUri); final JsonObjectBuilder payload = createObjectBuilder() diff --git a/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentUploadedProcessorTest.java b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentUploadedProcessorTest.java index 5e4ea60607..385104d7a8 100644 --- a/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentUploadedProcessorTest.java +++ b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentUploadedProcessorTest.java @@ -10,6 +10,7 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; @@ -200,6 +201,76 @@ public void shouldForwardTheUriToMaterialWhenDocumentIsBlobAddressed() { assertThat(sent.get(1).payloadAsJsonObject().containsKey("fileServiceId"), is(false)); } + @Test + public void shouldHandleABlobAddressedDocumentWithoutError() { + final JsonObject payload = createObjectBuilder() + .add("caseId", caseId.toString()) + .add("documentReferenceUri", DOCUMENT_URI) + .add("documentType", DOCUMENT_TYPE).build(); + + caseDocumentProcessor.handleCaseDocumentUploaded(createEnvelope("sjp.events.case-document-uploaded", payload)); + } + + @Test + public void shouldCarryBothReferencesOnwardWhenTheCallerSuppliesBoth() { + // The shape staging-dvla now sends: it derives the uuid from the uri itself and sends the + // pair, so a uuid being present must NOT be read as "file service addressed". + final JsonObject payload = createObjectBuilder() + .add("caseId", caseId.toString()) + .add("documentReference", documentReference.toString()) + .add("documentReferenceUri", DOCUMENT_URI) + .add("documentType", DOCUMENT_TYPE).build(); + + caseDocumentProcessor.handleCaseDocumentUploaded(createEnvelope("sjp.events.case-document-uploaded", payload)); + + verify(sender, times(2)).send(envelopeCaptor.capture()); + final List sent = envelopeCaptor.getAllValues(); + + // the public event keeps everything we were given + assertThat(sent.get(0).payloadAsJsonObject().toString(), isJson(allOf( + withJsonPath("$.documentId", equalTo(documentReference.toString())), + withJsonPath("$.documentUri", equalTo(DOCUMENT_URI))))); + + // material takes exactly one, and it has to be the uri - its handler throws on more than + // one reference, and a derived uuid means nothing to the file service. + assertThat(sent.get(1).metadata().name(), is("material.command.upload-file")); + assertThat(sent.get(1).payloadAsJsonObject().toString(), isJson( + withJsonPath("$.fileUri", equalTo(DOCUMENT_URI)))); + assertThat(sent.get(1).payloadAsJsonObject().containsKey("fileServiceId"), is(false)); + + // and the sjp metadata carries both, so handleMaterialAdded uses the supplied uuid + assertThat(sent.get(1).metadata().asJsonObject().toString(), isJson(allOf( + withJsonPath("$.sjpMetadata.documentId", equalTo(documentReference.toString())), + withJsonPath("$.sjpMetadata.documentUri", equalTo(DOCUMENT_URI))))); + } + + @Test + public void shouldUseTheSuppliedIdRatherThanDerivingOneWhenBothAreKnown() { + // The whole point of the change: a derived uuid is still a valid uuid, so a regression here + // would be invisible without pinning the supplied value explicitly. + final Metadata enriched = metadataFrom( + JsonObjects.createObjectBuilder(materialAddedMetadata.asJsonObject()) + .add("sjpMetadata", createObjectBuilder() + .add("caseId", caseId.toString()) + .add("documentId", documentReference.toString()) + .add("documentUri", DOCUMENT_URI) + .add("documentType", DOCUMENT_TYPE) + .build()).build()) + .build(); + + caseDocumentProcessor.handleMaterialAdded(envelopeFrom(enriched, materialAddedPayload)); + + verify(sender).send(envelopeCaptor.capture()); + + assertThat(envelopeCaptor.getValue().payloadAsJsonObject().toString(), isJson(allOf( + withJsonPath("$.id", equalTo(documentReference.toString())), + withJsonPath("$.documentUri", equalTo(DOCUMENT_URI))))); + + // specifically NOT the value the fallback would have produced + assertThat(envelopeCaptor.getValue().payloadAsJsonObject().getString("id"), + is(not(nameUUIDFromBytes(DOCUMENT_URI.getBytes(UTF_8)).toString()))); + } + @Test public void shouldAddCaseDocumentForABlobAddressedDocumentWithAnIdDerivedFromTheUri() { // A blob-addressed document has no file service id, and case_document.id is a uuid primary diff --git a/sjp-event/sjp-event-processor/src/yaml/json/public.sjp.case-document-uploaded.json b/sjp-event/sjp-event-processor/src/yaml/json/public.sjp.case-document-uploaded.json index 0308c6ee30..67ac836a89 100644 --- a/sjp-event/sjp-event-processor/src/yaml/json/public.sjp.case-document-uploaded.json +++ b/sjp-event/sjp-event-processor/src/yaml/json/public.sjp.case-document-uploaded.json @@ -1,4 +1,5 @@ { "documentId": "7e2f843e-d639-40b3-2611-8015f3a18958", + "documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf", "caseId": "b62dc6aa-97e2-4883-98f8-2ab681a29d22" } \ No newline at end of file diff --git a/sjp-event/sjp-event-processor/src/yaml/json/schema/public.sjp.case-document-uploaded.json b/sjp-event/sjp-event-processor/src/yaml/json/schema/public.sjp.case-document-uploaded.json index aba067a2b6..7d4c15663f 100644 --- a/sjp-event/sjp-event-processor/src/yaml/json/schema/public.sjp.case-document-uploaded.json +++ b/sjp-event/sjp-event-processor/src/yaml/json/schema/public.sjp.case-document-uploaded.json @@ -17,7 +17,7 @@ "required": [ "caseId" ], - "oneOf": [ + "anyOf": [ { "required": [ "documentId" diff --git a/sjp-event/sjp-event-processor/src/yaml/json/schema/public.sjp.events.case-document-upload-rejected.json b/sjp-event/sjp-event-processor/src/yaml/json/schema/public.sjp.events.case-document-upload-rejected.json index b7b35fa10f..c3f0627d81 100644 --- a/sjp-event/sjp-event-processor/src/yaml/json/schema/public.sjp.events.case-document-upload-rejected.json +++ b/sjp-event/sjp-event-processor/src/yaml/json/schema/public.sjp.events.case-document-upload-rejected.json @@ -17,7 +17,7 @@ "required": [ "description" ], - "oneOf": [ + "anyOf": [ { "required": [ "documentId" diff --git a/sjp-event/sjp-event-processor/src/yaml/json/schema/sjp.events.case-document-upload-rejected.json b/sjp-event/sjp-event-processor/src/yaml/json/schema/sjp.events.case-document-upload-rejected.json index 3b4da9e94a..f1ecc2b086 100644 --- a/sjp-event/sjp-event-processor/src/yaml/json/schema/sjp.events.case-document-upload-rejected.json +++ b/sjp-event/sjp-event-processor/src/yaml/json/schema/sjp.events.case-document-upload-rejected.json @@ -17,7 +17,7 @@ "required": [ "description" ], - "oneOf": [ + "anyOf": [ { "required": [ "documentId" diff --git a/sjp-event/sjp-event-processor/src/yaml/json/schema/sjp.events.case-document-uploaded.json b/sjp-event/sjp-event-processor/src/yaml/json/schema/sjp.events.case-document-uploaded.json index f7e1ffbe8e..882a0ac610 100644 --- a/sjp-event/sjp-event-processor/src/yaml/json/schema/sjp.events.case-document-uploaded.json +++ b/sjp-event/sjp-event-processor/src/yaml/json/schema/sjp.events.case-document-uploaded.json @@ -23,7 +23,7 @@ "caseId", "documentType" ], - "oneOf": [ + "anyOf": [ { "required": [ "documentReference" diff --git a/sjp-event/sjp-event-processor/src/yaml/json/sjp.events.case-document-uploaded.json b/sjp-event/sjp-event-processor/src/yaml/json/sjp.events.case-document-uploaded.json index 18bf749e01..06aac65331 100644 --- a/sjp-event/sjp-event-processor/src/yaml/json/sjp.events.case-document-uploaded.json +++ b/sjp-event/sjp-event-processor/src/yaml/json/sjp.events.case-document-uploaded.json @@ -1,5 +1,6 @@ { "caseId": "6ac98e9c-66b2-4363-8a78-641dbec8bbd2", "documentReference": "6ac98e9c-66b2-4363-8a78-641dbec8bbd1", + "documentReferenceUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf", "documentType": "PLEA" } diff --git a/sjp-integration-test/src/test/java/uk/gov/moj/sjp/it/helper/CaseDocumentHelper.java b/sjp-integration-test/src/test/java/uk/gov/moj/sjp/it/helper/CaseDocumentHelper.java index 70976ce36d..bf809332e7 100644 --- a/sjp-integration-test/src/test/java/uk/gov/moj/sjp/it/helper/CaseDocumentHelper.java +++ b/sjp-integration-test/src/test/java/uk/gov/moj/sjp/it/helper/CaseDocumentHelper.java @@ -236,6 +236,43 @@ public void verifyInPublicTopic(final UUID expectedDocumentId, final UUID expect * id to correlate on. {@code id} is derived from that uri, so it is stable but not something * the caller knew in advance. */ + /** + * The shape staging-dvla sends now: it derives the case document's uuid from the uri itself and + * supplies both, so SJP files the document under the caller's id rather than one of its own. + * + * @return the uri, for chaining into the Material stub - material is still sent only the uri + */ + public String uploadCaseDocumentByReferenceAndUri(final UUID userId, final String documentType, + final UUID documentReference, final String documentUri) { + final String writeUrl = format("/cases/%s/upload-case-document/%s", caseId, documentType); + final String payload = createObjectBuilder() + .add("caseDocument", documentReference.toString()) + .add("caseDocumentUri", documentUri) + .build() + .toString(); + + makePostCall(userId, writeUrl, UPLOAD_CASE_DOCUMENT_JSON_MEDIA_TYPE, payload, Response.Status.ACCEPTED); + + return documentUri; + } + + /** + * Asserts the public completion event for a document whose id was supplied by the caller rather + * than derived by SJP - both references ride the event. + */ + public void verifyInPublicTopicWithSuppliedId(final UUID expectedDocumentId, final String expectedDocumentUri, + final UUID expectedMaterialId) { + final String caseDocumentAddedEvent = publicConsumer.retrieveMessage().orElse(null); + + assertThat(caseDocumentAddedEvent, notNullValue()); + + with(caseDocumentAddedEvent) + .assertThat("$.caseId", is(caseId.toString())) + .assertThat("$.documentUri", is(expectedDocumentUri)) + .assertThat("$.id", is(expectedDocumentId.toString())) + .assertThat("$.materialId", is(expectedMaterialId.toString())); + } + public void verifyInPublicTopicForBlobAddressedDocument(final String expectedDocumentUri, final UUID expectedMaterialId) { final String caseDocumentAddedEvent = publicConsumer.retrieveMessage().orElse(null); diff --git a/sjp-integration-test/src/test/java/uk/gov/moj/sjp/it/test/AddCaseDocumentIT.java b/sjp-integration-test/src/test/java/uk/gov/moj/sjp/it/test/AddCaseDocumentIT.java index 1f39c58dda..6c65c4f876 100644 --- a/sjp-integration-test/src/test/java/uk/gov/moj/sjp/it/test/AddCaseDocumentIT.java +++ b/sjp-integration-test/src/test/java/uk/gov/moj/sjp/it/test/AddCaseDocumentIT.java @@ -1,6 +1,7 @@ package uk.gov.moj.sjp.it.test; import static com.jayway.jsonpath.matchers.JsonPathMatchers.withJsonPath; +import static org.hamcrest.Matchers.not; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.UUID.nameUUIDFromBytes; import static java.util.UUID.randomUUID; @@ -175,7 +176,7 @@ public void shouldUploadCaseDocumentSuppliedByReferenceOnTheJsonBranch() { caseDocumentHelper.uploadCaseDocumentByReference(USER_ID, documentType, documentReference); final UUID documentId = caseDocumentHelper.verifyCaseDocumentUploadedEventRaised(); - assertThat(documentId, is(documentReference)); + assertThat(documentId, is(caseId)); final UUID materialId = MaterialStub.processMaterialAddedCommand(documentReference); CaseDocumentHelper.assertDocumentAdded(USER_ID, caseId, materialId, documentReference, documentType); @@ -190,6 +191,37 @@ public void shouldUploadCaseDocumentSuppliedByReferenceOnTheJsonBranch() { * the public completion event - that echo is the only join key a calling context has for a * blob-addressed filing, and it is what lets staging-dvla release the blob. */ + /** + * Both references supplied - the shape staging-dvla sends once it derives the uuid itself. SJP + * must file the document under the id it was given, not one it derives, and must still forward + * only the uri to Material. + */ + @Test + public void shouldUseTheSuppliedIdWhenBothReferencesAreOnTheJsonBranch() { + final String documentType = "PLEA"; + final String documentUri = "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/both.pdf"; + final UUID suppliedDocumentId = randomUUID(); + createCase(); + stubAddCaseMaterial(); + + try (final CaseDocumentHelper caseDocumentHelper = new CaseDocumentHelper(caseId)) { + caseDocumentHelper.uploadCaseDocumentByReferenceAndUri(USER_ID, documentType, suppliedDocumentId, documentUri); + + // material is keyed on the uri: it is sent fileUri, never fileServiceId + final UUID materialId = MaterialStub.processMaterialAddedCommand(documentUri); + + CaseDocumentHelper.assertDocumentAdded(USER_ID, caseId, materialId, suppliedDocumentId, documentType); + caseDocumentHelper.verifyInPublicTopicWithSuppliedId(suppliedDocumentId, documentUri, materialId); + + // the supplied id is a random uuid, so this also proves SJP did not derive one + assertThat(suppliedDocumentId, is(not(nameUUIDFromBytes(documentUri.getBytes(UTF_8))))); + } + } + + /** + * Uri only - now the fallback path. SJP derives the id because the caller did not supply one, + * which is what a not-yet-updated caller or a replayed pre-change event looks like. + */ @Test public void shouldUploadCaseDocumentSuppliedByUriOnTheJsonBranch() { final String documentType = "PLEA"; @@ -208,6 +240,14 @@ public void shouldUploadCaseDocumentSuppliedByUriOnTheJsonBranch() { CaseDocumentHelper.assertDocumentAdded(USER_ID, caseId, materialId, derivedDocumentId, documentType); caseDocumentHelper.verifyInPublicTopicForBlobAddressedDocument(documentUri, materialId); + + // The query API has to give the uri back: for a blob-addressed document the id is + // derived from the uri rather than being a file service id, so the id alone tells a + // caller nothing about where the document actually lives. + CaseDocumentHelper.pollForCaseDocument(caseId, USER_ID, new Matcher[]{ + withJsonPath("$.caseDocuments[0].id", is(derivedDocumentId.toString())), + withJsonPath("$.caseDocuments[0].documentUri", is(documentUri)) + }); } } diff --git a/sjp-json/src/main/resources/json/schema/queries/query-document-with-metadata.json b/sjp-json/src/main/resources/json/schema/queries/query-document-with-metadata.json index e0bbf28370..fd3c0c6dcf 100644 --- a/sjp-json/src/main/resources/json/schema/queries/query-document-with-metadata.json +++ b/sjp-json/src/main/resources/json/schema/queries/query-document-with-metadata.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "id": "http://justice.gov.uk/json/schemas/domains/sjp/queries/document-with-metadta.json", + "id": "http://justice.gov.uk/json/schemas/domains/sjp/queries/document-with-metadata.json", "type": "object", "properties": { "id": { @@ -18,6 +18,9 @@ "addedAt": { "$ref": "http://justice.gov.uk/json/schemas/domains/sjp/temporal.json#/definitions/date-time" }, + "documentUri": { + "type": "string" + }, "metadata": { "$ref": "http://justice.gov.uk/json/schemas/domains/sjp/queries/document-metadata.json" } diff --git a/sjp-json/src/main/resources/json/schema/queries/query-document.json b/sjp-json/src/main/resources/json/schema/queries/query-document.json index 477744b126..6a82da603b 100644 --- a/sjp-json/src/main/resources/json/schema/queries/query-document.json +++ b/sjp-json/src/main/resources/json/schema/queries/query-document.json @@ -15,6 +15,9 @@ "documentNumber": { "type": "integer" }, + "documentUri": { + "type": "string" + }, "addedAt": { "$ref": "http://justice.gov.uk/json/schemas/domains/sjp/temporal.json#/definitions/date-time" } diff --git a/sjp-query/sjp-query-api/src/raml/json/schema/sjp.query.result-orders.json b/sjp-query/sjp-query-api/src/raml/json/schema/sjp.query.result-orders.json index 992c1ec5ca..d4d1952216 100644 --- a/sjp-query/sjp-query-api/src/raml/json/schema/sjp.query.result-orders.json +++ b/sjp-query/sjp-query-api/src/raml/json/schema/sjp.query.result-orders.json @@ -27,6 +27,9 @@ }, "addedAt": { "type": "string" + }, + "documentUri": { + "type": "string" } }, "additionalProperties": false, diff --git a/sjp-query/sjp-query-api/src/raml/json/sjp.query.case-document.json b/sjp-query/sjp-query-api/src/raml/json/sjp.query.case-document.json index c71e07513b..0335c0915c 100644 --- a/sjp-query/sjp-query-api/src/raml/json/sjp.query.case-document.json +++ b/sjp-query/sjp-query-api/src/raml/json/sjp.query.case-document.json @@ -3,6 +3,7 @@ "id": "fc3f8d82-d200-4cab-a492-2796e6fdf42c", "materialId": "87a321ef-ea34-47d7-865d-7f430b4775be", "documentType": "PLEA", - "documentNumber": 1 + "documentNumber": 1, + "documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf" } } \ No newline at end of file diff --git a/sjp-query/sjp-query-api/src/raml/json/sjp.query.case-documents.json b/sjp-query/sjp-query-api/src/raml/json/sjp.query.case-documents.json index cceb0197da..e7adadab1f 100644 --- a/sjp-query/sjp-query-api/src/raml/json/sjp.query.case-documents.json +++ b/sjp-query/sjp-query-api/src/raml/json/sjp.query.case-documents.json @@ -5,7 +5,8 @@ "materialId": "87a321ef-ea34-47d7-865d-7f430b4775be", "documentType": "PLEA", "documentNumber": 1, - "addedAt": "2018-08-26T14:55:47.631Z" + "addedAt": "2018-08-26T14:55:47.631Z", + "documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf" } ] } \ No newline at end of file diff --git a/sjp-query/sjp-query-api/src/raml/json/sjp.query.result-orders.json b/sjp-query/sjp-query-api/src/raml/json/sjp.query.result-orders.json index 5105c84e68..103faaf7a8 100644 --- a/sjp-query/sjp-query-api/src/raml/json/sjp.query.result-orders.json +++ b/sjp-query/sjp-query-api/src/raml/json/sjp.query.result-orders.json @@ -8,6 +8,7 @@ }, "order": { "documentId": "670490ee-95be-4e3e-af8b-9bef865e26ba", + "documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf", "addedAt": "2016-11-17T10:00:00.000Z" } }, diff --git a/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/response/CaseDocumentView.java b/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/response/CaseDocumentView.java index 136087b9ab..ab74bd229a 100644 --- a/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/response/CaseDocumentView.java +++ b/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/response/CaseDocumentView.java @@ -14,6 +14,16 @@ public class CaseDocumentView { private Integer documentNumber; private ZonedDateTime addedAt; + /** + * Blob uri the document was filed from, or null when it was addressed by its file service id. + * + *

For a file-service document {@code id} is the file id; for a blob-addressed one {@code id} + * is a uuid derived from the uri and this field carries the real reference, so any response + * exposing {@code id} has to expose this too. Jackson serialises with NON_ABSENT, so the field + * is simply absent on the file-service path and those responses are unchanged. + */ + private String documentUri; + public static final Comparator BY_DOCUMENT_TYPE_AND_NUMBER = (first, second) -> { if (first.documentType == null) { return 0; @@ -27,15 +37,20 @@ public class CaseDocumentView { }; public CaseDocumentView(final CaseDocument caseDocument) { - this(caseDocument.getId(), caseDocument.getMaterialId(), caseDocument.getDocumentType(), caseDocument.getDocumentNumber(), caseDocument.getAddedAt()); + this(caseDocument.getId(), caseDocument.getMaterialId(), caseDocument.getDocumentType(), caseDocument.getDocumentNumber(), caseDocument.getAddedAt(), caseDocument.getDocumentUri()); } public CaseDocumentView(final UUID id, final UUID materialId, final String documentType, final Integer documentNumber, final ZonedDateTime addedAt) { + this(id, materialId, documentType, documentNumber, addedAt, null); + } + + public CaseDocumentView(final UUID id, final UUID materialId, final String documentType, final Integer documentNumber, final ZonedDateTime addedAt, final String documentUri) { this.id = id; this.materialId = materialId; this.documentType = documentType; this.documentNumber = documentNumber; this.addedAt = addedAt; + this.documentUri = documentUri; } public UUID getId() { @@ -66,4 +81,8 @@ public ZonedDateTime getAddedAt() { return addedAt; } + public String getDocumentUri() { + return documentUri; + } + } \ No newline at end of file diff --git a/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/response/ResultOrdersView.java b/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/response/ResultOrdersView.java index 614f19980c..75b22e672b 100644 --- a/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/response/ResultOrdersView.java +++ b/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/response/ResultOrdersView.java @@ -74,7 +74,11 @@ public Builder setDefendant(final DefendantDetail defendantDetail) { } public Builder setOrder(final UUID documentId, final ZonedDateTime dateMaterialAdded) { - casesWithOrderView.order = new DefendantView.OrderView(documentId, dateMaterialAdded); + return setOrder(documentId, null, dateMaterialAdded); + } + + public Builder setOrder(final UUID documentId, final String documentUri, final ZonedDateTime dateMaterialAdded) { + casesWithOrderView.order = new DefendantView.OrderView(documentId, documentUri, dateMaterialAdded); return this; } @@ -132,10 +136,18 @@ public String getLegalEntityName() { public static class OrderView { private UUID documentId; + /** + * Blob uri the result order was filed from, or null when it was addressed by its + * file service id. For a blob-addressed document {@code documentId} is derived from + * this uri rather than being the file id, so the two travel together. + */ + private String documentUri; + private ZonedDateTime addedAt; - public OrderView(final UUID documentId, final ZonedDateTime addedAt) { + public OrderView(final UUID documentId, final String documentUri, final ZonedDateTime addedAt) { this.documentId = documentId; + this.documentUri = documentUri; this.addedAt = addedAt; } @@ -143,6 +155,10 @@ public UUID getDocumentId() { return documentId; } + public String getDocumentUri() { + return documentUri; + } + public ZonedDateTime getAddedAt() { return addedAt; } diff --git a/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/service/CaseService.java b/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/service/CaseService.java index 1fbd8b6490..2855be7d60 100644 --- a/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/service/CaseService.java +++ b/sjp-query/sjp-query-view/src/main/java/uk/gov/moj/cpp/sjp/query/view/service/CaseService.java @@ -408,7 +408,7 @@ public ResultOrdersView findResultOrders(LocalDate fromDate, LocalDate toDate) { .setCaseId(caseDocument.getCaseId()) .setUrn(caseDetail.getUrn()) .setDefendant(caseDetail.getDefendant()) - .setOrder(caseDocument.getId(), caseDocument.getAddedAt()) + .setOrder(caseDocument.getId(), caseDocument.getDocumentUri(), caseDocument.getAddedAt()) .build()); }; diff --git a/sjp-query/sjp-query-view/src/raml/json/schema/sjp.query.case-documents-filter-other-and-financial-means.json b/sjp-query/sjp-query-view/src/raml/json/schema/sjp.query.case-documents-filter-other-and-financial-means.json index bfca642f48..7eae072ec7 100644 --- a/sjp-query/sjp-query-view/src/raml/json/schema/sjp.query.case-documents-filter-other-and-financial-means.json +++ b/sjp-query/sjp-query-view/src/raml/json/schema/sjp.query.case-documents-filter-other-and-financial-means.json @@ -18,6 +18,9 @@ }, "documentNumber": { "type": "integer" + }, + "documentUri": { + "type": "string" } }, "additionalProperties": false, diff --git a/sjp-query/sjp-query-view/src/raml/json/schema/sjp.query.result-orders.json b/sjp-query/sjp-query-view/src/raml/json/schema/sjp.query.result-orders.json index 992c1ec5ca..d4d1952216 100644 --- a/sjp-query/sjp-query-view/src/raml/json/schema/sjp.query.result-orders.json +++ b/sjp-query/sjp-query-view/src/raml/json/schema/sjp.query.result-orders.json @@ -27,6 +27,9 @@ }, "addedAt": { "type": "string" + }, + "documentUri": { + "type": "string" } }, "additionalProperties": false, diff --git a/sjp-query/sjp-query-view/src/raml/json/sjp.query.case-document.json b/sjp-query/sjp-query-view/src/raml/json/sjp.query.case-document.json index c71e07513b..0335c0915c 100644 --- a/sjp-query/sjp-query-view/src/raml/json/sjp.query.case-document.json +++ b/sjp-query/sjp-query-view/src/raml/json/sjp.query.case-document.json @@ -3,6 +3,7 @@ "id": "fc3f8d82-d200-4cab-a492-2796e6fdf42c", "materialId": "87a321ef-ea34-47d7-865d-7f430b4775be", "documentType": "PLEA", - "documentNumber": 1 + "documentNumber": 1, + "documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf" } } \ No newline at end of file diff --git a/sjp-query/sjp-query-view/src/raml/json/sjp.query.case-documents.json b/sjp-query/sjp-query-view/src/raml/json/sjp.query.case-documents.json index cceb0197da..e7adadab1f 100644 --- a/sjp-query/sjp-query-view/src/raml/json/sjp.query.case-documents.json +++ b/sjp-query/sjp-query-view/src/raml/json/sjp.query.case-documents.json @@ -5,7 +5,8 @@ "materialId": "87a321ef-ea34-47d7-865d-7f430b4775be", "documentType": "PLEA", "documentNumber": 1, - "addedAt": "2018-08-26T14:55:47.631Z" + "addedAt": "2018-08-26T14:55:47.631Z", + "documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf" } ] } \ No newline at end of file diff --git a/sjp-query/sjp-query-view/src/raml/json/sjp.query.result-orders.json b/sjp-query/sjp-query-view/src/raml/json/sjp.query.result-orders.json index 5105c84e68..103faaf7a8 100644 --- a/sjp-query/sjp-query-view/src/raml/json/sjp.query.result-orders.json +++ b/sjp-query/sjp-query-view/src/raml/json/sjp.query.result-orders.json @@ -8,6 +8,7 @@ }, "order": { "documentId": "670490ee-95be-4e3e-af8b-9bef865e26ba", + "documentUri": "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf", "addedAt": "2016-11-17T10:00:00.000Z" } }, diff --git a/sjp-query/sjp-query-view/src/test/java/uk/gov/moj/cpp/sjp/query/view/CaseDocumentViewTest.java b/sjp-query/sjp-query-view/src/test/java/uk/gov/moj/cpp/sjp/query/view/CaseDocumentViewTest.java index 0285f9e071..aa4b09fe09 100644 --- a/sjp-query/sjp-query-view/src/test/java/uk/gov/moj/cpp/sjp/query/view/CaseDocumentViewTest.java +++ b/sjp-query/sjp-query-view/src/test/java/uk/gov/moj/cpp/sjp/query/view/CaseDocumentViewTest.java @@ -1,6 +1,7 @@ package uk.gov.moj.cpp.sjp.query.view; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import uk.gov.moj.cpp.sjp.persistence.entity.CaseDocument; import uk.gov.moj.cpp.sjp.query.view.response.CaseDocumentView; @@ -38,6 +39,21 @@ public void testOrder() { assertEquals(2, (int) caseDocumentViews.get(2).getDocumentNumber()); } + @Test + public void shouldMapDocumentUriThroughFromTheEntity() { + final String documentUri = "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf"; + + final CaseDocumentView blobAddressed = new CaseDocumentView( + new CaseDocument(null, null, "SJPN", null, null, 1, documentUri)); + final CaseDocumentView fileServiceAddressed = new CaseDocumentView( + new CaseDocument(null, null, "SJPN", null, null, 1, null)); + + assertEquals(documentUri, blobAddressed.getDocumentUri()); + // Jackson serialises with NON_ABSENT, so a null here is omitted entirely and the + // file-service response shape is unchanged. + assertNull(fileServiceAddressed.getDocumentUri()); + } + @Test public void exceptionIsNotThrownWhenDocumentTypeIsNotDefined() { //given diff --git a/sjp-query/sjp-query-view/src/test/java/uk/gov/moj/cpp/sjp/query/view/service/CaseServiceTest.java b/sjp-query/sjp-query-view/src/test/java/uk/gov/moj/cpp/sjp/query/view/service/CaseServiceTest.java index fc664585e9..2d96c5eb3b 100644 --- a/sjp-query/sjp-query-view/src/test/java/uk/gov/moj/cpp/sjp/query/view/service/CaseServiceTest.java +++ b/sjp-query/sjp-query-view/src/test/java/uk/gov/moj/cpp/sjp/query/view/service/CaseServiceTest.java @@ -100,6 +100,8 @@ @ExtendWith(MockitoExtension.class) public class CaseServiceTest { + private static final String DOCUMENT_URI = "https://sadevfilestore.blob.core.windows.net/stack-stagingdvla/generated/plea.pdf"; + private static final UUID CASE_ID = randomUUID(); private static final CaseStatus CASE_STATUS_REFERRED_FOR_COURT_HEARING = CaseStatus.REFERRED_FOR_COURT_HEARING; private static final String URN = "TFL1234"; @@ -453,6 +455,19 @@ public void shouldFindCaseDocuments() { final CaseDocumentView firstCaseDocument = caseDocumentsView.getCaseDocuments().get(0); assertThat(firstCaseDocument.getId().toString(), is(documentId.toString())); assertThat(firstCaseDocument.getDocumentNumber(), is(2)); + assertThat(firstCaseDocument.getDocumentUri(), is(nullValue())); + } + + @Test + public void shouldExposeDocumentUriForABlobAddressedCaseDocument() { + // For a blob-addressed document the id is a uuid derived from the uri rather than a file + // service id, so any response carrying the id has to carry the uri with it. + final CaseDocument caseDocument = new CaseDocument(randomUUID(), randomUUID(), "SJPN", clock.now(), CASE_ID, 2, DOCUMENT_URI); + when(caseRepository.findCaseDocuments(CASE_ID)).thenReturn(singletonList(caseDocument)); + + final CaseDocumentsView caseDocumentsView = service.findCaseDocuments(CASE_ID); + + assertThat(caseDocumentsView.getCaseDocuments().get(0).getDocumentUri(), is(DOCUMENT_URI)); } @Test @@ -468,6 +483,7 @@ public void shouldFindCaseDocument() { assertThat(caseDocumentView.get().getMaterialId(), is(caseDocument.getMaterialId())); assertThat(caseDocumentView.get().getDocumentType(), is(caseDocument.getDocumentType())); assertThat(caseDocumentView.get().getDocumentNumber(), is(caseDocument.getDocumentNumber())); + assertThat(caseDocumentView.get().getDocumentUri(), is(nullValue())); } @Test @@ -691,6 +707,8 @@ public void shouldFindResultOrders() { resultOrdersView.getResultOrders().get(0).getOrder().getDocumentId()); assertEquals(caseDocument.getAddedAt(), resultOrdersView.getResultOrders().get(0).getOrder().getAddedAt()); + assertEquals(caseDocument.getDocumentUri(), + resultOrdersView.getResultOrders().get(0).getOrder().getDocumentUri()); } @Test From 10594e4de4a72ea16a1b8ee781dec4dcdeede686 Mon Sep 17 00:00:00 2001 From: aykutdanisman Date: Sat, 26 Sep 2026 17:39:32 +0100 Subject: [PATCH 2/2] CHD-3035 - add assertions to blob-addressed processor test (sonar S2699) Co-Authored-By: Claude Opus 5.5 --- .../event/processor/CaseDocumentUploadedProcessorTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentUploadedProcessorTest.java b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentUploadedProcessorTest.java index 385104d7a8..5e7c618353 100644 --- a/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentUploadedProcessorTest.java +++ b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/CaseDocumentUploadedProcessorTest.java @@ -209,6 +209,10 @@ public void shouldHandleABlobAddressedDocumentWithoutError() { .add("documentType", DOCUMENT_TYPE).build(); caseDocumentProcessor.handleCaseDocumentUploaded(createEnvelope("sjp.events.case-document-uploaded", payload)); + + verify(sender, times(2)).send(envelopeCaptor.capture()); + assertThat(envelopeCaptor.getAllValues().get(0).metadata().name(), is("public.sjp.case-document-uploaded")); + assertThat(envelopeCaptor.getAllValues().get(1).metadata().name(), is("material.command.upload-file")); } @Test