Skip to content

Conversation

shusheer
Copy link

Overview

Key Changes

  • expose binary_storage_mode and binary_storage_filesystem_base_directory through AppProperties, keeping DATABASE as the default.
  • update FhirServerConfigCommon to instantiate the appropriate IBinaryStorageSvc, validate the filesystem base path, and apply a 100 KB inline threshold automatically when no minimum is provided.
  • ensure JpaStorageSettings receives the same threshold so inline text handling matches the storage service.
  • document the new knobs in README and sample application.yaml.
  • add FhirServerConfigCommonBinaryStorageTest verifying default/database wiring, filesystem mode, min-size overrides (including zero), and base-directory validation.

Testing

  • mvn -Dtest=FhirServerConfigCommonBinaryStorageTest test
  • manual runs:
    1. filesystem with default threshold → 200 KB payload stored on disk, 32 KB remains inline.
    2. filesystem with inline_resource_storage_below_size: 16384 → 32 KB payload stored on disk, 8 KB remains inline.

@jkiddo jkiddo requested a review from Copilot September 30, 2025 04:58
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds configurable filesystem binary storage to the HAPI FHIR JPA Server Starter, allowing users to store binary data on disk instead of in the database without requiring custom beans.

  • Introduces binary_storage_mode and binary_storage_filesystem_base_directory configuration properties
  • Implements automatic 100 KB inline threshold when filesystem mode is enabled without explicit size configuration
  • Adds comprehensive test coverage for the new binary storage configuration options

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
AppProperties.java Adds new enum and properties for binary storage mode and filesystem base directory configuration
FhirServerConfigCommon.java Implements logic to instantiate appropriate binary storage service based on configuration with validation
application.yaml Documents the new configuration options with examples and usage notes
README.md Adds documentation section explaining how to configure filesystem binary storage
FhirServerConfigCommonBinaryStorageTest.java Comprehensive test suite covering all configuration scenarios and validation

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 365 to 367
if (appProperties.getMax_binary_size() != null) {
filesystemSvc.setMaximumBinarySize(appProperties.getMax_binary_size().longValue());
}
Copy link
Preview

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

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

Inconsistent type handling for max_binary_size. The filesystem implementation uses .longValue() while the database implementation on line 373 passes the Integer directly. This could cause type compatibility issues.

Copilot uses AI. Check for mistakes.

Comment on lines 228 to 232
Integer inlineResourceThreshold = appProperties.getInline_resource_storage_below_size();
if (inlineResourceThreshold == null
&& appProperties.getBinary_storage_mode() == AppProperties.BinaryStorageMode.FILESYSTEM) {
inlineResourceThreshold = resolveFilesystemMinimumBinarySize(appProperties);
}
Copy link
Preview

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

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

The logic for resolving the inline resource threshold is duplicated between jpaStorageSettings() and binaryStorageSvc() methods. Consider extracting this threshold resolution logic to avoid code duplication.

Suggested change
Integer inlineResourceThreshold = appProperties.getInline_resource_storage_below_size();
if (inlineResourceThreshold == null
&& appProperties.getBinary_storage_mode() == AppProperties.BinaryStorageMode.FILESYSTEM) {
inlineResourceThreshold = resolveFilesystemMinimumBinarySize(appProperties);
}
Integer inlineResourceThreshold = resolveInlineResourceThreshold(appProperties);

Copilot uses AI. Check for mistakes.

@Bean
public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) {
DatabaseBinaryContentStorageSvcImpl binaryStorageSvc = new DatabaseBinaryContentStorageSvcImpl();
IBinaryStorageSvc binaryStorageSvc;
Copy link
Collaborator

Choose a reason for hiding this comment

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

The implementations of IBinaryStorageSvc should be defined as Beans and injected as such.

Copy link
Collaborator

@jkiddo jkiddo left a comment

Choose a reason for hiding this comment

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

See comments above

if (appProperties.getMax_binary_size() != null) {
binaryStorageSvc.setMaximumBinarySize(appProperties.getMax_binary_size());
@Lazy
@Bean
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of having these declared as lazy you can follow the approach like in

that will guarantee you that only one of those implementation beans created. Then you dont need the ObjectProvider above as well

@shusheer
Copy link
Author

shusheer commented Oct 4, 2025

Apologies, tried to rebase to avoid the merging issues, but may have added complexity in doing so. Hopefully all is clear

@jkiddo jkiddo requested a review from Copilot October 5, 2025 14:17
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@jkiddo
Copy link
Collaborator

jkiddo commented Oct 5, 2025

Looks good so far. Please add an integration test that proves it works when not using the default values.

@jkiddo
Copy link
Collaborator

jkiddo commented Oct 6, 2025

With integration tests I mean an entire passthrough like with the rest of the integration tests - which are more end2end.

@jkiddo jkiddo requested a review from Copilot October 6, 2025 06:56
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +384 to +389
# validation:
# requests_enabled: true
# responses_enabled: true
# binary_storage_enabled: true
# binary_storage_mode: FILESYSTEM
# binary_storage_filesystem_base_directory: /binstore
Copy link
Preview

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

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

Inconsistent indentation inside commented sample properties (extra spaces after #) may confuse users when uncommenting, potentially producing mis-indented YAML. Align these comment lines with surrounding examples (e.g., ' # validation:' and nested keys indented two spaces after the #) for consistency.

Suggested change
# validation:
# requests_enabled: true
# responses_enabled: true
# binary_storage_enabled: true
# binary_storage_mode: FILESYSTEM
# binary_storage_filesystem_base_directory: /binstore
# validation:
# requests_enabled: true
# responses_enabled: true
# binary_storage_enabled: true
# binary_storage_mode: FILESYSTEM
# binary_storage_filesystem_base_directory: /binstore

Copilot uses AI. Check for mistakes.

Comment on lines 215 to 219
private Path baseDir() throws IOException {
Path baseDir = Paths.get(BASE_DIRECTORY).toAbsolutePath();
Files.createDirectories(baseDir);
return baseDir;
}
Copy link
Preview

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

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

Duplicated baseDir() implementations in two test classes increase maintenance overhead. Extract this method into the BaseBinaryStorageIntegrationTest (parameterized by a supplied directory constant) or a shared utility to eliminate duplication.

Copilot uses AI. Check for mistakes.

Comment on lines 275 to 279
private Path baseDir() throws IOException {
Path baseDir = Paths.get(BASE_DIRECTORY).toAbsolutePath();
Files.createDirectories(baseDir);
return baseDir;
}
Copy link
Preview

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

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

Duplicated baseDir() implementations in two test classes increase maintenance overhead. Extract this method into the BaseBinaryStorageIntegrationTest (parameterized by a supplied directory constant) or a shared utility to eliminate duplication.

Copilot uses AI. Check for mistakes.

@jkiddo jkiddo requested a review from Copilot October 7, 2025 19:15
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 346 to +381
@Bean
public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) {
DatabaseBinaryContentStorageSvcImpl binaryStorageSvc = new DatabaseBinaryContentStorageSvcImpl();
@ConditionalOnProperty(prefix = "hapi.fhir", name = "binary_storage_mode", havingValue = "FILESYSTEM")
public FilesystemBinaryStorageSvcImpl filesystemBinaryStorageSvc(AppProperties appProperties) {
String baseDirectory = appProperties.getBinary_storage_filesystem_base_directory();
Assert.hasText(
baseDirectory,
"binary_storage_filesystem_base_directory must be provided when binary_storage_mode=FILESYSTEM");

FilesystemBinaryStorageSvcImpl filesystemSvc = new FilesystemBinaryStorageSvcImpl(baseDirectory);
Integer inlineResourceThreshold = resolveInlineResourceThreshold(appProperties);
int minimumBinarySize =
inlineResourceThreshold == null ? DEFAULT_FILESYSTEM_INLINE_THRESHOLD : inlineResourceThreshold;
filesystemSvc.setMinimumBinarySize(minimumBinarySize);

Integer maxBinarySize = appProperties.getMax_binary_size();
if (maxBinarySize != null) {
filesystemSvc.setMaximumBinarySize(maxBinarySize.longValue());
}

return filesystemSvc;
}

if (appProperties.getMax_binary_size() != null) {
binaryStorageSvc.setMaximumBinarySize(appProperties.getMax_binary_size());
@Bean
@ConditionalOnProperty(
prefix = "hapi.fhir",
name = "binary_storage_mode",
havingValue = "DATABASE",
matchIfMissing = true)
public DatabaseBinaryContentStorageSvcImpl databaseBinaryStorageSvc(AppProperties appProperties) {
DatabaseBinaryContentStorageSvcImpl databaseSvc = new DatabaseBinaryContentStorageSvcImpl();
Integer maxBinarySize = appProperties.getMax_binary_size();
if (maxBinarySize != null) {
databaseSvc.setMaximumBinarySize(maxBinarySize.longValue());
}
return databaseSvc;
}
Copy link
Preview

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

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

Previously there was a single bean named binaryStorageSvc (returning IBinaryStorageSvc); replacing it with two differently named concrete beans removes that bean name and can break external customizations relying on @qualifier("binaryStorageSvc") or bean name overrides. Consider adding a unifying @bean(name = "binaryStorageSvc") that returns IBinaryStorageSvc and delegates to the active implementation, or add @primary plus an alias to preserve backward compatibility.

Copilot uses AI. Check for mistakes.

Copy link
Collaborator

Choose a reason for hiding this comment

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

no - that is a deliberate choice.

Comment on lines +355 to +363
Integer inlineResourceThreshold = resolveInlineResourceThreshold(appProperties);
int minimumBinarySize =
inlineResourceThreshold == null ? DEFAULT_FILESYSTEM_INLINE_THRESHOLD : inlineResourceThreshold;
filesystemSvc.setMinimumBinarySize(minimumBinarySize);

Integer maxBinarySize = appProperties.getMax_binary_size();
if (maxBinarySize != null) {
filesystemSvc.setMaximumBinarySize(maxBinarySize.longValue());
}
Copy link
Preview

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

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

Negative values for inline_resource_storage_below_size or max_binary_size are not validated and could lead to unexpected behavior. Add explicit non-negative assertions (e.g., Assert.isTrue(value >= 0, "...") ) before applying these values.

Copilot uses AI. Check for mistakes.

@shusheer
Copy link
Author

shusheer commented Oct 8, 2025

I see you disagree with copilot on one point, would you like me to implement the non-negative assertion tests? Anything else to adjust?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants