Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ buildContexts.sh

lightning-jenkins.properties
**/.DS_Store

# Claude Code
.claude/
38 changes: 33 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
<parent>
<groupId>uk.gov.moj.cpp.common</groupId>
<artifactId>service-parent-pom</artifactId>
<version>17.103.11</version>
<version>17.104.3</version>
</parent>

<groupId>uk.gov.moj.cpp.sjp</groupId>
<artifactId>sjp-parent</artifactId>
<version>17.103.170-SNAPSHOT</version>
<version>17.104.1-SNAPSHOT</version>
<packaging>pom</packaging>

<properties>
Expand Down Expand Up @@ -47,13 +47,13 @@
<scheduling.version>17.0.11</scheduling.version>
<authorisation.service.version>0.1.49</authorisation.service.version>
<usersgroups.version>17.104.48</usersgroups.version>
<progression.version>17.0.249</progression.version>
<progression.version>17.0.260</progression.version>
<prosecutioncasefile.version>17.0.66</prosecutioncasefile.version>
<access-control-sjp-providers.version>4.1.4-ATCM</access-control-sjp-providers.version>
<cpp.framework.action.mapping.enabled>true</cpp.framework.action.mapping.enabled>
<mvn.site.url>file://${project.build.directory}/site</mvn.site.url>
<file.alfresco.version>2.3.0</file.alfresco.version>
<coredomain.version>17.103.12</coredomain.version>
<coredomain.version>17.103.13</coredomain.version>
<criminal-court-public-model.version>${coredomain.version}</criminal-court-public-model.version>
<javax.el-api.version>3.0.0</javax.el-api.version>
<joda-time.version>2.10</joda-time.version>
Expand All @@ -66,14 +66,18 @@
<argLine>-Djava.locale.providers=CLDR</argLine>
<elasticsearch-maven-plugin.version>6.13</elasticsearch-maven-plugin.version>
<elasticsearch.embedded.version>7.16.2</elasticsearch.embedded.version>
<stagingenforcement.version>17.103.76</stagingenforcement.version>
<stagingenforcement.version>17.104.81</stagingenforcement.version>
<unifiedsearch.version>17.0.26</unifiedsearch.version>
<jsonassert.version>1.5.0</jsonassert.version>
<junit4.version>4.13.1</junit4.version>
<maven.cucumber.reporting.version>2.0.0</maven.cucumber.reporting.version>
<jgitflow-maven-plugin.version>1.0-m5.1</jgitflow-maven-plugin.version>
<jgitflow.maven.developBranchName>main</jgitflow.maven.developBranchName>
<h2.version>1.4.196</h2.version>
<!-- JDK HTTP transport for Azure SDK — avoids Netty conflicts with WildFly 26's bundled Netty modules. Not in common-bom. -->
<azure-core-http-jdk-httpclient.version>1.0.0-beta.14</azure-core-http-jdk-httpclient.version>
<!-- junit-jupiter aggregate is not in common-bom (individual sub-modules are). Must match common-bom's junit sub-module version. -->
<junit.jupiter.version>5.9.3</junit.jupiter.version>
</properties>

<modules>
Expand All @@ -87,6 +91,7 @@
<module>sjp-json</module>
<module>sjp-event-sources</module>
<module>sjp-healthchecks</module>
<module>sjp-file-store</module>
</modules>

<build>
Expand Down Expand Up @@ -158,6 +163,29 @@
<artifactId>sjp-json</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.moj.cpp.sjp</groupId>
<artifactId>sjp-file-store-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.moj.cpp.sjp</groupId>
<artifactId>sjp-file-store-test-utils</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!-- JDK HTTP transport for Azure SDK — not in common-bom -->
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core-http-jdk-httpclient</artifactId>
<version>${azure-core-http-jdk-httpclient.version}</version>
</dependency>
<!-- junit-jupiter aggregate — not in common-bom; individual sub-modules are -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>test-utils-enveloper-provider</artifactId>
Expand Down
1 change: 0 additions & 1 deletion runIntegrationTests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ runLiquibase() {
runViewStoreLiquibase
runSystemLiquibase
runEventTrackingLiquibase
runFileServiceLiquibase
runActivitiLiquibase
printf "${CYAN}All liquibase $LIQUIBASE_COMMAND scripts run${NO_COLOUR}\n\n"
}
Expand Down
2 changes: 1 addition & 1 deletion sjp-command/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<artifactId>sjp-parent</artifactId>
<groupId>uk.gov.moj.cpp.sjp</groupId>
<version>17.103.170-SNAPSHOT</version>
<version>17.104.1-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>
Expand Down
13 changes: 4 additions & 9 deletions sjp-command/sjp-command-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<artifactId>sjp-command</artifactId>
<groupId>uk.gov.moj.cpp.sjp</groupId>
<version>17.103.170-SNAPSHOT</version>
<version>17.104.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down Expand Up @@ -35,14 +35,9 @@
</dependency>

<dependency>
<groupId>uk.gov.justice.framework-generators</groupId>
<artifactId>rest-adapter-file-service</artifactId>
<version>${cpp.framework.version}</version>
</dependency>

<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>file-service-persistence</artifactId>
<groupId>uk.gov.moj.cpp.sjp</groupId>
<artifactId>sjp-file-store-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.moj.cpp.core.domain</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package uk.gov.moj.cpp.sjp.command.api;

import static uk.gov.justice.services.core.annotation.Component.COMMAND_API;
import static uk.gov.justice.services.messaging.JsonEnvelope.envelopeFrom;
import static uk.gov.justice.services.messaging.JsonEnvelope.metadataFrom;

import uk.gov.justice.services.core.annotation.Handles;
import uk.gov.justice.services.core.annotation.ServiceComponent;
import uk.gov.justice.services.core.sender.Sender;
import uk.gov.justice.services.messaging.JsonEnvelope;
import uk.gov.justice.services.messaging.Metadata;

import javax.inject.Inject;

@ServiceComponent(COMMAND_API)
public class IngestFileApi {

@Inject
private Sender sender;

@Handles("sjp.ingest-file")
public void ingestFile(final JsonEnvelope envelope) {
final Metadata metadata = metadataFrom(envelope.metadata())
.withName("sjp.command.ingest-file")
.build();
sender.send(envelopeFrom(metadata, envelope.payload()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ public static List<String> getDeleteCaseDocumentActionGroups() {
return asList(SECOND_LINE_SUPPORT);
}

public static List<String> getIngestFileActionGroups() {
return asList(GROUP_SYSTEM_USERS);
}

public static List<String> getCaseCompleteBdfGroups() {
return asList(GROUP_SYSTEM_USERS);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package uk.gov.moj.cpp.sjp.command.interceptor;

public class SjpDocumentUploadException extends RuntimeException {

public SjpDocumentUploadException(final String message, final Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,47 +1,72 @@
package uk.gov.moj.cpp.sjp.command.interceptor;

import uk.gov.justice.services.adapter.rest.interceptor.MultipleFileInputDetailsService;
import uk.gov.justice.services.adapter.rest.interceptor.ResultsHandler;
import static java.util.UUID.randomUUID;
import static javax.json.Json.createObjectBuilder;
import static uk.gov.justice.services.messaging.Envelope.metadataFrom;
import static uk.gov.justice.services.messaging.JsonEnvelope.envelopeFrom;
import static uk.gov.moj.cpp.sjp.filestore.azure.StoragePath.internal;

import uk.gov.justice.services.adapter.rest.multipart.FileInputDetails;
import uk.gov.justice.services.common.exception.ForbiddenRequestException;
import uk.gov.justice.services.core.interceptor.Interceptor;
import uk.gov.justice.services.core.interceptor.InterceptorChain;
import uk.gov.justice.services.core.interceptor.InterceptorContext;
import uk.gov.justice.services.messaging.JsonEnvelope;
import uk.gov.moj.cpp.sjp.filestore.azure.FileStorer;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import javax.inject.Inject;
import javax.json.JsonObjectBuilder;

public class SjpServiceFileInterceptor implements Interceptor {

@Inject
DocumentTypeValidator documentTypeValidator;

@Inject
MultipleFileInputDetailsService multipleFileInputDetailsService;

@Inject
ResultsHandler resultsHandler;
private FileStorer fileStorer;

@Override
public InterceptorContext process(final InterceptorContext interceptorContext, final InterceptorChain interceptorChain) {
final Optional<Object> inputParameterOptional = interceptorContext.getInputParameter("fileInputDetailsList");
final Optional<Object> inputParameterOptional = interceptorContext.getInputParameter(FileInputDetails.FILE_INPUT_DETAILS_LIST);
if (inputParameterOptional.isPresent()) {
final List<FileInputDetails> fileInputDetails = (List) inputParameterOptional.get();
for (final FileInputDetails filedetails : fileInputDetails) {
final String fileNameWithExtention = filedetails.getFileName();
if (!documentTypeValidator.isValid(fileNameWithExtention)) {
final List<FileInputDetails> fileInputDetailsList = (List<FileInputDetails>) inputParameterOptional.get();
for (final FileInputDetails fileDetails : fileInputDetailsList) {
if (!documentTypeValidator.isValid(fileDetails.getFileName())) {
throw new ForbiddenRequestException("Allowed only doc|docx|jpg|jpeg|pdf|txt extensions");
}
}
final Map<String, UUID> results = this.multipleFileInputDetailsService.storeFileDetails(fileInputDetails);
final JsonEnvelope inputEnvelope = this.resultsHandler.addResultsTo(interceptorContext.inputEnvelope(), results);
return interceptorChain.processNext(interceptorContext.copyWithInput(inputEnvelope));
final Map<String, UUID> results = storeFiles(fileInputDetailsList);
final JsonEnvelope modifiedEnvelope = addResultsToEnvelope(interceptorContext.inputEnvelope(), results);
return interceptorChain.processNext(interceptorContext.copyWithInput(modifiedEnvelope));
} else {
return interceptorChain.processNext(interceptorContext);
}
}
}

private Map<String, UUID> storeFiles(final List<FileInputDetails> fileInputDetailsList) {
final Map<String, UUID> results = new HashMap<>();
for (final FileInputDetails fileDetails : fileInputDetailsList) {
try (final InputStream inputStream = fileDetails.getInputStream()) {
final UUID fileId = fileStorer.store(internal(), randomUUID(), fileDetails.getFileName(), inputStream);
results.put(fileDetails.getFieldName(), fileId);
} catch (final IOException e) {
throw new SjpDocumentUploadException("Failed to store uploaded file: " + fileDetails.getFileName(), e);
}
}
return results;
}

private JsonEnvelope addResultsToEnvelope(final JsonEnvelope envelope, final Map<String, UUID> results) {
final JsonObjectBuilder payloadBuilder = createObjectBuilder(envelope.payloadAsJsonObject());
results.forEach((fieldName, fileId) -> payloadBuilder.add(fieldName, fileId.toString()));
return envelopeFrom(metadataFrom(envelope.metadata()), payloadBuilder.build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package uk.gov.moj.cpp.sjp.command.api.accesscontrol;

import uk.gov.moj.cpp.accesscontrol.drools.Outcome;
import uk.gov.moj.cpp.accesscontrol.drools.Action;
import java.util.Arrays;
import uk.gov.moj.cpp.sjp.command.api.accesscontrol.RuleConstants;

global uk.gov.moj.cpp.accesscontrol.common.providers.UserAndGroupProvider userAndGroupProvider;

rule "Command - Rule for Ingesting File"
when
$outcome: Outcome();
$action: Action(name == "sjp.ingest-file");
eval(userAndGroupProvider.isMemberOfAnyOfTheSuppliedGroups($action, RuleConstants.getIngestFileActionGroups()));
then
$outcome.setSuccess(true);
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "http://justice.gov.uk/json/schemas/domains/sjp/sjp.ingest-file.json",
"type": "object",
"properties": {
"sourceUri": {
"type": "string"
},
"correlationId": {
"$ref": "http://justice.gov.uk/domain/core/common/definitions.json#/definitions/uuid"
},
"filename": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"sourceUri",
"correlationId",
"filename"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sourceUri": "https://storage.blob.core.windows.net/systemdocgenerator/published/sjp-docs/184416a9-ef20-4500-a9c1-f64b87b424a9?sv=2021-08-06&se=2026-05-15T12%3A00%3A00Z&sr=b&sp=r&sig=example",
"correlationId": "384416a9-ef20-4500-a9c1-f64b87b424a0",
"filename": "transparency_report_2026-05-15.pdf"
}
25 changes: 25 additions & 0 deletions sjp-command/sjp-command-api/src/raml/sjp-command-api.raml
Original file line number Diff line number Diff line change
Expand Up @@ -1010,3 +1010,28 @@ baseUri: http://localhost:8080/sjp-command-api/command/api/rest/sjp
description: Internal Server Error
202:
description: Accepted

/files/{fileId}:
uriParameters:
fileId:
description: The ID to assign to the ingested file in SJP's own container
type: string
post:
description: |
Ingest a file from a source SAS URI into SJP's Azure Blob container (UC2 receiver).
...
(mapping):
requestType: application/vnd.sjp.ingest-file+json
name: sjp.ingest-file
...
body:
application/vnd.sjp.ingest-file+json:
example:
!include json/sjp.ingest-file.json
schema:
!include json/schema/sjp.ingest-file.json
responses:
500:
description: Internal Server Error
202:
description: Accepted
Loading