Skip to content

Fix/s3 fallback#60

Merged
MartinStenhagen merged 6 commits into
mainfrom
fix/s3-fallback
Apr 28, 2026
Merged

Fix/s3 fallback#60
MartinStenhagen merged 6 commits into
mainfrom
fix/s3-fallback

Conversation

@Rickank

@Rickank Rickank commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes
    • File upload, download, and delete operations now properly handle and report storage configuration errors, returning appropriate error messages instead of false success responses.

@coderabbitai

coderabbitai Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@MartinStenhagen has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 53 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1d33c6ff-5bdb-40d0-96ff-bea6f6b23f0e

📥 Commits

Reviewing files that changed from the base of the PR and between 77441bc and 81b5819.

📒 Files selected for processing (1)
  • src/main/java/backendlab/team4you/s3/S3Controller.java
📝 Walkthrough

Walkthrough

S3Service methods now catch S3Exception and rethrow as FileStorageConfigurationException. S3Controller endpoints catch this exception, returning HTTP 500 with Swedish error messages instead of propagating failures. Tests validate exception behavior across uploadFile, downloadFile, and deleteFile operations.

Changes

Cohort / File(s) Summary
Error handling in S3 service layer
src/main/java/backendlab/team4you/s3/S3Service.java
Added try-catch blocks in uploadFile, downloadFile, and deleteFile methods to catch S3Exception and rethrow as FileStorageConfigurationException with localized error messages containing the object key.
Error handling in S3 controller
src/main/java/backendlab/team4you/s3/S3Controller.java
Added try-catch exception handling in all three endpoints (uploadFile, downloadFile, deleteFile) to catch FileStorageConfigurationException and return HTTP 500 responses with Swedish error messages. Modified downloadFile return type from ResponseEntity<byte[]> to ResponseEntity<?> to support error responses.
Exception scenario tests
src/test/java/backendlab/team4you/s3/S3ServiceTest.java
Added new test cases for negative paths: uploadFile, downloadFile, and deleteFile now include tests that mock S3Exception from the S3Client and verify that FileStorageConfigurationException is raised by the service methods.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hop hop, the S3 must flow with care,
When files dance through clouds so rare,
We catch the faults with gentle grace,
Five-hundred errors find their place,
Now robustness hops with style and flair! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Fix/s3 fallback' is vague and does not clearly convey what the actual changes accomplish. Use a more descriptive title that explains the specific fix, such as 'Add exception handling for S3 file operations' or 'Handle FileStorageConfigurationException in S3 endpoints'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/s3-fallback

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@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: 2

🧹 Nitpick comments (5)
src/main/java/backendlab/team4you/s3/S3Controller.java (2)

35-67: Prefer a centralized @RestControllerAdvice over per-endpoint try/catch.

The same catch (FileStorageConfigurationException e) → 500 + Swedish message pattern is duplicated three times here, and any other controller that ends up calling S3Service (e.g., CaseFileController) will need the same boilerplate. Centralizing in a @RestControllerAdvice eliminates the duplication, ensures consistent behavior across all controllers, and gives a single place for logging.

`@RestControllerAdvice`
public class FileStorageExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(FileStorageExceptionHandler.class);

    `@ExceptionHandler`(FileStorageConfigurationException.class)
    public ResponseEntity<String> handle(FileStorageConfigurationException e) {
        log.error("File storage failure", e);
        return ResponseEntity.internalServerError().body(e.getMessage());
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/backendlab/team4you/s3/S3Controller.java` around lines 35 - 67,
Replace the repeated try/catch blocks in S3Controller methods uploadFile,
downloadFile, and deleteFile by removing catches for
FileStorageConfigurationException and instead create a centralized
`@RestControllerAdvice` named FileStorageExceptionHandler with an
`@ExceptionHandler`(FileStorageConfigurationException.class) method (e.g., handle)
that logs the exception and returns
ResponseEntity.internalServerError().body(e.getMessage()) so all controllers
(including CaseFileController) get consistent 500 handling and logging for
FileStorageConfigurationException.

46-46: Loosened return type — consider ResponseEntity<Object> or a sealed response shape.

Changing ResponseEntity<byte[]> to ResponseEntity<?> works but loses information for API documentation tools (Swagger/OpenAPI) and makes the contract harder to reason about. ResponseEntity<Object> is more idiomatic for mixed body types, or alternatively keep ResponseEntity<byte[]> and have the error path return an empty body / use @ResponseStatus with a global handler (see the @RestControllerAdvice suggestion).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/backendlab/team4you/s3/S3Controller.java` at line 46, The
method signature for downloadFile currently uses ResponseEntity<?> which hides
the response contract; update the signature of downloadFile to a more explicit
type such as ResponseEntity<Object> (or revert to ResponseEntity<byte[]> if the
success path always returns bytes) and adjust error handling accordingly—e.g.,
keep ResponseEntity<byte[]> and return an empty body / use `@ResponseStatus` with
your global `@RestControllerAdvice`, or change to ResponseEntity<Object> and
ensure both the success (byte[] payload) and error (error DTO or message) paths
are properly typed so OpenAPI/Swagger and callers see a clear contract.
src/test/java/backendlab/team4you/s3/S3ServiceTest.java (1)

74-105: Optional: import the exception type and assert on the cause.

The fully-qualified class name in assertThrows calls is verbose. Adding an import would be cleaner. Additionally, asserting that the original S3Exception is preserved as getCause() would protect against accidental loss of the underlying SDK failure during refactors.

♻️ Suggested cleanup
+import backendlab.team4you.exceptions.FileStorageConfigurationException;
@@
-        assertThrows(backendlab.team4you.exceptions.FileStorageConfigurationException.class,
-                () -> s3Service.uploadFile("test.txt", "Hello".getBytes(), "text/plain"));
+        FileStorageConfigurationException ex = assertThrows(FileStorageConfigurationException.class,
+                () -> s3Service.uploadFile("test.txt", "Hello".getBytes(), "text/plain"));
+        assertInstanceOf(S3Exception.class, ex.getCause());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/backendlab/team4you/s3/S3ServiceTest.java` around lines 74 -
105, Replace the fully-qualified exception class in the three tests in
S3ServiceTest
(uploadFile_shouldThrowFileStorageConfigurationException_whenS3Fails,
downloadFile_shouldThrowFileStorageConfigurationException_whenS3Fails,
deleteFile_shouldThrowFileStorageConfigurationException_whenS3Fails) with an
import of backendlab.team4you.exceptions.FileStorageConfigurationException and,
after asserting the exception is thrown, assert that ex.getCause() is an
instance of S3Exception (or that the cause message matches "S3 error") to ensure
the original S3Exception is preserved as the cause.
src/main/java/backendlab/team4you/s3/S3Service.java (2)

34-74: Exception choice misrepresents the failure mode.

FileStorageConfigurationException semantically implies a misconfiguration, but S3Exception covers many transient/runtime failures (network, throttling, auth expiry, server-side errors, etc.). Callers reading the type will be misled, and operationally a 5xx caused by throttling will be classified as a configuration problem.

Consider introducing a more general FileStorageException (or similar) for runtime failures, and reserving FileStorageConfigurationException for genuine configuration issues. At minimum, the message should preserve more detail from the underlying S3Exception (status code / AWS error code) rather than only the key.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/backendlab/team4you/s3/S3Service.java` around lines 34 - 74,
The catch blocks in upload/download/delete (methods: the putObject block,
downloadFile, deleteFile) wrongly wrap all S3Exception cases as
FileStorageConfigurationException; introduce a new runtime exception (e.g.,
FileStorageException) for transient/runtime S3 errors and reserve
FileStorageConfigurationException only for genuine config validation failures,
then update the catch clauses to throw FileStorageException instead,
constructing the message to include S3Exception.getMessage(), status code and
AWS error code (or e.awsErrorDetails()) and pass the original exception as the
cause so callers can inspect details.

76-93: Inconsistent error translation in uploadFileIfAbsent.

uploadFile, downloadFile, and deleteFile now translate S3ExceptionFileStorageConfigurationException, but uploadFileIfAbsent still rethrows the raw S3Exception for any non-409/412 status. Callers of this method will see a different exception type from the same SDK failure, which makes uniform handling at the controller / service boundary harder.

♻️ Suggested alignment
         } catch (S3Exception exception) {
             if (exception.statusCode() == 409 || exception.statusCode() == 412) {
                 throw new FileKeyConflictException(key, exception);
             }
-            throw exception;
+            throw new FileStorageConfigurationException("Kunde inte ladda upp filen: " + key, exception);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/backendlab/team4you/s3/S3Service.java` around lines 76 - 93,
uploadFileIfAbsent currently catches S3Exception only to map 409/412 into
FileKeyConflictException and rethrows all other S3Exception instances directly,
causing inconsistent exception types compared to uploadFile, downloadFile, and
deleteFile; change uploadFileIfAbsent to catch S3Exception from
s3Client.putObject and, after mapping 409/412 to FileKeyConflictException (as it
already does), wrap all other S3Exception instances in
FileStorageConfigurationException (the same translation used by
uploadFile/downloadFile/deleteFile) so callers always receive consistent
exceptions for SDK failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/backendlab/team4you/s3/S3Controller.java`:
- Line 39: The error response in S3Controller currently returns a Swedish string
("Kunde inte ladda upp filen: " + key) while success messages are English;
change the endpoint to return a consistent structured JSON error object (e.g.,
Map or a small ErrorResponse DTO with fields "error" and "key") and use English
messages (e.g., { "error": "Could not upload file", "key": key }) so clients can
localize; update the ResponseEntity.internalServerError().body(...) call to
return the ErrorResponse (or Map.of("error", "...", "key", key)) and ensure any
other error branches in S3Controller use the same structure.
- Around line 35-67: The three catch blocks in S3Controller (in methods
uploadFile, downloadFile, deleteFile) currently swallow
FileStorageConfigurationException; update each catch to log the exception before
returning the 500 response (use the controller's logger and call logger.error
with a clear message and the exception object so stacktrace and cause are
preserved), keeping the existing Swedish response body; ensure the logger used
is the class logger (e.g., LoggerFactory.getLogger(S3Controller.class)) and
include contextual info like the key and operation in the log message.

---

Nitpick comments:
In `@src/main/java/backendlab/team4you/s3/S3Controller.java`:
- Around line 35-67: Replace the repeated try/catch blocks in S3Controller
methods uploadFile, downloadFile, and deleteFile by removing catches for
FileStorageConfigurationException and instead create a centralized
`@RestControllerAdvice` named FileStorageExceptionHandler with an
`@ExceptionHandler`(FileStorageConfigurationException.class) method (e.g., handle)
that logs the exception and returns
ResponseEntity.internalServerError().body(e.getMessage()) so all controllers
(including CaseFileController) get consistent 500 handling and logging for
FileStorageConfigurationException.
- Line 46: The method signature for downloadFile currently uses
ResponseEntity<?> which hides the response contract; update the signature of
downloadFile to a more explicit type such as ResponseEntity<Object> (or revert
to ResponseEntity<byte[]> if the success path always returns bytes) and adjust
error handling accordingly—e.g., keep ResponseEntity<byte[]> and return an empty
body / use `@ResponseStatus` with your global `@RestControllerAdvice`, or change to
ResponseEntity<Object> and ensure both the success (byte[] payload) and error
(error DTO or message) paths are properly typed so OpenAPI/Swagger and callers
see a clear contract.

In `@src/main/java/backendlab/team4you/s3/S3Service.java`:
- Around line 34-74: The catch blocks in upload/download/delete (methods: the
putObject block, downloadFile, deleteFile) wrongly wrap all S3Exception cases as
FileStorageConfigurationException; introduce a new runtime exception (e.g.,
FileStorageException) for transient/runtime S3 errors and reserve
FileStorageConfigurationException only for genuine config validation failures,
then update the catch clauses to throw FileStorageException instead,
constructing the message to include S3Exception.getMessage(), status code and
AWS error code (or e.awsErrorDetails()) and pass the original exception as the
cause so callers can inspect details.
- Around line 76-93: uploadFileIfAbsent currently catches S3Exception only to
map 409/412 into FileKeyConflictException and rethrows all other S3Exception
instances directly, causing inconsistent exception types compared to uploadFile,
downloadFile, and deleteFile; change uploadFileIfAbsent to catch S3Exception
from s3Client.putObject and, after mapping 409/412 to FileKeyConflictException
(as it already does), wrap all other S3Exception instances in
FileStorageConfigurationException (the same translation used by
uploadFile/downloadFile/deleteFile) so callers always receive consistent
exceptions for SDK failures.

In `@src/test/java/backendlab/team4you/s3/S3ServiceTest.java`:
- Around line 74-105: Replace the fully-qualified exception class in the three
tests in S3ServiceTest
(uploadFile_shouldThrowFileStorageConfigurationException_whenS3Fails,
downloadFile_shouldThrowFileStorageConfigurationException_whenS3Fails,
deleteFile_shouldThrowFileStorageConfigurationException_whenS3Fails) with an
import of backendlab.team4you.exceptions.FileStorageConfigurationException and,
after asserting the exception is thrown, assert that ex.getCause() is an
instance of S3Exception (or that the cause message matches "S3 error") to ensure
the original S3Exception is preserved as the cause.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f52fe721-29a9-41ba-b16c-49aff8eb5cc7

📥 Commits

Reviewing files that changed from the base of the PR and between aac0945 and 77441bc.

📒 Files selected for processing (3)
  • src/main/java/backendlab/team4you/s3/S3Controller.java
  • src/main/java/backendlab/team4you/s3/S3Service.java
  • src/test/java/backendlab/team4you/s3/S3ServiceTest.java

Comment thread src/main/java/backendlab/team4you/s3/S3Controller.java
@ithsjava25 ithsjava25 deleted a comment from coderabbitai Bot Apr 25, 2026
@MartinStenhagen
MartinStenhagen merged commit f937883 into main Apr 28, 2026
2 checks passed
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