Skip to content

MOSU-44 feat: 이벤트 관련 기능 #83

Merged
wlgns12370 merged 10 commits intodevelopfrom
feature/mosu-44
Jul 11, 2025
Merged

MOSU-44 feat: 이벤트 관련 기능 #83
wlgns12370 merged 10 commits intodevelopfrom
feature/mosu-44

Conversation

@chominju02
Copy link
Contributor

@chominju02 chominju02 commented Jul 10, 2025

✨ 구현한 기능

  • 이벤트 등록 기능
  • 이벤트 전체 조회 기능
  • 이벤트 상세 조회 기능
  • 이벤트 업데이트 기능
  • 이벤트 삭제 기능
  • 스웨거 작성

📢 논의하고 싶은 내용

🎸 기타

Summary by CodeRabbit

  • New Features
    • Introduced a complete event management system with create, view, update, and delete endpoints.
    • Added support for event attachments, enabling file uploads and access linked to events.
    • Event listings and details now include attachment info with public URLs for easy access.
    • Enhanced event data with duration and title fields for better event representation.
  • Bug Fixes
    • Improved error handling with clear messages when events are not found.

@chominju02 chominju02 self-assigned this Jul 10, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 10, 2025

Walkthrough

This change introduces a complete event management feature, including domain entities, repositories, services, DTOs, and a REST controller for CRUD operations on events and their attachments. The update adds projection records, error handling enhancements, S3 integration for file attachments, and request/response structures for API endpoints.

Changes

File(s) / Group Change Summary
.../application/event/EventAttachmentService.java New service for handling event attachments, implementing attachment creation and deletion logic.
.../application/event/EventService.java New service managing event CRUD operations, attachment handling, and exception management.
.../domain/event/AttachmentProjection.java
.../EventWithAttachmentProjection.java
New projection records for event and attachment data transfer.
.../domain/event/EventAttachmentJpaEntity.java New JPA entity for event attachments, extending File and linking to events via eventId.
.../domain/event/EventAttachmentRepository.java New repository interface for event attachments with custom query methods.
.../domain/event/EventJpaEntity.java Refactored event entity: removed unused fields, added title, duration, update method, and constructor changes.
.../domain/event/EventJpaRepository.java New repository with JPQL-based projection queries for events and attachments.
.../global/exception/ErrorCode.java Added EVENT_NOT_FOUND error code for event-related exception handling.
.../global/util/FileRequest.java Added method to convert FileRequest to an event attachment entity.
.../presentation/event/EventController.java New REST controller exposing endpoints for creating, retrieving, updating, and deleting events.
.../presentation/event/dto/DurationRequest.java New DTO record for event duration with conversion to value object.
.../presentation/event/dto/EventRequest.java New DTO record for event creation/update requests, including validation and entity conversion methods.
.../presentation/event/dto/EventResponse.java New DTO record for event responses with static factory method and nested attachment response record.
.../presentation/event/EventControllerDocs.java New interface defining Swagger/OpenAPI documentation for event REST API endpoints.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant EventController
    participant EventService
    participant EventJpaRepository
    participant EventAttachmentService
    participant EventAttachmentRepository
    participant S3Service

    Client->>EventController: POST /event (EventRequest)
    EventController->>EventService: createEvent(EventRequest)
    EventService->>EventJpaRepository: save(EventJpaEntity)
    EventService->>EventAttachmentService: createAttachment(FileRequest, EventJpaEntity)
    EventAttachmentService->>EventAttachmentRepository: save(EventAttachmentJpaEntity)
    EventAttachmentService->>S3Service: Upload file to S3
    EventService-->>EventController: Success
    EventController-->>Client: 201 Created

    Client->>EventController: GET /event/list
    EventController->>EventService: getEvents()
    EventService->>EventJpaRepository: findAllWithAttachment()
    EventService->>S3Service: getPublicUrl(s3Key)
    EventService-->>EventController: List<EventResponse>
    EventController-->>Client: 200 OK (List<EventResponse>)
Loading

Possibly related PRs

  • MOSU-45 feat: 공지 기능 구현 #58: Refactors attachment creation in domain-specific services using a shared helper method, similar to the new EventAttachmentService but for different domain entities.

Suggested reviewers

  • wlgns12370

Poem

🐇 A hop, a skip, an event in sight,
Attachments stored and shining bright.
From controller calls to S3's grace,
Each event finds its rightful place.
Projections, DTOs, all aligned,
This bunny’s code is well-designed! 🥕✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7c95acb and f3dcbe2.

📒 Files selected for processing (5)
  • src/main/java/life/mosu/mosuserver/presentation/event/EventController.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/event/EventControllerDocs.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/event/dto/DurationRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/event/dto/EventResponse.java (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/life/mosu/mosuserver/presentation/event/EventControllerDocs.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/main/java/life/mosu/mosuserver/presentation/event/dto/DurationRequest.java
  • src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java
  • src/main/java/life/mosu/mosuserver/presentation/event/dto/EventResponse.java
  • src/main/java/life/mosu/mosuserver/presentation/event/EventController.java
⏰ 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). (1)
  • GitHub Check: build-and-deploy
✨ 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.
    • 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.
    • @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 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.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

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 generate sequence diagram to generate a sequence diagram of the changes in 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.

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

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @chominju02, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a comprehensive event management system, providing full CRUD capabilities for events within the application. It establishes new data models for events and their attachments, integrates with S3 for file storage, and refines existing entity structures for improved maintainability. The changes enable robust handling of event-related data and associated files.

Highlights

  • Event Management API: Implemented a full set of REST endpoints for creating, retrieving (all and by ID), updating, and deleting event records.
  • Event and Attachment Data Models: Introduced new JPA entities (EventJpaEntity, EventAttachmentJpaEntity) and their corresponding repositories (EventJpaRepository, EventAttachmentRepository) to manage event data and associated file attachments.
  • S3 Integration for Attachments: Developed EventAttachmentService to handle the lifecycle of event attachments, including saving and deleting files, leveraging an existing FileUploadHelper and S3Service.
  • Data Transfer Objects (DTOs): Defined new DTOs (EventRequest, EventResponse, DurationRequest, AttachmentProjection, EventWithAttachmentProjection) to facilitate data exchange between the presentation layer and the service layer, and for efficient data retrieval via JPA projections.
  • Entity Refinement: Modified EventJpaEntity to streamline its structure, replacing eventDescription, eventImg, and userId with a title field and adding an update method for cleaner entity management.
  • Error Handling: Added a new EVENT_NOT_FOUND error code to ErrorCode.java for robust error management.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The code changes introduce event management functionality. The review identifies a potential resource leak in S3 and an incorrect error code being used.

Comment on lines +44 to +46
if (eventAttachmentRepository.findByEventId(entity.getId()).isPresent()) {
eventAttachmentRepository.deleteByEventId(entity.getId());
}

Choose a reason for hiding this comment

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

high

The deleteAttachment method only deletes the attachment record from the database but does not delete the corresponding file from S3. This will lead to orphaned files in your S3 bucket, consuming storage and potentially incurring costs.

eventAttachmentRepository.findByEventId(entity.getId()).ifPresent(attachment -> {
    s3Service.deleteFile(attachment);
    eventAttachmentRepository.delete(attachment);
});

Comment on lines +72 to +73
EventJpaEntity eventEntity = eventJpaRepository.findById(eventId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.FILE_NOT_FOUND));

Choose a reason for hiding this comment

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

high

The deleteEvent method throws a CustomRuntimeException with ErrorCode.FILE_NOT_FOUND when an event is not found. This is misleading; it should be ErrorCode.EVENT_NOT_FOUND.

Suggested change
EventJpaEntity eventEntity = eventJpaRepository.findById(eventId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.FILE_NOT_FOUND));
EventJpaEntity eventEntity = eventJpaRepository.findById(eventId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.EVENT_NOT_FOUND));

Copy link

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

♻️ Duplicate comments (2)
src/main/java/life/mosu/mosuserver/presentation/event/EventController.java (2)

52-52: Address commented-out security annotations.

Same security concern as the POST endpoint.


62-62: Address commented-out security annotations.

Same security concern as the other admin endpoints.

🧹 Nitpick comments (7)
src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java (1)

20-23: Potential null pointer exception in optionalAttachment method.

The method directly assigns this.attachment to parsedAttachment without any processing, making the variable name misleading. Also, consider defensive programming practices.

Simplify the method and make it more explicit:

-public List<FileRequest> optionalAttachment() {
-    FileRequest parsedAttachment = this.attachment;
-    return parsedAttachment == null ? List.of() : List.of(parsedAttachment);
-}
+public List<FileRequest> optionalAttachment() {
+    return this.attachment == null ? List.of() : List.of(this.attachment);
+}
src/main/java/life/mosu/mosuserver/domain/event/EventJpaRepository.java (1)

27-43: Code duplication between queries.

The two queries are nearly identical, differing only in the WHERE clause. This violates the DRY principle and makes maintenance harder.

Consider refactoring to reduce duplication by creating a private method with the base query:

public interface EventJpaRepository extends JpaRepository<EventJpaEntity, Long> {

+    String BASE_QUERY = """
+            select new life.mosu.mosuserver.domain.event.EventWithAttachmentProjection(
+                e.id,
+                e.title,
+                e.duration.endDate,
+                e.eventLink,
+                new life.mosu.mosuserver.domain.event.AttachmentProjection(
+                    ea.fileName,
+                    ea.s3Key
+                )
+            )
+            from EventJpaEntity e
+            left join EventAttachmentJpaEntity ea
+            on e.id = ea.eventId
+            """;

-    @Query("""
-            select new life.mosu.mosuserver.domain.event.EventWithAttachmentProjection(
-                e.id,
-                e.title,
-                e.duration.endDate,
-                e.eventLink,
-                new life.mosu.mosuserver.domain.event.AttachmentProjection(
-                    ea.fileName,
-                    ea.s3Key
-                )
-            )
-            from EventJpaEntity e
-            left join EventAttachmentJpaEntity ea
-            on e.id = ea.eventId
-            """)
+    @Query(BASE_QUERY)
     List<EventWithAttachmentProjection> findAllWithAttachment();

-    @Query("""
-             select new life.mosu.mosuserver.domain.event.EventWithAttachmentProjection(
-                 e.id,
-                 e.title,
-                 e.duration.endDate,
-                 e.eventLink,
-                 new life.mosu.mosuserver.domain.event.AttachmentProjection(
-                     ea.fileName,
-                     ea.s3Key
-                 )
-             )
-             from EventJpaEntity e
-             left join EventAttachmentJpaEntity ea
-             on e.id = ea.eventId
-             WHERE e.id = :id
-            """)
+    @Query(BASE_QUERY + " WHERE e.id = :id")
     Optional<EventWithAttachmentProjection> findWithAttachmentById(Long id);
}
src/main/java/life/mosu/mosuserver/application/event/EventAttachmentService.java (1)

43-47: Optimize attachment deletion by removing unnecessary existence check.

The current implementation checks for existence before deletion, which is unnecessary since JPA delete operations are idempotent.

-    public void deleteAttachment(EventJpaEntity entity) {
-        if (eventAttachmentRepository.findByEventId(entity.getId()).isPresent()) {
-            eventAttachmentRepository.deleteByEventId(entity.getId());
-        }
-    }
+    public void deleteAttachment(EventJpaEntity entity) {
+        eventAttachmentRepository.deleteByEventId(entity.getId());
+    }
src/main/java/life/mosu/mosuserver/application/event/EventService.java (3)

31-31: Reconsider transaction propagation for read-only operations.

Using Propagation.SUPPORTS for read-only operations is unusual. Consider using the default REQUIRED propagation or removing the propagation parameter entirely.

-    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
+    @Transactional(readOnly = true)

45-45: Reconsider transaction propagation for read-only operations.

Same issue as the previous read-only method.

-    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
+    @Transactional(readOnly = true)

66-67: Consider optimizing attachment update strategy.

The current approach deletes all attachments and recreates them, which may be inefficient for large attachment sets. Consider implementing a diff-based update strategy that only modifies changed attachments.

This could be optimized to:

  1. Compare existing vs new attachments
  2. Delete only removed attachments
  3. Add only new attachments
  4. Keep unchanged attachments as-is
src/main/java/life/mosu/mosuserver/presentation/event/EventController.java (1)

33-33: Inconsistent HTTP status usage in ResponseEntity construction.

The response returns ResponseEntity.ok() but sets HttpStatus.CREATED in the wrapper. Consider using ResponseEntity.status(HttpStatus.CREATED) for consistency.

-        return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.CREATED, "이벤트 등록 성공"));
+        return ResponseEntity.status(HttpStatus.CREATED)
+                .body(ApiResponseWrapper.success(HttpStatus.CREATED, "이벤트 등록 성공"));
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5d27b4c and 7c95acb.

📒 Files selected for processing (14)
  • src/main/java/life/mosu/mosuserver/application/event/EventAttachmentService.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/application/event/EventService.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/event/AttachmentProjection.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/event/EventAttachmentJpaEntity.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/event/EventAttachmentRepository.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/event/EventJpaEntity.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/domain/event/EventJpaRepository.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/event/EventWithAttachmentProjection.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/global/util/FileRequest.java (3 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/event/EventController.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/event/dto/DurationRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/event/dto/EventResponse.java (1 hunks)
⏰ 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). (1)
  • GitHub Check: build-and-deploy
🔇 Additional comments (6)
src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java (1)

62-64: LGTM! Error code follows established patterns.

The new event-related error code is properly implemented with consistent naming, appropriate HTTP status, and Korean error message that aligns with the existing codebase patterns.

src/main/java/life/mosu/mosuserver/global/util/FileRequest.java (1)

75-83: LGTM! Method follows established patterns.

The new toEventAttachmentEntity method is well-implemented and consistent with existing conversion methods. The use of Visibility.PUBLIC is appropriate for event attachments.

src/main/java/life/mosu/mosuserver/domain/event/EventAttachmentRepository.java (2)

8-8: Add @transactional annotation for delete operation.

The deleteByEventId method should be annotated with @Transactional to ensure the operation is performed within a transaction context.

+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional
 void deleteByEventId(Long eventId);

Likely an incorrect or invalid review comment.


10-10: Verify EventAttachmentRepository.findByEventId Return Type

The current method signature in
src/main/java/life/mosu/mosuserver/domain/event/EventAttachmentRepository.java

Optional<EventAttachmentJpaEntity> findByEventId(Long eventId);

implies only one attachment per event. If your domain allows multiple attachments for a single event, this should return a collection:

- Optional<EventAttachmentJpaEntity> findByEventId(Long eventId);
+ List<EventAttachmentJpaEntity> findByEventId(Long eventId);

Otherwise, please ensure:

  • The database schema enforces a one-to-one mapping (e.g. unique constraint on event_attachment.event_id).
  • Your JPA entity reflects that (e.g. use @OneToOne in EventAttachmentJpaEntity).
src/main/java/life/mosu/mosuserver/domain/event/EventWithAttachmentProjection.java (1)

1-13: Missing import for AttachmentProjection.

The record references AttachmentProjection but doesn't import it. This will cause a compilation error.

Add the missing import:

package life.mosu.mosuserver.domain.event;

import java.time.LocalDate;
+import life.mosu.mosuserver.domain.event.AttachmentProjection;

public record EventWithAttachmentProjection(

Likely an incorrect or invalid review comment.

src/main/java/life/mosu/mosuserver/presentation/event/dto/EventResponse.java (1)

6-34: Well-structured DTO implementation with clean factory method.

The record-based DTO design is excellent and follows modern Java practices. The factory method properly handles projection mapping and S3 URL integration.

Comment on lines +3 to +5
public record AttachmentProjection(String fileName, String s3Key) {

}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation annotations and clean up formatting.

Consider adding validation annotations to ensure data integrity and remove unnecessary whitespace.

+import jakarta.validation.constraints.NotBlank;
+
-public record AttachmentProjection(String fileName, String s3Key) {
+public record AttachmentProjection(
+        @NotBlank String fileName,
+        @NotBlank String s3Key
+) {
-
-}
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public record AttachmentProjection(String fileName, String s3Key) {
}
import jakarta.validation.constraints.NotBlank;
public record AttachmentProjection(
@NotBlank String fileName,
@NotBlank String s3Key
) {
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/domain/event/AttachmentProjection.java
around lines 3 to 5, add appropriate validation annotations such as @NotNull or
@NotBlank to the record components fileName and s3Key to enforce data integrity.
Also, remove any unnecessary whitespace or blank lines within the record
declaration to clean up formatting.

Comment on lines 6 to 16
public record DurationRequest(
LocalDate startDate,
LocalDate endDate
) {

public DurationJpaVO toDurationJpaVO() {
return new DurationJpaVO(startDate, endDate);
}


}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation annotations and business logic validation.

Consider adding validation to ensure data integrity and proper date relationships.

+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.Valid;
+
 public record DurationRequest(
+        @NotNull
         LocalDate startDate,
+        @NotNull
         LocalDate endDate
 ) {
 
+    public DurationRequest {
+        if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
+            throw new IllegalArgumentException("End date must be after start date");
+        }
+    }
+
     public DurationJpaVO toDurationJpaVO() {
         return new DurationJpaVO(startDate, endDate);
     }
-
-
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public record DurationRequest(
LocalDate startDate,
LocalDate endDate
) {
public DurationJpaVO toDurationJpaVO() {
return new DurationJpaVO(startDate, endDate);
}
}
import jakarta.validation.constraints.NotNull;
import jakarta.validation.Valid;
public record DurationRequest(
@NotNull
LocalDate startDate,
@NotNull
LocalDate endDate
) {
public DurationRequest {
if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
throw new IllegalArgumentException("End date must be after start date");
}
}
public DurationJpaVO toDurationJpaVO() {
return new DurationJpaVO(startDate, endDate);
}
}
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/presentation/event/dto/DurationRequest.java
between lines 6 and 16, add validation annotations such as @NotNull on startDate
and endDate fields to ensure they are not null. Additionally, implement business
logic validation inside the record or in a separate validator method to check
that startDate is before or equal to endDate, throwing an appropriate exception
if this condition is violated.

Comment on lines +25 to +31
public EventJpaEntity toEntity() {
return EventJpaEntity.builder()
.title(title)
.eventLink(eventLink)
.duration(duration.toDurationJpaVO())
.build();
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential null pointer exception in toEntity method.

The method calls duration.toDurationJpaVO() without null checking, which could cause a NullPointerException if duration is null.

Add null checking or ensure validation prevents null duration:

public EventJpaEntity toEntity() {
+    if (duration == null) {
+        throw new IllegalArgumentException("Duration cannot be null");
+    }
    return EventJpaEntity.builder()
            .title(title)
            .eventLink(eventLink)
            .duration(duration.toDurationJpaVO())
            .build();
}

Alternatively, if you add the @NotNull validation suggested earlier, this defensive check may not be necessary.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public EventJpaEntity toEntity() {
return EventJpaEntity.builder()
.title(title)
.eventLink(eventLink)
.duration(duration.toDurationJpaVO())
.build();
}
public EventJpaEntity toEntity() {
if (duration == null) {
throw new IllegalArgumentException("Duration cannot be null");
}
return EventJpaEntity.builder()
.title(title)
.eventLink(eventLink)
.duration(duration.toDurationJpaVO())
.build();
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java
around lines 25 to 31, the toEntity method calls duration.toDurationJpaVO()
without checking if duration is null, which can cause a NullPointerException. To
fix this, either add a null check before calling toDurationJpaVO or ensure that
duration is validated as non-null earlier, for example by adding a @NotNull
annotation on the duration field. If the validation is added, the null check
here can be omitted.


String eventLink,

DurationRequest duration,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing validation for required duration field.

The duration field appears to be required for event creation but lacks validation annotations. This could allow null values to be passed through.

Add validation to ensure duration is provided:

-        DurationRequest duration,
+        @NotNull
+        DurationRequest duration,

Don't forget to import @NotNull:

import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
DurationRequest duration,
// imports
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
// … in EventRequest record/class declaration …
@NotNull
DurationRequest duration,
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java
at line 14, the duration field lacks validation to ensure it is not null. Add
the @NotNull annotation to the duration field to enforce that it must be
provided during event creation. Also, import
javax.validation.constraints.NotNull at the top of the file to support this
annotation.

Comment on lines +50 to +55
public void update(final String title, final DurationJpaVO duration, final String eventLink) {
this.title = title;
this.duration = duration;
this.eventLink = eventLink;

}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing null validation in update method.

The update method doesn't validate parameters, which could lead to data integrity issues.

Add validation to the update method:

public void update(final String title, final DurationJpaVO duration, final String eventLink) {
+    if (title == null || title.trim().isEmpty()) {
+        throw new IllegalArgumentException("Title cannot be null or empty");
+    }
+    if (duration == null) {
+        throw new IllegalArgumentException("Duration cannot be null");
+    }
    this.title = title;
    this.duration = duration;
    this.eventLink = eventLink;
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void update(final String title, final DurationJpaVO duration, final String eventLink) {
this.title = title;
this.duration = duration;
this.eventLink = eventLink;
}
public void update(final String title, final DurationJpaVO duration, final String eventLink) {
if (title == null || title.trim().isEmpty()) {
throw new IllegalArgumentException("Title cannot be null or empty");
}
if (duration == null) {
throw new IllegalArgumentException("Duration cannot be null");
}
this.title = title;
this.duration = duration;
this.eventLink = eventLink;
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/domain/event/EventJpaEntity.java around
lines 50 to 55, the update method lacks null checks for its parameters, risking
data integrity. Add validation to ensure that title, duration, and eventLink are
not null before assigning them to the fields. If any parameter is null, throw an
appropriate exception such as IllegalArgumentException to prevent invalid
updates.

Comment on lines +15 to +19
@Entity
@RequiredArgsConstructor
@Getter
@Table(name = "event_attachment")
public class EventAttachmentJpaEntity extends File {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using @NoArgsConstructor instead of @requiredargsconstructor.

Since this is a JPA entity, it typically needs a no-argument constructor for JPA to work properly. The @RequiredArgsConstructor might not provide the appropriate constructor.

Use @NoArgsConstructor with appropriate access level:

@Entity
-@RequiredArgsConstructor
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "event_attachment")
public class EventAttachmentJpaEntity extends File {

Add the import:

import lombok.Builder;
import lombok.Getter;
-import lombok.RequiredArgsConstructor;
+import lombok.NoArgsConstructor;
+import lombok.AccessLevel;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Entity
@RequiredArgsConstructor
@Getter
@Table(name = "event_attachment")
public class EventAttachmentJpaEntity extends File {
// imports
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.AccessLevel;
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "event_attachment")
public class EventAttachmentJpaEntity extends File {
// ...
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/domain/event/EventAttachmentJpaEntity.java
around lines 15 to 19, replace the @RequiredArgsConstructor annotation with
@NoArgsConstructor to provide a no-argument constructor required by JPA. Add the
@NoArgsConstructor annotation with an appropriate access level (e.g., protected)
to ensure JPA can instantiate the entity properly. Also, include the necessary
import statement for @NoArgsConstructor.

Comment on lines +26 to +27
@Column(name = "event_id", nullable = false)
private Long eventId;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing foreign key constraint annotation.

The eventId field references an event but lacks proper JPA relationship annotations. This could lead to referential integrity issues.

Consider adding proper JPA relationship annotations:

-@Column(name = "event_id", nullable = false)
-private Long eventId;
+@Column(name = "event_id", nullable = false)
+@JoinColumn(name = "event_id", referencedColumnName = "event_id")
+private Long eventId;

Alternatively, you could use @ManyToOne if you want object-oriented relationship:

-@Column(name = "event_id", nullable = false)
-private Long eventId;
+@ManyToOne(fetch = FetchType.LAZY)
+@JoinColumn(name = "event_id", referencedColumnName = "event_id")
+private EventJpaEntity event;

Add the import:

import jakarta.persistence.Table;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.FetchType;

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/domain/event/EventAttachmentJpaEntity.java
around lines 26 to 27, the eventId field lacks JPA relationship annotations to
enforce foreign key constraints. Replace the plain Long eventId field with a
proper JPA relationship by adding a @ManyToOne annotation linking to the Event
entity, and update the field type accordingly. Also, add the necessary import
statements for the relationship annotations and the Event class to ensure
referential integrity is maintained at the database level.

Comment on lines +33 to +37
(fileRequest, eventId) -> fileRequest.toEventAttachmentEntity(
fileRequest.fileName(),
fileRequest.s3Key(),
eventEntity.getId()
),
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Redundant parameter passing in attachment creation lambda.

The lambda passes fileRequest.fileName() and fileRequest.s3Key() as separate parameters when they're already accessible from the fileRequest object itself.

-                (fileRequest, eventId) -> fileRequest.toEventAttachmentEntity(
-                        fileRequest.fileName(),
-                        fileRequest.s3Key(),
-                        eventEntity.getId()
-                ),
+                (fileRequest, eventId) -> fileRequest.toEventAttachmentEntity(
+                        eventId
+                ),

Assuming toEventAttachmentEntity can be simplified to only require the eventId parameter.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/application/event/EventAttachmentService.java
around lines 33 to 37, the lambda function redundantly passes fileName and s3Key
extracted from fileRequest to toEventAttachmentEntity, even though these values
are accessible within fileRequest. Modify the lambda to call
toEventAttachmentEntity with only the eventId parameter, and update the
toEventAttachmentEntity method accordingly to use fileRequest's internal fields
directly, removing the need for separate fileName and s3Key parameters.

Comment on lines +72 to +74
EventJpaEntity eventEntity = eventJpaRepository.findById(eventId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.FILE_NOT_FOUND));

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical bug: Wrong error code used for event deletion.

The delete method uses ErrorCode.FILE_NOT_FOUND instead of ErrorCode.EVENT_NOT_FOUND, which would provide incorrect error information to clients.

         EventJpaEntity eventEntity = eventJpaRepository.findById(eventId)
-                .orElseThrow(() -> new CustomRuntimeException(ErrorCode.FILE_NOT_FOUND));
+                .orElseThrow(() -> new CustomRuntimeException(ErrorCode.EVENT_NOT_FOUND));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
EventJpaEntity eventEntity = eventJpaRepository.findById(eventId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.FILE_NOT_FOUND));
EventJpaEntity eventEntity = eventJpaRepository.findById(eventId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.EVENT_NOT_FOUND));
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/application/event/EventService.java around
lines 72 to 74, replace the incorrect error code ErrorCode.FILE_NOT_FOUND with
ErrorCode.EVENT_NOT_FOUND in the orElseThrow call when the event is not found.
This ensures the correct error information is provided when an event deletion
fails due to a missing event.

private final EventService eventService;

@PostMapping
// @PreAuthorize("isAuthenticated() and hasRole('ADMIN')")
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Address commented-out security annotations.

The @PreAuthorize annotations are commented out, leaving admin-only operations unsecured. This presents a security risk in production.

Please verify the security implementation status and either:

  1. Enable the security annotations if authentication is ready
  2. Add TODO comments with timeline for security implementation
  3. Document why these operations are currently unsecured

🏁 Script executed:

#!/bin/bash
# Search for security configuration and authentication setup
rg -A 3 -B 3 "PreAuthorize|SecurityConfig|WebSecurityConfig" --type java

Length of output: 16626


Enable security annotations for EventController admin endpoints

The commented-out @PreAuthorize annotations on admin operations leave these methods publicly accessible despite method security being enabled in SecurityConfig. Please either uncomment and enable these checks or document the rationale and timeline for implementing them:

• File: src/main/java/life/mosu/mosuserver/presentation/event/EventController.java
– Methods: createEvent, updateEvent, deleteEvent

Suggested diff (apply to each admin method):

@@ src/main/java/life/mosu/mosuserver/presentation/event/EventController.java
-    //    @PreAuthorize("isAuthenticated() and hasRole('ADMIN')")
+    @PreAuthorize("isAuthenticated() and hasRole('ADMIN')")  // enforce admin-only access

If authentication isn’t ready yet, add a // TODO with expected completion date or link to a tracking issue.

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/presentation/event/EventController.java at
line 29 and similarly for the createEvent, updateEvent, and deleteEvent methods,
the @PreAuthorize annotations are commented out, leaving these admin endpoints
unsecured. To fix this, uncomment the @PreAuthorize annotations to enforce that
only authenticated users with the ADMIN role can access these methods. If
authentication is not yet implemented, add a TODO comment with an expected
completion date or a link to a tracking issue explaining when these security
checks will be enabled.

@wlgns12370 wlgns12370 merged commit dd11919 into develop Jul 11, 2025
2 checks passed
@wlgns12370 wlgns12370 deleted the feature/mosu-44 branch July 15, 2025 17:02
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