Skip to content

feat(dispatch): multi s3 tenant - #153

Draft
simonhir wants to merge 12 commits into
mainfrom
feat/dispatch-multi-s3-tenant
Draft

feat(dispatch): multi s3 tenant#153
simonhir wants to merge 12 commits into
mainfrom
feat/dispatch-multi-s3-tenant

Conversation

@simonhir

@simonhir simonhir commented Mar 28, 2025

Copy link
Copy Markdown
Member

Description

Dispatch implement s3 multi tenant support.

Reference

Issues closes #143

Summary by CodeRabbit

  • New Features

    • Added comprehensive multi-tenant support for file storage operations. Configuration now groups storage settings by tenant, ensuring tenant-specific handling.
    • Introduced a new FileEvent data structure for improved file event processing.
  • Refactor

    • Consolidated file event processing into a unified data structure, resulting in streamlined error handling and improved file dispatch operations.
    • Updated various methods to include tenant context, enhancing the adaptability of file operations.
    • Enhanced presigned URL verification to return detailed file information and validate tenant-specific access.
    • Improved configuration and client management for multi-tenant S3 access with caching of tenant clients.

These improvements enhance the overall robustness of file management and provide more scalable, tenant-aware operations.

@simonhir simonhir self-assigned this Mar 28, 2025
@coderabbitai

coderabbitai Bot commented Mar 28, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This update refactors the dispatch-service to support multi-tenant operations. The changes remove the old FileEventDTO record and replace it with a new FileEvent record, update method signatures across adapters, ports, and use cases to include a tenant parameter, and modify S3 interactions to use tenant-specific clients. Domain models, configuration files, and tests have been updated accordingly to ensure that all file operations—including error handling and protocol processing—are now tenant-aware.

Changes

File(s) Change Summary
Streaming DTO Removal
.../adapter/in/streaming/FileEventDTO.java, .../adapter/out/streaming/FileEventDTO.java
Removed the record FileEventDTO from both streaming adapter packages.
Streaming Adapter Updates
.../adapter/in/streaming/StreamingInAdapter.java, .../adapter/out/streaming/StreamingOutAdapter.java
Updated method signatures and event instantiation to replace FileEventDTO with the new FileEvent record.
S3 Adapter and Properties Refactor
.../adapter/out/s3/S3Adapter.java, .../adapter/out/s3/S3Properties.java
Added tenant parameter to multiple S3 method signatures; replaced single MinioClient with tenant-aware client retrieval; introduced fileFromPresignedUrl and tenant connection management.
Application Ports and Use Cases
.../application/port/in/ErrorHandlerInPort.java, .../application/port/in/MarkFileFinishedInPort.java, .../application/port/out/FileSystemOutPort.java, .../application/port/out/ReadProtocolOutPort.java, .../application/usecase/{DispatcherUseCase,ErrorHandlerUseCase,MarkFileFinishedUseCase,ProtocolProcessingUseCase,FileHandlingHelper}.java
Refactored error handling and file completion methods to accept FileEvent; added tenant parameters to file system operations; updated internal logic to handle tenant context.
Domain Model Changes
.../domain/model/File.java, .../domain/model/FileEvent.java, .../domain/model/UseCase.java
Added tenant field to File and UseCase; introduced new immutable FileEvent record; removed presigned URL parsing from File.
Configuration and Tests
.../resources/application-local.yml, .../resources/application-test.yml, .../test/**
Restructured configuration for multi-tenant S3 setup; updated use cases with tenant info; modified tests to include tenant context and updated file constructors and constants.
New Test Class
.../adapter/out/s3/S3AdapterTest.java
Added tests for S3Adapter tenant-aware presigned URL parsing and exception handling.

Sequence Diagram(s)

sequenceDiagram
    participant DC as DispatcherUseCase
    participant SA as Streaming Adapters
    participant FS as FileSystemOutPort/S3Adapter
    participant SP as S3Properties
    participant MC as MinioClient

    DC->>SA: dispatchFile(event with tenant)
    SA->>FS: Invoke file operations (tag, move, read) with tenant info
    FS->>SP: getClient(tenant)
    SP->>MC: Return tenant-specific client
    MC-->>SP: Client operations complete
    SP-->>FS: Client provided
    FS-->>DC: File processed/finished or error handled
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
Enable dispatch-service to use several tenants in parallel (#143)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes detected. All code changes align with the stated objectives related to multi-tenant support.

Poem

Hoppity hop, I’ve leaped through code today,
Adding tenant magic in a brand new way.
Files now dance with a tenant twist,
Multi-tenant dreams impossible to miss.
A rabbit’s smile in each refactored line—
Code so fresh, it’s simply divine!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/dispatch-multi-s3-tenant

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🧹 Nitpick comments (11)
dispatch-service/src/test/resources/application-test.yml (4)

2-5: Introduce Tenant-Specific S3 Configuration.
The new s3.tenants mapping correctly registers a test-tenant with its associated URL (https://s3.muenchen.de). This setup is essential for routing S3 operations under a multi-tenant strategy. Please verify that downstream components utilize this configuration correctly when instantiating tenant-specific S3 clients.


10-12: Link Use-Case 'test-meta' to Tenant.
The addition of tenant: test-tenant to the test-meta use-case ensures that file operations are directed to the correct tenant environment. Confirm that all relevant adapters and use case implementations correctly reference this tenant configuration.


21-23: Attach Tenant Information for Use-Case 'test-meta-dest'.
Adding the tenant: test-tenant parameter for the test-meta-dest use-case is consistent with the multi-tenancy objective. It’s important to ensure that tenant-specific routing in the business logic is updated accordingly.


31-33: Assign Tenant for Use-Case 'test2'.
The configuration now includes tenant: test-tenant for the test2 use-case, which promotes consistency across your use case configurations. Make sure that test scenarios and service components are updated to simulate and validate tenant-aware behavior.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/ReadProtocolOutPort.java (1)

17-17: JavaDoc needs to be updated to include the new tenant parameter.

The method signature now includes a tenant parameter, but the JavaDoc comments don't document this parameter. Consider updating the JavaDoc to include a description for the tenant parameter.

 /**
  * Load parsed protocol.
  *
  * @param bucket The bucket of the protocol file.
  * @param path The path of the protocol file.
+ * @param tenant The tenant identifier for multi-tenant support.
  * @return The parsed protocol entries.
  */
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/in/MarkFileFinishedInPort.java (1)

15-17: Good use of a structured object instead of multiple parameters.

Using a structured FileEvent object is a better design approach than using multiple separate parameters. The validation annotations ensure proper validation.

Consider enhancing the JavaDoc description to provide more detail about what information the FileEvent contains and its significance:

 /**
  * Mark a file as finished processing.
  *
- * @param event The event for the file to marks as finished.
+ * @param event The event for the file to mark as finished, containing useCase, presignedUrl, and metadata information.
  */
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/MarkFileFinishedUseCase.java (1)

33-39: Good addition of metadata file handling

The new code to handle metadata files is a valuable improvement. It properly checks if the metadata URL exists before processing it, verifies the URL, and finishes the file with tenant awareness.

Consider adding a log statement before processing the metadata file to aid in debugging, such as:

if (Strings.isNotBlank(event.metadataPresignedUrl())) {
+   log.debug("Processing metadata file from URL: {}", event.metadataPresignedUrl());
    // verify presigned url and extract metadata File
    final File metadataFile = this.fileSystemOutPort.verifyAndResolvePresignedUrl(event.metadataPresignedUrl());
    // finish metadata file
    fileHandlingHelper.finishFile(useCase, metadataFile.tenant(), metadataFile.bucket(), metadataFile.path());
}
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCase.java (1)

88-89: Check for potential repeated calls.
You're calling getMatchingFilesWithTags multiple times for similar operations. Consider extracting a helper method to reduce duplication and improve maintainability.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCase.java (1)

39-39: Fallback error notification.
Providing a fallback address and sending the presigned URL is a good fallback mechanism; just ensure no sensitive info is exposed in production logs.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Properties.java (1)

79-92: Tenant lookup by URL.
The findTenantByUrl method is straightforward. Consider normalizing URLs (e.g., removing trailing slashes) if needed to avoid mismatches. Otherwise, this is fine.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Adapter.java (1)

209-226: Refactor GET to HEAD for lighter verification (optional).
Using GET with Range=0-0 works but has some overhead. A HEAD request might be more efficient for verifying presence.

-connection.setRequestMethod("GET");
+connection.setRequestMethod("HEAD");
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76210e3 and 43334e2.

📒 Files selected for processing (26)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/in/streaming/FileEventDTO.java (0 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/in/streaming/StreamingInAdapter.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Adapter.java (17 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Properties.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/streaming/FileEventDTO.java (0 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/streaming/StreamingOutAdapter.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/in/ErrorHandlerInPort.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/in/MarkFileFinishedInPort.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/FileSystemOutPort.java (8 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/ReadProtocolOutPort.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCase.java (6 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCase.java (3 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/MarkFileFinishedUseCase.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCase.java (4 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/helper/FileHandlingHelper.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/File.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/FileEvent.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/UseCase.java (1 hunks)
  • dispatch-service/src/main/resources/application-local.yml (1 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/TestConstants.java (2 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCaseTest.java (9 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCaseTest.java (3 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/MarkFileFinishedUseCaseTest.java (3 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCaseTest.java (5 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/domain/model/FileTest.java (2 hunks)
  • dispatch-service/src/test/resources/application-test.yml (3 hunks)
💤 Files with no reviewable changes (2)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/streaming/FileEventDTO.java
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/in/streaming/FileEventDTO.java
🧰 Additional context used
🧬 Code Definitions (1)
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Properties.java (1)
dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/TestConstants.java (1)
  • NoArgsConstructor (10-31)
🪛 GitHub Actions: build all
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCase.java

[warning] 130-130: Error file processing test/inProcess/path/path.csv for use case test-meta

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCase.java

[error] 38-38: Error while handling error: java.lang.NullPointerException: Cannot invoke "de.muenchen.oss.swim.dispatcher.domain.model.File.tenant()" because "file" is null

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCase.java

[warning] 105-105: Error while processing file test/inProcess/path/example2.pdf for use case test-meta

🔇 Additional comments (72)
dispatch-service/src/main/resources/application-local.yml (1)

70-74: Multi-Tenant S3 Configuration Structured Correctly

The new nested structure under the s3 key—with tenant-specific settings under tenants.swm—is correctly implemented to support multi-tenant operations. Please ensure that the application components responsible for reading these configuration values have been updated accordingly to traverse this new hierarchy. Additionally, consider updating the configuration documentation so that it clearly reflects these changes for future maintainability and ease of onboarding.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/FileEvent.java (1)

5-9: Well-designed record for the file event domain model.

The introduction of this record as part of the domain model is appropriate for representing file events in a multi-tenant context. The validation constraints on required fields ensure proper input validation while keeping the structure immutable and concise.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/UseCase.java (1)

17-21: Good addition of tenant field to support multi-tenancy.

The new tenant field with proper validation and documentation is essential for the multi-tenant S3 functionality. The @NotBlank annotation ensures that tenant information will always be provided, which is critical for tenant isolation.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/streaming/StreamingOutAdapter.java (2)

4-4: Good update of import to use domain model.

The import now correctly references the new FileEvent domain model, which aligns with good Domain-Driven Design practices.


17-17: Successfully migrated to the new FileEvent domain model.

The code now correctly instantiates the new FileEvent record instead of the previous FileEventDTO, while maintaining the same parameter structure for backward compatibility.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/File.java (1)

5-5:

Details

✅ Verification successful

Tenant addition supports multi-tenancy requirement.

The addition of the tenant field to the File record is appropriate for implementing multi-tenant functionality. The @notblank annotation ensures that a valid tenant value is always provided.

Since the static fromPresignedUrl method has been removed (according to the AI summary), ensure there's a clear alternative way to create File objects from URLs that incorporates tenant information, possibly in a service or helper class.


🏁 Script executed:

#!/bin/bash
# Check if there is an alternative method to create File objects from presigned URLs
rg -A 10 "fromPresignedUrl|File\s+.*\s+.*presigned|presignedUrl.*File" --no-heading

Length of output: 16154


Multi-Tenancy Verified – Alternative URL Parsing in Place

The tenant field addition in the File record is correctly enforced with the @notblank annotation. As discussed, the removal of the static fromPresignedUrl method has been addressed: file creation from presigned URLs now happens in the S3Adapter (and via the FileSystemOutPort) where tenant information is extracted from the URL. The grep search confirms this alternative mechanism is actively used in multiple use cases (e.g., in the MarkFileFinishedUseCase and ErrorHandlerUseCase).

Please ensure that this alternative URL parsing logic is clearly documented so that future developers can readily understand how File objects are created with tenant data.

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/MarkFileFinishedUseCaseTest.java (3)

48-55: Tests properly updated for multi-tenant support.

The test has been correctly updated to use the new FileEvent model and include tenant information in the verifications. The change from verifyPresignedUrl to verifyAndResolvePresignedUrl suggests enhanced functionality to handle tenant-specific resolution.

Good job ensuring that the verification steps include explicit checks for the tenant parameter.


60-61: Exception test properly updated.

The test for PresignedUrlException has been correctly modified to use the new method name verifyAndResolvePresignedUrl and to pass the file event object.


66-67: UseCaseException test properly updated.

The test now creates a new FileEvent with an unknown use case, which is the right approach for testing the exception case with the new parameter structure.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/in/ErrorHandlerInPort.java (1)

4-4: Good refactoring to use FileEvent for error handling

The switch from individual parameters to a consolidated FileEvent object improves the interface design by encapsulating related data. This change aligns well with the multi-tenant implementation by containing tenant-specific context within a single object, reducing method complexity and enhancing maintainability.

Also applies to: 14-15, 17-17

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCaseTest.java (2)

8-8: Added tenant support and updated test data

Adding the TENANT constant import and updating the metadata path from "test.json" to "example.json" maintains test consistency with other changes in the implementation.

Also applies to: 57-57


84-86: Well-implemented tenant support across all file operations

The consistent addition of the TENANT parameter to all file system operations (getSubDirectories, getMatchingFilesWithTags, fileExists, getPresignedUrl, tagFile, copyFile) effectively implements multi-tenant functionality. The parameter ordering is logical, with tenant preceding bucket, which aligns with the hierarchical relationship between these concepts.

Also applies to: 101-103, 120-123, 129-130, 149-150, 158-158, 165-165, 183-184, 201-203

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCaseTest.java (2)

7-7: Added tenant support and improved test data consistency

The addition of the TENANT import and updating test file constructors to include the tenant parameter correctly implements multi-tenant functionality. The update of protocol entry file names from "test.pdf" to "example.pdf" improves consistency with other test data changes throughout the codebase.

Also applies to: 27-27, 67-72


76-80: Comprehensive tenant integration in protocol processing tests

The method signature change to include PresignedUrlException handling and the consistent addition of the TENANT parameter across all protocol-related operations ensures proper multi-tenant support throughout the protocol processing flow. All file operations (loadProtocol, getMatchingFilesWithTags, readFile, tagFile, moveFile) now correctly consider tenant context.

Also applies to: 84-88, 92-92, 95-97, 98-102, 104-105, 112-116, 124-134, 139-142, 150-151

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCaseTest.java (2)

3-5: Updated imports to support tenant functionality

The import changes properly support the implementation of tenant-aware error handling by including FILE1, TEST_FILE_EVENT, when for mocking, and PresignedUrlException for exception handling.

Also applies to: 12-12, 19-19


54-60: Enhanced error handling with tenant-aware file events

The test now properly uses the consolidated TEST_FILE_EVENT object instead of separate parameters and includes verification of presigned URLs. The addition of PresignedUrlException handling ensures the test correctly validates the error path for URL verification failures, which is critical for the multi-tenant implementation.

Also applies to: 63-63, 72-73

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/MarkFileFinishedUseCase.java (1)

25-32: Method properly updated for multi-tenant support

The method signature has been successfully updated to accept a FileEvent parameter instead of separate parameters. The implementation correctly extracts the useCase and file information from the event, and properly includes the tenant information with file.tenant() when finishing the file.

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/TestConstants.java (3)

17-18: Good addition of tenant constant

Adding the TENANT constant is a good practice for maintaining test consistency in the multi-tenant implementation.


22-24: Properly updated File constants for multi-tenant support

The FILE1 and FILE2 constants have been correctly updated to include tenant information.


28-31:

Details

❓ Verification inconclusive

Verify presigned URL consistency with tenant information

The presigned URL constant doesn't seem to include the tenant information, only the bucket name. While this might be intentional based on your URL structure, ensure it's consistent with how your S3 client uses tenant information in the actual implementation.


🏁 Script executed:

#!/bin/bash
# Check how presigned URLs are generated in the codebase

# Find implementations of URL generation
rg -n "presignedUrl|generatePresigned" --type java

Length of output: 6863


Attention: Verify Tenant Inclusion in Test Presigned URL

It appears that production code (e.g., in DispatcherUseCase.java where the URL is built using file.tenant(), file.bucket(), and file.path()) constructs the presigned URL with tenant details. However, the test constant in TestConstants.java (used to create FileEvent) only provides a file path and seems to omit any tenant information. Please verify whether the test presigned URL should mimic the complete structure—including the tenant identifier—or if tenant details are incorporated elsewhere in the test setup. Adjust the test constant or document the handling accordingly.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/in/streaming/StreamingInAdapter.java (2)

24-33: Successfully updated to use FileEvent

The finished() method has been properly updated to work with the new FileEvent model instead of FileEventDTO. The code is now cleaner, passing the entire event object rather than extracting individual fields.


36-42: Error handling updated for FileEvent

The dlq() method has been properly updated to use the FileEvent model and correctly passes it to the error handler.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/helper/FileHandlingHelper.java (2)

34-44: Successfully updated for multi-tenant support

The markFileError method has been correctly updated to include tenant information when tagging files.


46-64: Method signature and implementation updated for multi-tenant support

The finishFile method has been properly updated to include the tenant parameter in its signature and implementation. All necessary method calls now include tenant information, and the documentation has been updated accordingly.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCase.java (6)

47-47: Tenant retrieval logic looks consistent.
The explicit usage of useCase.getTenant() aligns with the new multi-tenant approach, ensuring the correct tenant is passed to file system operations. No immediate issues detected.


84-84: Confirm tenant handling in loadProtocol call.
Passing file.tenant() to loadProtocol is correct for multi-tenant support. Verify that the readProtocolOutPort handles missing or invalid tenants gracefully.


92-93: Use consistent filtering logic for finished paths.
Using file.tenant(), file.bucket(), finishedPath is sound. Confirm that recursive flags in these calls match your intended search scope.


108-108: Tenant-specific readFile operation looks correct.
No functional or syntax issues found here.


123-123: Tagging files with tenant context is appropriate.
Ensure the tag keys and values do not exceed S3 or system-defined size limits.


128-128: Multi-tenant move operation.
Moving files with file.tenant() is aligned with the new architecture. No immediate issues found.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCase.java (5)

10-10: New FileEvent import.
The introduction of FileEvent improves clarity by bundling relevant file and use-case data, removing the need for multiple parameters.


28-29: Handling error with new event-based signature.
Switching to FileEvent is correct for multi-tenant expansions. Log usage is clear and includes the cause details.


36-36: Error notification references new FileEvent.
The updated call to sendFileError uses useCase.getMailAddresses() and the file.path(). This is well-aligned with the multi-tenant approach and the new event model.


42-42: Metric increment for error tracking.
dispatchMeter.incrementError is effectively parameterized by useCase() and cause.source(). This is consistent with multi-tenant error tracking.


56-56: Tagging error on file may trigger NPE.
As noted above, if file is null, calling file.tenant() will fail. Ensure a null-check to avoid runtime errors.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Properties.java (4)

3-4: New imports for UseCase and MinioClient.
These additions are valid for the revised approach, storing tenant-based Minio clients.


7-13: Refactor to standard imports and Lombok annotations.
Adopting @NoArgsConstructor, AllArgsConstructor, Getter, Setter, and ToString fosters clarity. Make sure to handle any sensitive fields with caution in toString().


17-21: Consolidated Lombok usage.
These annotations are consistent with the rest of the codebase, removing the older @Data approach.


29-34: Tenant map structure is appropriate.
Storing ConnectionOptions per tenant in a nested property is a clean approach for multi-tenant S3 configurations.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCase.java (6)

59-59: Ensure the tenant matches the intended context.
The additional tenant parameter is passed correctly to getSubDirectories. This seems consistent with multi-tenant support.


86-86: Tenant parameter usage looks correct.
The parameter is passed here without issues.


193-205: Verify file.tenant() vs. useCase.getTenant() usage.
Here, the code uses file.tenant() calls. In other methods, useCase.getTenant() is used. Ensure that the file’s tenant always aligns with the use case tenant to prevent unintended mismatches or file-access errors.


223-223: Safely reading metadata file.
Passing file.tenant() to readFile is consistent with the multi-tenant approach. No immediate issues.


245-249: Check for consistency of tenant usage.
In the if-condition, useCase.getTenant() is checked, whereas finishFile calls use file.tenant(). Confirm that these two always match.


283-283: Copying file with tenant-specific logic looks good.
Using file.tenant() here is consistent with how the file was instantiated.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/FileSystemOutPort.java (9)

26-32: New tenant parameter in getMatchingFilesWithTags.
This addition aligns well with multi-tenant needs.


41-43: Tenant parameter in getSubDirectories.
Looks correct; no concerns.


52-52: Tenant parameter in tagFile.
No immediate issues with the added parameter.


61-61: Tenant parameter in fileExists.
Implementation is consistent with the interface changes.


70-70: Tenant parameter in readFile.
Matches the overall multi-tenant strategy.


79-79: Tenant parameter in getPresignedUrl.
No issues.


82-87: Renamed method and changed return type.
verifyAndResolvePresignedUrl returning a File object is a clean approach to unify verification and resolution steps.


96-96: Tenant parameter in moveFile.
Continues the tenant-aware pattern.


108-109: Tenant parameter in copyFile.
Method signature changes are consistent.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Adapter.java (16)

32-32: No issues with the new import.
The jakarta.validation.constraints.NotBlank import is appropriate.


52-52: Imported Strings utility.
This helps with blank-checking.


81-87: Added tenant parameter in getMatchingFilesWithTags.
Implementation looks cohesive for multi-tenant support.


89-93: Constructing File objects with tenant.
Ensures each file is tenant-scoped. Good consistency.


100-100: Retrieving file tags with tenant context.
Properly delegated to getTagsOfFile.


115-119: Tenant-based subdirectory retrieval.
Logic seems consistent and straightforward.


125-128: Tenant parameter in tagFile.
Loading and updating tags in a tenant-specific context looks correct.


142-142: Invoking setObjectTags on the tenant-specific client.
Implementation is correct.


151-157: Tenant parameter in fileExists.
Remains consistent with the approach.


176-182: Reading file with tenant context.
No issues identified.


191-199: Generating presigned URL for tenant.
Implementation looks solid.


229-244: Tenant-based moveFile.
Properly copies and removes the object using the tenant-specific client.


255-269: Tenant-based copyFile.
No issues spotted; the logic is straightforward.


287-296: Listing objects with tenant-based client.
Ensures correct scoping for multi-tenant data.


318-324: Tenant-specific object tags retrieval.
Implementation looks correct and exception handling is sensible.


372-401: Validate path parsing in fileFromPresignedUrl.
The substring logic to isolate bucket and path may fail if any unexpected slashes appear. Consider stricter validation or error handling for unusual URLs.

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/domain/model/FileTest.java (2)

3-4: Good refactoring to use constants instead of hardcoded values.

Importing constants from a centralized TestConstants class improves maintainability and consistency across tests. This aligns well with the multi-tenant architecture changes.


13-13: Proper tenant parameter added to File constructor.

The File constructor now accepts a tenant parameter as the first argument, correctly implementing the multi-tenant functionality required by this PR. This change ensures that file operations are now tenant-aware, which is a key objective of the PR.

simonhir added 3 commits April 7, 2025 10:08
# Conflicts:
#	dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCase.java
@simonhir simonhir changed the title feat/dispatch-multi-s3-tenant feat(dispatch): multi s3 tenant Apr 25, 2025
# Conflicts:
#	dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/in/streaming/StreamingInAdapter.java
#	dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Adapter.java
#	dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/File.java
#	dispatch-service/src/main/resources/application-local.yml
#	dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCaseTest.java

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

🔭 Outside diff range comments (2)
dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCaseTest.java (1)

68-77: Missing mock setup for presigned URL verification failure.

The test handleError_PresignedUrlException doesn't mock the fileSystemOutPort.verifyAndResolvePresignedUrl method to throw a PresignedUrlException. This test appears to be testing the error scenario but may not properly simulate the exception case.

Apply this diff to properly simulate the exception scenario:

 @Test
 void handleError_PresignedUrlException() {
     final UseCase useCase = swimDispatcherProperties.getUseCases().getFirst();
+    // setup
+    when(fileSystemOutPort.verifyAndResolvePresignedUrl(eq(useCase), eq(TEST_PRESIGNED_URL)))
+        .thenThrow(new PresignedUrlException("Invalid presigned URL"));
     // call
     errorHandlerUseCase.handleError(TEST_FILE_EVENT, TEST_ERROR_DETAILS);
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/FileSystemOutPort.java (1)

102-113: Consider supporting cross-tenant copy operations.

The copyFile method only accepts a single tenant parameter, which may limit cross-tenant file operations. If use cases can have different tenants (as suggested by the multi-tenant architecture), copying files between use cases would require separate source and destination tenant parameters.

Consider updating the signature to support cross-tenant operations:

-    void copyFile(@NotBlank String tenant, @NotBlank String srcBucket, @NotBlank String srcPath, @NotBlank String destBucket, @NotBlank String destPath,
-            boolean clearTags);
+    void copyFile(@NotBlank String srcTenant, @NotBlank String srcBucket, @NotBlank String srcPath, 
+            @NotBlank String destTenant, @NotBlank String destBucket, @NotBlank String destPath,
+            boolean clearTags);
♻️ Duplicate comments (1)
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCase.java (1)

32-34: Add null check for file resolution.

The verifyAndResolvePresignedUrl method could return null, but line 34 uses the file object without checking. This could cause a NullPointerException.

Add a null check as suggested in the previous review:

 final File file = this.fileSystemOutPort.verifyAndResolvePresignedUrl(useCase, event.presignedUrl());
+if (file == null) {
+    log.warn("File resolution returned null for presigned URL {}", event.presignedUrl());
+    return;
+}
 // tag file
 this.markFileError(file, cause);
🧹 Nitpick comments (1)
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/ReadProtocolOutPort.java (1)

17-17: Update Javadoc to document the tenant parameter.

The method signature correctly adds the tenant parameter with proper validation, but the Javadoc should be updated to document this new parameter.

     /**
      * Load parsed protocol.
      *
+     * @param tenant The tenant context for S3 operations.
      * @param bucket The bucket of the protocol file.
      * @param path The path of the protocol file.
      * @return The parsed protocol entries.
      */
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d150a0 and b35f12e.

📒 Files selected for processing (28)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/in/streaming/FileEventDTO.java (0 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/in/streaming/StreamingInAdapter.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Adapter.java (18 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Properties.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/streaming/FileEventDTO.java (0 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/streaming/StreamingOutAdapter.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/in/ErrorHandlerInPort.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/in/MarkFileFinishedInPort.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/FileSystemOutPort.java (9 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/ReadProtocolOutPort.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCase.java (6 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCase.java (3 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/MarkFileFinishedUseCase.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCase.java (4 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/helper/FileHandlingHelper.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/configuration/SwimDispatcherProperties.java (2 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/File.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/FileEvent.java (1 hunks)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/UseCase.java (1 hunks)
  • dispatch-service/src/main/resources/application-local.yml (2 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/TestConstants.java (2 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3AdapterTest.java (1 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCaseTest.java (8 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCaseTest.java (3 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/MarkFileFinishedUseCaseTest.java (3 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCaseTest.java (5 hunks)
  • dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/domain/model/FileTest.java (2 hunks)
  • dispatch-service/src/test/resources/application-test.yml (3 hunks)
💤 Files with no reviewable changes (2)
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/streaming/FileEventDTO.java
  • dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/in/streaming/FileEventDTO.java
🧰 Additional context used
🧬 Code Graph Analysis (2)
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/FileSystemOutPort.java (1)
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/exception/PresignedUrlException.java (1)
  • SuppressWarnings (3-12)
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Adapter.java (1)
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/exception/PresignedUrlException.java (1)
  • SuppressWarnings (3-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Analyze Java source files (./dispatch-service)
  • GitHub Check: Analyze Java source files (./invoice-service)
  • GitHub Check: Analyze Java source files (./matching-service)
  • GitHub Check: Analyze Java source files (./dms-service)
  • GitHub Check: Analyze Java source files (./dipa-service)
  • GitHub Check: Analyze Java source files (./handler-core)
🔇 Additional comments (61)
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/UseCase.java (1)

18-22: LGTM! Tenant field addition is well-implemented.

The new tenant field follows established patterns in the class with appropriate validation (@NotBlank) and clear documentation. This change effectively enables multi-tenant support as intended.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/streaming/StreamingOutAdapter.java (2)

4-4: LGTM! Clean transition from DTO to domain record.

The import update aligns with the broader refactoring to use domain records instead of DTOs.


17-17: LGTM! FileEvent instantiation is correct.

The new FileEvent constructor properly maps the existing method parameters, maintaining backward compatibility while using the new domain model.

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/domain/model/FileTest.java (2)

3-4: LGTM! Improved test constants usage.

Using imported constants instead of hardcoded values improves test maintainability and consistency.


13-13: LGTM! File constructor updated correctly.

The File instantiation properly includes the tenant parameter, aligning with the new multi-tenant domain model.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/FileEvent.java (1)

1-9: LGTM! Well-designed domain record.

The FileEvent record is cleanly implemented with:

  • Proper validation annotations for required fields
  • Immutable design using Java records
  • Clear field naming
  • Appropriate optional field handling for metadataPresignedUrl

This design effectively replaces the previous DTO while maintaining type safety.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCase.java (5)

49-49: LGTM! Proper tenant context addition.

The tenant parameter is correctly extracted from the useCase for file system operations, maintaining consistency with the multi-tenant architecture.


86-86: LGTM! Tenant-aware protocol loading.

The loadProtocol method correctly uses the tenant from the file context for tenant-specific protocol operations.


90-91: LGTM! Consistent tenant usage in file operations.

Both getMatchingFilesWithTags calls properly use the tenant from the file context, ensuring tenant-aware file discovery in both dispatch and finished folders.

Also applies to: 94-95


110-110: LGTM! Tenant parameter in file reading.

The readFile operation correctly includes the tenant parameter for tenant-specific file access.


125-127: LGTM! Complete tenant integration.

Both tagFile and moveFile operations properly include the tenant parameter, ensuring all file system operations are tenant-aware throughout the protocol processing workflow.

Also applies to: 130-130

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/configuration/SwimDispatcherProperties.java (1)

82-82: LGTM: Proper validation enhancement for multi-tenant support.

The addition of @Valid annotation ensures recursive validation of UseCase instances, which is essential now that they contain tenant fields requiring validation. This change properly enforces tenant constraints at configuration load time.

dispatch-service/src/test/resources/application-test.yml (2)

2-5: LGTM: Clean multi-tenant configuration structure.

The new s3.tenants configuration properly defines tenant-specific S3 endpoints, providing a clean foundation for multi-tenant support.


11-11: LGTM: Consistent tenant assignment across use cases.

All use cases consistently reference test-tenant, ensuring proper test isolation and alignment with the new tenant-aware domain model.

Also applies to: 22-22, 32-32

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/domain/model/File.java (1)

5-5: LGTM: Clean tenant-aware domain model.

The addition of tenant as the first parameter with proper validation enhances the File record to be tenant-aware while maintaining its immutable structure. This establishes tenant as a fundamental part of file identity, which aligns well with the multi-tenant architecture.

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/MarkFileFinishedUseCaseTest.java (2)

48-56: LGTM: Comprehensive test adaptation for tenant-aware API.

The test properly adapts to the new FileEvent-based API and consistently includes tenant context in all file system operations. The mock setup and verifications align well with the refactored method signatures.


59-68: LGTM: Exception handling tests properly updated.

Both exception test scenarios are correctly updated to use the new FileEvent parameter and updated verification methods, ensuring comprehensive error case coverage.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/in/MarkFileFinishedInPort.java (2)

5-7: LGTM! Good consolidation with proper validation.

The addition of proper validation imports and the consolidation of parameters into a single FileEvent object improves the interface design and ensures validation consistency.


17-17: LGTM! Improved method signature with validation.

The method signature change from multiple string parameters to a single validated FileEvent parameter is a good architectural improvement that provides better encapsulation and validation.

dispatch-service/src/main/resources/application-local.yml (1)

46-51: LGTM! Well-structured multi-tenant configuration.

The restructuring of S3 properties under tenants.local provides a clean foundation for multi-tenant support.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/helper/FileHandlingHelper.java (3)

40-40: LGTM! Proper tenant context propagation.

The addition of file.tenant() as the first parameter correctly implements tenant-aware file tagging operations.


49-49: LGTM! Consistent tenant parameter addition.

The method signature change to include the tenant parameter and its usage in subsequent operations properly implements multi-tenant support.

Also applies to: 54-54


56-56: LGTM! Tenant-aware file operations.

Both tagFile and moveFile operations correctly use the tenant parameter, ensuring proper multi-tenant file handling.

Also applies to: 60-60

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/in/ErrorHandlerInPort.java (2)

4-4: LGTM! Consistent with architectural refactoring.

The import addition supports the consolidation of parameters into the FileEvent object.


17-17: LGTM! Improved error handling interface.

The method signature change from multiple parameters to a single validated FileEvent parameter improves consistency with the event-driven architecture and provides better encapsulation.

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCaseTest.java (3)

8-8: LGTM! Proper test constant import.

The import of TENANT constant enables proper tenant-aware testing.


57-57: LGTM! Consistent test data naming.

The path update from "test.json" to "example.json" maintains consistency with other test data naming conventions.


84-86: LGTM! Comprehensive tenant-aware test updates.

All file system operation calls have been properly updated to include the tenant parameter as the first argument, ensuring tests validate the multi-tenant functionality correctly.

Also applies to: 101-103, 120-122, 139-143, 154-154, 161-161, 179-179, 197-199

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3AdapterTest.java (1)

33-40: LGTM: Well-structured success test.

The test correctly validates the tenant-aware file parsing from presigned URLs, asserting the expected tenant, bucket, and path values.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCase.java (2)

28-29: LGTM: Clean refactoring to use FileEvent.

The method signature has been properly updated to use the new FileEvent object, and the logging correctly extracts the use case and presigned URL from the event.


56-59: LGTM: Tenant-aware file tagging.

The tagFile method call correctly includes the tenant parameter as the first argument, aligning with the multi-tenant architecture.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/MarkFileFinishedUseCase.java (3)

30-32: Add null check for file resolution.

Similar to the ErrorHandlerUseCase, the verifyAndResolvePresignedUrl method could return null, but the code proceeds to use the file object without checking.

 final File file = this.fileSystemOutPort.verifyAndResolvePresignedUrl(useCase, event.presignedUrl());
+if (file == null) {
+    log.warn("File resolution returned null for presigned URL {}", event.presignedUrl());
+    throw new PresignedUrlException("Could not resolve file from presigned URL: " + event.presignedUrl());
+}

34-39: Add null check for metadata file resolution.

The metadata file resolution also lacks a null check. Additionally, consider the logic flow - if the main file resolution fails, should metadata processing still proceed?

 if (Strings.isNotBlank(event.metadataPresignedUrl())) {
     // verify presigned url and extract metadata File
     final File metadataFile = this.fileSystemOutPort.verifyAndResolvePresignedUrl(useCase, event.metadataPresignedUrl());
+    if (metadataFile == null) {
+        log.warn("Metadata file resolution returned null for presigned URL {}", event.metadataPresignedUrl());
+        throw new PresignedUrlException("Could not resolve metadata file from presigned URL: " + event.metadataPresignedUrl());
+    }
     // finish metadata file
     fileHandlingHelper.finishFile(useCase, metadataFile.tenant(), metadataFile.bucket(), metadataFile.path());
 }

26-28: LGTM: Clean method signature update.

The method has been properly refactored to accept a FileEvent parameter, maintaining consistency with the new event-driven architecture.

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/TestConstants.java (3)

17-17: LGTM: Added tenant constant for testing.

The new TENANT constant provides consistent tenant identification across test scenarios.


22-23: LGTM: Updated File constructors with tenant parameter.

The File constructors have been correctly updated to include the tenant as the first parameter, maintaining consistency with the new multi-tenant File record structure.


29-30: LGTM: Added FileEvent test constant.

The new TEST_FILE_EVENT constant provides a standardized FileEvent instance for testing, properly initialized with use case, presigned URL, and null metadata URL.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Properties.java (4)

28-28: LGTM: Thread-safe client caching.

The use of ConcurrentHashMap for the CLIENTS map addresses the concurrency concern raised in previous reviews, ensuring thread-safe access to cached MinioClient instances.


67-77: LGTM: Proper tenant validation and client creation.

The getClient method correctly validates tenant existence and creates clients on-demand. The logic condition has been fixed from the previous review to properly throw an exception when the tenant doesn't exist.


49-57: LGTM: Secure ConnectionOptions implementation.

The ConnectionOptions class properly excludes sensitive fields (secretKey, accessKey) from the toString method, preventing accidental logging of credentials.


32-34: LGTM: Well-documented tenant configuration.

The tenants field is properly annotated and documented, clearly indicating its relationship with the UseCase#getTenant() method.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/in/streaming/StreamingInAdapter.java (2)

23-33: LGTM! Clean refactoring to use FileEvent.

The simplified approach of passing the entire FileEvent object improves code maintainability and aligns well with the multi-tenant architecture changes.


35-42: LGTM! Consistent error handling with FileEvent.

The DLQ handler correctly uses the new FileEvent structure for error processing.

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/ErrorHandlerUseCaseTest.java (1)

54-66: Test properly adapted for FileEvent and tenant-aware operations.

The test correctly mocks the new verifyAndResolvePresignedUrl method and verifies tenant-aware file tagging.

dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/application/usecase/ProtocolProcessingUseCaseTest.java (1)

67-92: Excellent tenant-aware test setup.

The test correctly incorporates tenant parameters in all file system operations and properly handles the new exception type.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/usecase/DispatcherUseCase.java (2)

46-71: Proper tenant context propagation in main dispatch loop.

The implementation correctly uses useCase.getTenant() for all file system operations within each use case iteration.


276-283: Verify tenant handling in cross–use–case file copy

Ensure the correct tenant is used when copying files between use cases. The current code in DispatcherUseCase.java (around lines 276–283) always passes the source file’s tenant for both source and destination:

fileSystemOutPort.copyFile(
    file.tenant(),
    file.bucket(),
    file.path(),
    targetUseCase.getBucket(),
    destPath,
    true
);

If target use cases can belong to different tenants, this may cause data to land in the wrong tenant’s namespace. Please:

  • Confirm whether your UseCase model exposes a getTenant() (or equivalent) method on targetUseCase.
  • Verify that the FileSystemOutPort.copyFile(…) signature supports a separate destination‐tenant parameter.
  • If both are true, update the call to:
- fileSystemOutPort.copyFile(
-     file.tenant(),
-     file.bucket(),
-     file.path(),
-     targetUseCase.getBucket(),
-     destPath,
-     true
- );
+ fileSystemOutPort.copyFile(
+     file.tenant(),                    // source tenant
+     file.bucket(),
+     file.path(),
+     targetUseCase.getTenant(),        // destination tenant
+     targetUseCase.getBucket(),
+     destPath,
+     true
+ );
  • Otherwise, document that cross–use–case copies require all use cases to share the same tenant.
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/application/port/out/FileSystemOutPort.java (2)

26-34: Consistent tenant-aware file matching.

The method signature properly includes tenant context for scoped file operations.


84-90: Good refactoring of presigned URL verification.

The renamed method with File return type better encapsulates the verification and resolution logic, providing a complete file object with tenant context.

dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Adapter.java (12)

82-103: LGTM! Tenant parameter correctly propagated.

The addition of the tenant parameter and its propagation through getObjectsInPath, File constructor, and getTagsOfFile calls properly implements multi-tenant support for this method.


118-122: LGTM! Consistent tenant parameter addition.

The tenant parameter is correctly added and passed to getObjectsInPath.


128-145: LGTM! Tenant-aware tagging implementation.

The tenant parameter is correctly used to retrieve the tenant-specific S3 client and passed to getTagsOfFile, ensuring proper tenant isolation for tagging operations.


154-160: LGTM! Tenant-specific file existence check.

The tenant parameter is correctly used to retrieve the appropriate S3 client for checking file existence.


179-185: LGTM! Tenant-aware file reading.

The tenant parameter is correctly used to retrieve the tenant-specific S3 client for reading files.


194-202: LGTM! Tenant-specific presigned URL generation.

The tenant parameter is correctly used to generate presigned URLs from the appropriate tenant's S3 client.


212-228: LGTM! Enhanced presigned URL verification with file resolution.

The refactored method correctly:

  1. Verifies the presigned URL is accessible
  2. Resolves and returns the File object based on the UseCase
  3. Throws clear exceptions for failure cases

This aligns with the requirement to resolve tenant via UseCase rather than presigned URL.


233-248: LGTM! Tenant-aware file moving.

The tenant parameter is correctly used for both the copy and remove operations, ensuring proper tenant isolation when moving files.


259-276: LGTM! Tenant-specific file copying.

The tenant parameter is correctly used to retrieve the appropriate S3 client for copying files.


294-303: LGTM! Tenant-aware object listing.

The protected helper method correctly uses the tenant parameter to retrieve objects from the appropriate tenant's S3 storage.


325-331: LGTM! Tenant-specific tag retrieval.

The protected helper method correctly uses the tenant parameter to retrieve tags from the appropriate tenant's S3 storage.


446-454: LGTM! Tenant-aware protocol loading.

The tenant parameter is correctly passed to readFile for loading protocol data from the appropriate tenant's storage.

Comment on lines +385 to +414
/**
* Resolves a File from a presigned URL.
* Needs to be formated as: {@code <tenant url>/<bucket>/<path>}
*
* @param presignedUrl The presigned URL to resolve.
* @return The resolved File.
* @throws PresignedUrlException If the presigned URL can't be parsed.
*/
protected File fileFromPresignedUrl(@NotNull final UseCase useCase, @NotBlank final String presignedUrl) throws PresignedUrlException {
// check input has content
if (Strings.isBlank(presignedUrl)) {
throw new PresignedUrlException("Empty presigned url can't be parsed");
}
// parse presigned url
final URI uri;
try {
uri = new URI(presignedUrl);
} catch (final URISyntaxException e) {
throw new PresignedUrlException("Presigned url could not be parsed", e);
}
// create File object from presigned url
final String tenantUrl = String.format("%s://%s%s", uri.getScheme(), uri.getHost(), uri.getPort() == -1 ? "" : ":" + uri.getPort());
final String uriPath = uri.getPath().replaceFirst("^/", "");
final int slashIndex = uriPath.indexOf('/');
final String bucket = uriPath.substring(0, slashIndex);
final String filePath = uriPath.substring(slashIndex + 1);
// compare with UseCase
this.verifyPresignedUrlForUseCase(useCase, tenantUrl, bucket, filePath);
return new File(useCase.getTenant(), useCase.getBucket(), filePath, null);
}

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.

⚠️ Potential issue

Potential StringIndexOutOfBoundsException in URL parsing.

The method correctly implements resolving tenant from UseCase rather than the presigned URL. However, there's a potential issue at line 409:

final int slashIndex = uriPath.indexOf('/');
final String bucket = uriPath.substring(0, slashIndex);

If slashIndex is -1 (no slash found), substring(0, -1) will throw a StringIndexOutOfBoundsException.

Add validation to handle URLs without a file path:

 final String uriPath = uri.getPath().replaceFirst("^/", "");
 final int slashIndex = uriPath.indexOf('/');
+if (slashIndex == -1) {
+    throw new PresignedUrlException("Invalid presigned URL format: missing file path");
+}
 final String bucket = uriPath.substring(0, slashIndex);
 final String filePath = uriPath.substring(slashIndex + 1);
🤖 Prompt for AI Agents
In
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Adapter.java
around lines 385 to 414, the code extracts the bucket and file path from the URI
path by finding the first slash. If no slash is found, substring(0, -1) causes a
StringIndexOutOfBoundsException. To fix this, add a check after getting
slashIndex to verify it is not -1. If it is -1, throw a PresignedUrlException
indicating the presigned URL format is invalid or missing the file path. This
validation prevents the exception and ensures only correctly formatted URLs are
processed.

Comment on lines +416 to +443
/**
* Verify the given presigned URL is valid for a UseCase.
*
* @param useCase The UseCase the presigned URL should be for.
* @param tenantUrl The resolved tenant URL from the presigned URL.
* @param bucket The resolved bucket from the presigned URL.
* @param filePath The resolved filePath URL from the presigned URL.
* @throws PresignedUrlException If the presigned URL is not valid for the UseCase.
*/
protected void verifyPresignedUrlForUseCase(final UseCase useCase, final String tenantUrl, final String bucket, final String filePath)
throws PresignedUrlException {
// tenant
final S3Properties.ConnectionOptions tenantOptions = this.s3Properties.getTenants().get(useCase.getTenant());
if (!tenantOptions.getUrl().startsWith(tenantUrl) && !tenantUrl.startsWith(tenantOptions.getUrl())) {
throw new PresignedUrlException(String.format("Presigned URL: UseCase %s tenant URL %s doesn't match presigned URL %s",
useCase.getName(), tenantOptions.getUrl(), tenantUrl));
}
// bucket
if (!useCase.getBucket().equals(bucket)) {
throw new PresignedUrlException(String.format("Presigned URL: Bucket %s from UseCase %s doesn't match bucket from presigned URL %s",
useCase.getBucket(), useCase.getName(), bucket));
}
// path
if (!filePath.startsWith(useCase.getPath())) {
throw new PresignedUrlException(
String.format("Presigned URL: Filepath %s isn't in UseCase %s path %s", filePath, useCase.getName(), useCase.getPath()));
}
}

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.

⚠️ Potential issue

Improve URL validation to prevent potential security issues.

The bidirectional startsWith check on line 429 could be too permissive and might allow unintended URL matches:

if (!tenantOptions.getUrl().startsWith(tenantUrl) && !tenantUrl.startsWith(tenantOptions.getUrl()))

This could incorrectly validate URLs like http://example.com.evil.com against http://example.com.

Consider normalizing both URLs and using exact host matching:

-if (!tenantOptions.getUrl().startsWith(tenantUrl) && !tenantUrl.startsWith(tenantOptions.getUrl())) {
+// Normalize URLs for comparison (remove trailing slashes, compare hosts)
+URI expectedUri = new URI(tenantOptions.getUrl());
+URI actualUri = new URI(tenantUrl);
+if (!expectedUri.getHost().equals(actualUri.getHost()) || 
+    expectedUri.getPort() != actualUri.getPort() ||
+    !expectedUri.getScheme().equals(actualUri.getScheme())) {
     throw new PresignedUrlException(String.format("Presigned URL: UseCase %s tenant URL %s doesn't match presigned URL %s",
             useCase.getName(), tenantOptions.getUrl(), tenantUrl));
 }
🤖 Prompt for AI Agents
In
dispatch-service/src/main/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3Adapter.java
between lines 416 and 443, the tenant URL validation uses a bidirectional
startsWith check that can incorrectly validate malicious URLs. To fix this,
parse both tenantOptions.getUrl() and tenantUrl into URL objects, then compare
their hosts exactly instead of using startsWith. This ensures only exact host
matches are accepted, preventing security issues from permissive URL matching.

fallback-mail: swim@example.com
use-cases:
- name: test-meta
tenant: local

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.

💡 Verification agent

🧩 Analysis chain

Verify tenant assignment consistency.

Some use cases have tenant: local specified while others (like test-inbox-incoming and test-dipa) don't. Ensure this is intentional and that use cases without tenant assignment are handled correctly.

Also applies to: 78-78, 86-86


🏁 Script executed:

#!/bin/bash
# Description: Check if use cases without tenant assignment are handled properly in the codebase
# Expected: Find how use cases without tenant are processed

# Search for how use cases without tenant field are handled
ast-grep --pattern 'class $_ {
  $$$
  tenant($$$) {
    $$$
  }
  $$$
}'

# Look for default tenant handling logic
rg -A 5 "tenant.*null|default.*tenant" --type java

# Check UseCase class for tenant handling
rg -A 10 "class UseCase" --type java

Length of output: 13227


Ensure Tenant Is Defined for Every Use Case
I couldn’t find any default‐tenant fallback in the code (the UseCase model’s tenant field is @NotBlank and there’s no “if null → use default” logic), so any use case entry without a tenant: will fail validation or behave unexpectedly at startup. Please add tenant: local (or another appropriate value) to all use cases in application-local.yml, for example:

• Line 57 – test-inbox-incoming
• Line 78 – test-dipa
• Line 86 – [next use case]

Alternatively, implement a default-tenant mechanism in the code if leaving these blank was intentional.

🤖 Prompt for AI Agents
In dispatch-service/src/main/resources/application-local.yml at line 57 and
similarly at lines 78 and 86, the use case entries lack a tenant field, which is
mandatory and currently has no default fallback in the code. To fix this, add
tenant: local (or another appropriate tenant value) to each use case entry to
ensure they pass validation and behave correctly at startup.

Comment on lines +42 to +46
@Test
void fileFromPresignedUrl_Exception() {
final UseCase useCase = swimDispatcherProperties.getUseCases().get(1);
assertThrows(PresignedUrlException.class, () -> s3Adapter.fileFromPresignedUrl(useCase, TEST_PRESIGNED_URL));
}

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.

💡 Verification agent

🧩 Analysis chain

Verify test configuration setup for different use cases.

The test uses getUseCases().get(1) to trigger an exception, implying the second use case should have different tenant configuration that causes fileFromPresignedUrl to fail. Ensure your test configuration properly sets up this scenario.


🏁 Script executed:

#!/bin/bash
# Description: Verify test configuration has multiple use cases with different tenant setups

# Search for test configuration files that define use cases
fd "application.*test.*\.ya?ml$" --exec cat {} \; | grep -A 20 -B 5 "use.*cases\|useCases"

Length of output: 2212


🏁 Script executed:

#!/bin/bash
# Locate application-*.yml/.yaml in test resources for dispatch-service
echo "Test property files in dispatch-service/src/test/resources:"
find dispatch-service/src/test/resources -maxdepth 1 -type f \( -name "application*.yml" -o -name "application*.yaml" \)

# Print contents of each found file for inspection
for file in $(find dispatch-service/src/test/resources -maxdepth 1 -type f \( -name "application*.yml" -o -name "application*.yaml" \)); do
  echo -e "\n==== $file ===="
  sed -n '1,200p' "$file"
done

Length of output: 1679


Ensure a distinct tenant is mis-configured to trigger the exception
The test at S3AdapterTest#fileFromPresignedUrl_Exception uses

swimDispatcherProperties.getUseCases().get(1)

but both use-cases in application-test.yml reference the same, correctly configured tenant (test-tenant). As a result, calling fileFromPresignedUrl(...) will not throw.

To fix:

  • In dispatch-service/src/test/resources/application-test.yml, under swim.s3.tenants, add a second tenant entry (e.g. invalid-tenant) with no or an invalid url.
  • Update the second use-case to reference this new tenant:
    swim:
      s3:
        tenants:
          test-tenant:
            url: https://s3.muenchen.de
          invalid-tenant:    # ← missing or malformed URL
      #
      use-cases:
        - name: test-meta
          tenant: test-tenant
          
        - name: test-meta-dest
          tenant: invalid-tenant  # ← now points to invalid config
          
  • This ensures that fileFromPresignedUrl() is invoked with a tenant lacking a valid endpoint and will throw the expected PresignedUrlException.
🤖 Prompt for AI Agents
In
dispatch-service/src/test/java/de/muenchen/oss/swim/dispatcher/adapter/out/s3/S3AdapterTest.java
around lines 42 to 46, the test uses a use-case referencing a correctly
configured tenant, so the expected PresignedUrlException is not thrown. To fix
this, modify dispatch-service/src/test/resources/application-test.yml by adding
a second tenant under swim.s3.tenants with an invalid or missing URL (e.g.,
invalid-tenant), then update the second use-case to reference this invalid
tenant. This ensures that calling fileFromPresignedUrl() with this use-case
triggers the exception as intended.

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.

Enable dispatch-service to use several tenants in parallel

1 participant