Skip to content

PO-3948 Implement get interface file content api#73

Open
thomas-croasdale-cgi wants to merge 16 commits into
masterfrom
PO-3948
Open

PO-3948 Implement get interface file content api#73
thomas-croasdale-cgi wants to merge 16 commits into
masterfrom
PO-3948

Conversation

@thomas-croasdale-cgi

@thomas-croasdale-cgi thomas-croasdale-cgi commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

### JIRA link ###

Implements the get interface file content api.

Does this PR introduce a breaking change? (check one with "x")

[ ] Yes
[X] No

@thomas-croasdale-cgi
thomas-croasdale-cgi requested a review from a team July 16, 2026 14:29
@samnaylorcgi

Copy link
Copy Markdown
Contributor

The suppression for the failing dependency is expired, but this has been updated in master, so rebase to fix

@Configuration
public class CapsReportBaisFileProcessorConfig implements BaisFileProcessorConfig {

@Value("${opal.file-handling-service.file-types.CAPS-report.storage-container-name}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @ConfigurationProperties("opal.file-handling-service.file-types.caps-report") as a top-level annotation would be preferred to @Value, especially when the configurations will have many fields

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread config/owasp/suppressions.xml
Comment thread src/main/java/uk/gov/hmcts/opal/filehandler/service/InterfaceFileService.java Outdated
Comment thread src/main/java/uk/gov/hmcts/opal/filehandler/service/InterfaceFileService.java Outdated
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix = "opal.file-handling-service.file-types.bteckoh-report")
@Component("BTECKOH_REPORT")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add config to the name please

import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix = "opal.file-handling-service.file-types.caps-report")
@Component("CAPS_REPORT")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add config to the name please

Comment on lines +26 to +27
@Autowired
private Map<String, BaisFileProcessorConfig> configs;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is the mapping done here?

id, entity.getStatus()));
}

BaisFileProcessorConfig config = configs.get(entity.getSource().toString());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be its own method in which we provide a source enum and we get the config?

As this will be re-used later


public class PermissionUtil {

public static void checkPermission(PermissionDescriptor permission) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing unit tests

import org.springframework.web.server.ResponseStatusException;

@Getter
public class BlobNotFoundException extends ResponseStatusException {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing unit tests

import org.springframework.web.server.ResponseStatusException;

@Getter
public class BlobStorageContainerNotFoundException extends ResponseStatusException {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing unit tests

import org.springframework.web.server.ResponseStatusException;

@Getter
public class InvalidInterfaceFileStatusException extends ResponseStatusException {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing unit tests

import org.springframework.context.annotation.Configuration;

@Configuration
public class FileHandlerAzureStorageConfig {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing unit tests


private final BlobServiceClient blobServiceClient;

private BlobContainerClient getBlobContainerClient(String containerName) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be tested in isolation (so remove the private from the method to make it package-private or public (as we may re-use this method) to support isolated testing)

return container;
}

private BlobClient getBlobClient(BlobContainerClient blobContainerClient, String file) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

return blob;
}

private BinaryData getFileContents(BlobClient blob) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

Comment on lines +136 to +139
assertThrows(
PermissionNotAllowedException.class,
() -> interfaceFileService.getInterfaceFilesContent(1L)
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to validate message is descriptive

Optional.ofNullable(null)
);

assertThrows(EntityNotFoundException.class, () -> interfaceFileService.getInterfaceFilesContent(1L));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to validate message is descriptive

Optional.of(buildEntity(1L, uuid, Interface.BTECKOH_REPORT, Status.FAILED))
);

assertThrows(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to validate message is descriptive

when(blobStoreService.fetchInterfaceFile(eq(1L), eq(uuid), eq("bteckoh-report")))
.thenThrow(BlobNotFoundException.class);

assertThrows(BlobNotFoundException.class, () -> interfaceFileService.getInterfaceFilesContent(1L));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to validate message is descriptive

private static final int AZURITE_BLOB_PORT = 10000;
private static final String AZURITE_ACCOUNT_NAME = "devstoreaccount1";
private static final String AZURITE_ACCOUNT_KEY =
"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where has this account key come from?
Is this exclusively for local dev using our docker images?
Or has this come from our secret store?

If exclusively local can a comment be added to indicate indicate this so it does not get picked up in security reviews

.buildClient();
}

public String storeReport(String report, String containerName, String uuid) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the names be made generic as we are not storing reports here rather we are string documents?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming this is a test file with no sensitive data?

@@ -0,0 +1 @@
DELETE FROM interface_files WHERE interface_file_id IN (1, 2, 3, 4); No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can likely delete all interface files in this SQL instead of just select ons to clean them up

);

res.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to validate the returned content is what we are expecting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants