Skip to content

[Feat/s3-presigned-url]#51

Merged
ekgns33 merged 9 commits into
mainfrom
feat/s3-image-upload
Apr 13, 2025
Merged

[Feat/s3-presigned-url]#51
ekgns33 merged 9 commits into
mainfrom
feat/s3-image-upload

Conversation

@ekgns33
Copy link
Copy Markdown
Contributor

@ekgns33 ekgns33 commented Apr 13, 2025

작업내역

#37

다음 작업

  • S3 데이터 올리기
  • S3 보안 작업
  • 클라이언트 시퀀스 다이어 그램 그리기

Summary by CodeRabbit

  • New Features
    • Enhanced AWS cloud integration, enabling secure file storage and management.
    • Introduced a new API endpoint for image uploads that generates time-limited secure URLs.
    • Streamlined configuration for cloud credentials, region selection, and storage bucket settings.
    • Added support for generating presigned URLs for file uploads through a dedicated service.
    • New request and response structures for image upload operations.
    • Improved error handling for external service exceptions.

@ekgns33 ekgns33 self-assigned this Apr 13, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2025

Walkthrough

This pull request integrates AWS S3 support into the project. New dependencies were added to the build configuration for AWS SDK and Spring Cloud AWS. A configuration class was introduced to provide an Amazon S3 client bean with AWS credentials and region setup. Additional components include an enum for response codes, a REST controller for handling image upload requests by generating presigned URLs, a service for interacting with AWS S3, and a request record for image uploads. The application configuration specifies credentials, region, and S3 bucket details.

Changes

File(s) Change Summary
build.gradle Added AWS SDK for S3 (software.amazon.awssdk:s3), AWS SDK BOM (software.amazon.awssdk:bom:2.20.56), and Spring Cloud AWS starter (io.awspring.cloud:spring-cloud-aws-starter:3.1.1).
src/main/resources/application.yml Added AWS configuration under cloud.aws for credentials, region, and S3 bucket.
src/main/java/.../config/S3Config.java Introduced S3Config class to configure and provide an S3Presigner bean using credentials and region from properties.
src/main/java/.../external/{ExternalResponseCode.java, ImageUploadController.java, ImageUploadRequest.java, S3Service.java} Added a new enum for response codes, a REST controller (ImageUploadController) to handle image upload requests, a record (ImageUploadRequest) for request payloads, and an S3 service (S3Service) to generate presigned URLs for file uploads.
src/main/java/.../exceptions/GlobalExceptionHandler.java Added a method to handle ExternalServiceException in the global exception handler.
src/main/java/.../external/ExternalServiceException.java Introduced ExternalServiceException class for handling external service errors with a static factory method for instantiation.

Sequence Diagram(s)

sequenceDiagram
    participant C as Client
    participant CTRL as ImageUploadController
    participant SVC as S3Service
    participant AWS as AmazonS3

    C->>CTRL: POST /api/v1/uploads (with fileName)
    CTRL->>SVC: generatePresignedUrl(fileName)
    SVC->>AWS: GeneratePresignedUrlRequest (bucket, object key, expiration)
    AWS-->>SVC: presigned URL
    SVC-->>CTRL: URL
    CTRL-->>C: HTTP 201 Created (SuccessResponse with presigned URL)
Loading

Poem

Oh, how I hop with joyful glee,
AWS clouds now sing with ease.
S3 URLs sprout like garden blooms,
Code and configs in merry rooms.
A rabbit's dance in digital skies,
Celebrating changes that truly surprise!

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (5)
src/main/java/org/runimo/runimo/external/ImageUploadRequest.java (1)

7-10: LGTM with a suggestion for enhanced validation

The record is well-designed and uses appropriate annotations for documentation and basic validation. Consider adding more robust validation to prevent potential security issues like path traversal attacks.

Consider adding an additional validation to ensure the fileName doesn't contain path traversal characters:

@Schema(description = "이미지 업로드 url발급 요청")
public record ImageUploadRequest(
    @Schema(description = "파일명")
-   @NotEmpty String fileName
+   @NotEmpty 
+   @Pattern(regexp = "^[^/\\\\:*?\"<>|]+$", message = "Invalid filename")
    String fileName
) {

}
src/main/java/org/runimo/runimo/config/S3Config.java (1)

23-31: Consider leveraging Spring Cloud AWS auto-configuration

Spring Cloud AWS starter typically provides auto-configuration for AWS clients, making this manual configuration potentially redundant.

If you're using spring-cloud-aws-starter:3.1.1, verify if this manual configuration is necessary or if it can be replaced by using the auto-configured beans.

Check if you're duplicating the auto-configuration provided by Spring Cloud AWS. If manual configuration is needed, consider adding validation and error handling for the injected values:

@Bean
public AmazonS3 amazonS3Client() {
+   if (accessKey == null || accessKey.isEmpty() ||
+       secretKey == null || secretKey.isEmpty() ||
+       region == null || region.isEmpty()) {
+       throw new IllegalStateException("AWS credentials or region not properly configured");
+   }
    return AmazonS3Client.builder()
        .withRegion(region)
        .withCredentials(new AWSStaticCredentialsProvider(
            new BasicAWSCredentials(accessKey, secretKey)
        ))
        .build();
}
src/main/java/org/runimo/runimo/external/S3Service.java (2)

18-19: Consider making the S3 path prefix configurable.

The "uploads/" prefix is hardcoded in the method. For better flexibility, consider making this configurable via properties.

@Value("${spring.cloud.aws.s3.bucket}")
private String bucketName;
+@Value("${spring.cloud.aws.s3.upload-prefix:uploads/}")
+private String uploadPrefix;

public URL generatePresignedUrl(String fileName) {
-    String objectKey = "uploads/" + UUID.randomUUID() + "_" + fileName;
+    String objectKey = uploadPrefix + UUID.randomUUID() + "_" + fileName;

25-25: Translate comment to English for consistency.

The comment "15분" is in Korean. For consistent code readability, consider using English comments.

- expTimeMillis += 1000 * 60 * 15; // 15분
+ expTimeMillis += 1000 * 60 * 15; // 15 minutes
src/main/java/org/runimo/runimo/external/ExternalResponseCode.java (1)

7-7: Consider internationalization for messages.

The success messages are currently hardcoded in Korean. For international applications, consider implementing a message source for internationalization.

You could use Spring's MessageSource to load messages based on the user's locale:

- PRESIGNED_URL_FETCHED(HttpStatus.CREATED, "Presigned URL 발급 성공", "Presigned URL 발급 성공");
+ PRESIGNED_URL_FETCHED(HttpStatus.CREATED, "presigned.url.success", "presigned.url.success.log");

@Override
public String getClientMessage() {
-    return this.clientMessage;
+    return messageSource.getMessage(this.clientMessage, null, LocaleContextHolder.getLocale());
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0fba0c and 194fc0d.

📒 Files selected for processing (7)
  • build.gradle (1 hunks)
  • src/main/java/org/runimo/runimo/config/S3Config.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/ExternalResponseCode.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/ImageUploadController.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/ImageUploadRequest.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/S3Service.java (1 hunks)
  • src/main/resources/application.yml (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/org/runimo/runimo/external/ImageUploadController.java (1)
src/main/java/org/runimo/runimo/user/controller/MyPageController.java (1)
  • RestController (13-28)
🔇 Additional comments (2)
src/main/java/org/runimo/runimo/external/ExternalResponseCode.java (1)

6-38: LGTM! Well-structured implementation of custom response codes.

The enum implementation of CustomResponseCode is clean and follows good practices. The use of an enum for response codes makes the code more maintainable and less error-prone.

src/main/java/org/runimo/runimo/external/ImageUploadController.java (1)

16-20: Well-structured controller with clear API documentation.

The controller is well-organized with appropriate Swagger annotations for API documentation. The endpoint follows RESTful practices with proper HTTP status codes.

Comment thread build.gradle
Comment thread src/main/java/org/runimo/runimo/config/S3Config.java Outdated
Comment thread src/main/resources/application.yml Outdated
Comment thread src/main/java/org/runimo/runimo/external/S3Service.java
Comment thread src/main/java/org/runimo/runimo/external/ImageUploadController.java
Comment thread src/main/java/org/runimo/runimo/external/ImageUploadController.java
Comment thread src/main/java/org/runimo/runimo/external/ImageUploadController.java
@ekgns33
Copy link
Copy Markdown
Contributor Author

ekgns33 commented Apr 13, 2025

AWS SDK 1.xx 버전이 2025년 12월까지만 지원하는 이유로 업데이트를 진행했습니다.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
src/main/java/org/runimo/runimo/config/S3Config.java (1)

13-20: ⚠️ Potential issue

Fix property path mismatch in @value annotations.

There's a mismatch between the property paths in the @Value annotations and how they're defined in application.yml. This will prevent the values from being injected correctly.

Correct the property paths in the @Value annotations:

-@Value("${spring.cloud.aws.credentials.access-key}")
+@Value("${cloud.aws.credentials.access-key}")
private String accessKey;

-@Value("${spring.cloud.aws.credentials.secret-key}")
+@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;

-@Value("${spring.cloud.aws.region.static}")
+@Value("${cloud.aws.region.static}")
private String region;
src/main/java/org/runimo/runimo/external/ImageUploadController.java (2)

31-41: ⚠️ Potential issue

Add request validation, authentication, and error handling.

The current implementation is missing several important aspects for a secure and robust API endpoint: user authentication, input validation, and error handling.

@PostMapping
public ResponseEntity<SuccessResponse<String>> upload(
+   @UserId Long userId, // Get authenticated user ID
-   @RequestBody ImageUploadRequest request
+   @Valid @RequestBody ImageUploadRequest request
) {
+   try {
        URL presignedUrl = s3Service.generatePresignedUrl(request.fileName());
        return ResponseEntity.status(201)
            .body(SuccessResponse.of(
                ExternalResponseCode.PRESIGNED_URL_FETCHED,
                presignedUrl.toExternalForm()
            ));
+   } catch (IllegalArgumentException e) {
+       // Log the error
+       return ResponseEntity.badRequest()
+           .body(/* appropriate error response */);
+   } catch (Exception e) {
+       // Log the error
+       return ResponseEntity.status(500)
+           .body(/* appropriate error response */);
+   }
}

Also, ensure your ImageUploadRequest record includes proper validation:

public record ImageUploadRequest(
    @NotBlank(message = "File name is required")
    @Pattern(regexp = ".*\\.(jpg|jpeg|png|gif)$", message = "Only image files (jpg, jpeg, png, gif) are allowed")
    String fileName,
    
    @NotBlank(message = "Content type is required")
    @Pattern(regexp = "image/(jpeg|png|gif|svg\\+xml)", message = "Invalid image content type")
    String contentType,
    
    @NotNull(message = "File size is required")
    @Max(value = 5242880, message = "File size must not exceed 5MB")
    Long fileSize
) {}

16-20: 🛠️ Refactor suggestion

Controller setup is good, but missing security annotations.

The controller setup with API documentation looks good, but based on other controllers in your codebase, authentication mechanisms should be in place.

Consider adding security annotations to restrict access:

@Tag(description = "파일 업로드 관련 API", name = "FILE UPLOAD")
@RestController
@RequestMapping("/api/v1/uploads")
@RequiredArgsConstructor
+@SecurityRequirement(name = "bearerAuth")  // If you're using OpenAPI with security
public class ImageUploadController {
🧹 Nitpick comments (3)
build.gradle (1)

47-51: AWS SDK integration looks good, but consider updating to dedicated S3 starter.

The AWS SDK 2.x dependencies are correctly added with a specified BOM version (2.20.56), which is a good practice. The Spring Cloud AWS starter is also included.

For better dependency management, consider using the S3-specific starter instead of the generic one:

// infra
implementation(platform("software.amazon.awssdk:bom:2.20.56"))
implementation("software.amazon.awssdk:s3")
-implementation 'io.awspring.cloud:spring-cloud-aws-starter:3.1.1'
+implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3:3.1.1'
src/main/java/org/runimo/runimo/config/S3Config.java (1)

22-30: S3Presigner implementation looks good but lacks error handling.

The S3Presigner bean is correctly implemented using the builder pattern with region and credentials. However, consider adding error handling for cases where the credentials or region might be invalid.

Add try-catch block to handle credential initialization errors:

@Bean
public S3Presigner amazonS3Client() {
+   try {
        return S3Presigner.builder()
            .region(Region.of(region))
            .credentialsProvider(
                () -> software.amazon.awssdk.auth.credentials.AwsBasicCredentials.create(accessKey, secretKey)
            )
            .build();
+   } catch (IllegalArgumentException e) {
+       throw new IllegalStateException("Failed to initialize S3Presigner: " + e.getMessage(), e);
+   }
}
src/main/java/org/runimo/runimo/external/S3Service.java (1)

39-44: Helper method for GET requests won't be needed after switching to PUT.

If you switch to using PUT requests for uploads as suggested above, you won't need this helper method since we'll directly create the PutObjectRequest. If you keep it, consider adding null validation for parameters.

If keeping this method:

private GetObjectRequest createGetObjectRequest(String bucketName, String objectKey) {
+   if (bucketName == null || objectKey == null) {
+       throw new IllegalArgumentException("Bucket name and object key cannot be null");
+   }
    return GetObjectRequest.builder()
        .bucket(bucketName)
        .key(objectKey)
        .build();
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 194fc0d and 1be761f.

📒 Files selected for processing (4)
  • build.gradle (1 hunks)
  • src/main/java/org/runimo/runimo/config/S3Config.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/ImageUploadController.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/S3Service.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/org/runimo/runimo/external/ImageUploadController.java (1)
src/main/java/org/runimo/runimo/user/controller/MyPageController.java (1)
  • RestController (13-28)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and analyze

Comment thread src/main/java/org/runimo/runimo/external/S3Service.java
Comment thread src/main/java/org/runimo/runimo/external/S3Service.java
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/test/resources/application.yml (1)

61-70: AWS S3 Configuration Integration

The new cloud.aws section is well structured and clearly defines the necessary parameters for AWS S3 integration. The use of environment variable placeholders (${AWS_ACCESS_KEY}, ${AWS_SECRET_KEY}, ${AWS_REGION}, ${AWS_S3_BUCKET_NAME}) ensures that sensitive credentials are not hard-coded, which aligns with best practices. Please verify that these property names match those expected in your S3Config class and that the necessary environment variables are set in your test and deployment environments. Additionally, you might consider adding inline comments to document that these values are dynamically injected from the environment, which can aid future maintainers.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1be761f and fce5061.

📒 Files selected for processing (4)
  • src/main/java/org/runimo/runimo/config/S3Config.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/S3Service.java (1 hunks)
  • src/main/resources/application.yml (1 hunks)
  • src/test/resources/application.yml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/resources/application.yml
  • src/main/java/org/runimo/runimo/config/S3Config.java
  • src/main/java/org/runimo/runimo/external/S3Service.java

Copy link
Copy Markdown
Contributor

@jeeheaG jeeheaG left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!
AI 리뷰가 야무지네요

Comment thread src/main/java/org/runimo/runimo/external/ExternalResponseCode.java
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/main/java/org/runimo/runimo/external/S3Service.java (1)

24-36: ⚠️ Potential issue

Add input validation and error handling.

The method should validate the input parameter and handle potential AWS exceptions.

public URL generatePresignedUrl(String fileName) {
+    if (fileName == null || fileName.trim().isEmpty()) {
+        throw new IllegalArgumentException("File name cannot be null or empty");
+    }
+    
+    try {
        String objectKey = "uploads/" + UUID.randomUUID() + "_" + fileName;

        PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
            .signatureDuration(Duration.ofMinutes(10))  // The URL expires in 10 minutes.
            .putObjectRequest(createPutObjectRequest(bucketName, objectKey))
            .build();

        PresignedPutObjectRequest presignedRequest = s3Presigner.presignPutObject(presignRequest);
        log.info("Presigned URL: [{}]", presignedRequest.url().toString());
        log.debug("HTTP method: [{}]", presignedRequest.httpRequest().method());
        return presignedRequest.url();
+    } catch (Exception e) {
+        log.error("Error generating presigned URL: {}", e.getMessage(), e);
+        throw new RuntimeException("Failed to generate presigned URL", e);
+    }
}
🧹 Nitpick comments (2)
src/main/java/org/runimo/runimo/external/S3Service.java (2)

24-36: Consider returning additional metadata with the presigned URL.

The current implementation only returns the URL. It might be helpful to return additional information such as the generated object key or expiration time to the client.

-public URL generatePresignedUrl(String fileName) {
+public PresignedUrlResponse generatePresignedUrl(String fileName) {
    String objectKey = "uploads/" + UUID.randomUUID() + "_" + fileName;

    PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
        .signatureDuration(Duration.ofMinutes(10))  // The URL expires in 10 minutes.
        .putObjectRequest(createPutObjectRequest(bucketName, objectKey))
        .build();

    PresignedPutObjectRequest presignedRequest = s3Presigner.presignPutObject(presignRequest);
    log.info("Presigned URL: [{}]", presignedRequest.url().toString());
    log.debug("HTTP method: [{}]", presignedRequest.httpRequest().method());
-    return presignedRequest.url();
+    return new PresignedUrlResponse(
+        presignedRequest.url(),
+        objectKey,
+        presignedRequest.expiration()
+    );
}

// Create this class in an appropriate location
+public record PresignedUrlResponse(URL url, String objectKey, Instant expiration) {}

38-43: Consider adding content-type and other metadata options.

The current implementation doesn't specify content-type or other metadata for the uploaded files, which might be important for proper handling by browsers or downstream systems.

private PutObjectRequest createPutObjectRequest(String bucketName, String objectKey) {
    return PutObjectRequest.builder()
        .bucket(bucketName)
        .key(objectKey)
+       .contentType(determineContentType(objectKey))
        .build();
}

+/**
+ * Determine the content type based on file extension.
+ * This is important for browsers to properly handle the file.
+ */
+private String determineContentType(String objectKey) {
+    String extension = objectKey.substring(objectKey.lastIndexOf('.') + 1).toLowerCase();
+    return switch (extension) {
+        case "jpg", "jpeg" -> "image/jpeg";
+        case "png" -> "image/png";
+        case "gif" -> "image/gif";
+        case "pdf" -> "application/pdf";
+        default -> "application/octet-stream";
+    };
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fce5061 and 887681b.

📒 Files selected for processing (2)
  • src/main/java/org/runimo/runimo/external/ExternalResponseCode.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/S3Service.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/runimo/runimo/external/ExternalResponseCode.java
🔇 Additional comments (1)
src/main/java/org/runimo/runimo/external/S3Service.java (1)

21-22:

Details

❓ Verification inconclusive

Make sure the property path matches your application configuration.

The @Value annotation refers to cloud.aws.s3.bucket, so ensure this property is correctly defined in your application.yml or application.properties file.


🏁 Script executed:

#!/bin/bash
# Check if the property is defined in application configuration files
echo "Looking for S3 bucket configuration in application files..."
fd -t f "application.*yml" -x cat {} | grep -n "cloud.aws.s3.bucket"
fd -t f "application.*properties" -x cat {} | grep -n "cloud.aws.s3.bucket"

Length of output: 277


Re-check the S3 Bucket Configuration in Your App Files

The code in src/main/java/org/runimo/runimo/external/S3Service.java uses the property cloud.aws.s3.bucket via the @Value annotation. However, our initial automated search did not return any occurrences of this property in your application configuration files (e.g., application.yml or application.properties). Please manually verify that the property is correctly defined in your configuration—if it’s missing, add it to ensure proper functionality.

  • File: src/main/java/org/runimo/runimo/external/S3Service.java (Lines 21-22)

Comment thread src/main/java/org/runimo/runimo/external/S3Service.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/main/java/org/runimo/runimo/external/ExternalResponseCode.java (1)

6-41: Consider consolidating response code conventions across the project.
As noted in previous reviews, there are multiple variations of response code fields. Adopting a unified naming convention or class structure for these enums or constants can simplify maintenance and improve clarity.

Would you like me to outline a refactoring approach for standardizing these code fields?

src/main/java/org/runimo/runimo/external/S3Service.java (1)

50-54: Filename sanitization approach is reasonable.
Replacing unsafe characters with underscores helps mitigate potential security issues like path traversal.

Consider logging or tracking the original filename for debugging purposes (e.g., user naming habits, potential repeated conflicts).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 887681b and 473d860.

📒 Files selected for processing (4)
  • src/main/java/org/runimo/runimo/exceptions/GlobalExceptionHandler.java (2 hunks)
  • src/main/java/org/runimo/runimo/external/ExternalResponseCode.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/ExternalServiceException.java (1 hunks)
  • src/main/java/org/runimo/runimo/external/S3Service.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/java/org/runimo/runimo/exceptions/GlobalExceptionHandler.java (2)
src/main/java/org/runimo/runimo/external/ExternalServiceException.java (1)
  • ExternalServiceException (6-15)
src/main/java/org/runimo/runimo/common/response/ErrorResponse.java (1)
  • ErrorResponse (5-22)
src/main/java/org/runimo/runimo/external/S3Service.java (1)
src/main/java/org/runimo/runimo/external/ExternalServiceException.java (1)
  • ExternalServiceException (6-15)
🔇 Additional comments (7)
src/main/java/org/runimo/runimo/exceptions/GlobalExceptionHandler.java (2)

10-10: No issues with the new import.


133-139: Looks good!
Having a dedicated handler for ExternalServiceException increases clarity and maintainability. The mapping of the exception to its corresponding HTTP status and error response is properly implemented.

src/main/java/org/runimo/runimo/external/ExternalServiceException.java (1)

6-14: Nicely structured custom exception.
The protected constructor and static factory method simplify exception creation, and the approach ensures consistent usage of CustomResponseCode.

src/main/java/org/runimo/runimo/external/S3Service.java (4)

24-39: Robust logic for generating presigned PUT URLs.

  1. The try-catch block is appropriate for error handling, throwing a meaningful external service exception.
  2. The default 10-minute expiration is reasonable.
  3. The unique object key naming pattern is good, but ensure it aligns with your desired upload path.

41-48: Good filename validation.
Checks for null or empty strings and enforces a maximum length. This step helps prevent edge-case errors downstream.


56-61: Concise helper method for building a PutObjectRequest.
This improves readability by keeping the main method uncluttered. No issues noted.


21-22:

Details

❓ Verification inconclusive

Confirm bucket property path.
Make sure "${cloud.aws.s3.bucket}" aligns with your application.yml property. If your config references spring.cloud.aws.s3.bucket instead, adjust accordingly.


🏁 Script executed:

#!/bin/bash
# Searching for the s3 bucket property reference in yml files.
fd -e yml --exec cat {} | rg 's3'

Length of output: 51


Action: Confirm Correct S3 Bucket Property Path

The service currently injects the value using "${cloud.aws.s3.bucket}", but your YAML files only reveal an s3: section without clear evidence of whether the full key is under cloud.aws.s3.bucket or spring.cloud.aws.s3.bucket. Please verify manually in your YAML configuration that the hierarchical key matches the property injected in S3Service.java (lines 21–22). If the YAML defines the bucket under spring.cloud.aws.s3.bucket, update the annotation accordingly.

@ekgns33 ekgns33 merged commit 573e3fc into main Apr 13, 2025
3 checks passed
@ekgns33 ekgns33 deleted the feat/s3-image-upload branch April 13, 2025 15:08
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.

2 participants