-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* feat: Carousel 저장 API * feat: Carousel 삭제 API 구현 및 file 연관관계 REMOVE로 변경 * feat: Carousel 조회 API 구현 * refactor: 유효성 검증 추가 Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * refactor: 스키마 일치화 Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * refactor: Controller NPE 방지 * fix: Facade 계층 테스트 코드 작성 * fix: 도메인 계층 테스트 코드 작성 --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Loading branch information
1 parent
f1c4e66
commit e2fe9ca
Showing
27 changed files
with
774 additions
and
4 deletions.
There are no files selected for viewing
25 changes: 25 additions & 0 deletions
25
aics-admin/src/main/java/kgu/developers/admin/carousel/application/CarouselAdminFacade.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package kgu.developers.admin.carousel.application; | ||
|
||
import org.springframework.stereotype.Component; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import kgu.developers.admin.carousel.presentation.request.CarouselRequest; | ||
import kgu.developers.admin.carousel.presentation.response.CarouselPersistResponse; | ||
import kgu.developers.domain.carousel.application.command.CarouselCommandService; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class CarouselAdminFacade { | ||
private final CarouselCommandService carouselCommandService; | ||
|
||
@Transactional | ||
public CarouselPersistResponse createCarousel(Long fileId, CarouselRequest request) { | ||
Long id = carouselCommandService.createCarousel(fileId, request.text(), request.link()); | ||
return CarouselPersistResponse.of(id); | ||
} | ||
|
||
public void deleteCarousel(Long id) { | ||
carouselCommandService.deleteCarouselById(id); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
...min/src/main/java/kgu/developers/admin/carousel/presentation/CarouselAdminController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package kgu.developers.admin.carousel.presentation; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.Parameter; | ||
import io.swagger.v3.oas.annotations.media.Content; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.validation.Valid; | ||
import jakarta.validation.constraints.Positive; | ||
import kgu.developers.admin.carousel.presentation.request.CarouselRequest; | ||
import kgu.developers.admin.carousel.presentation.response.CarouselPersistResponse; | ||
|
||
@Tag(name = "Carousel", description = "캐러셀 관리자 API") | ||
public interface CarouselAdminController { | ||
|
||
@Operation(summary = "캐러셀 저장 API", description = """ | ||
- Description : 이 API는 캐러셀 이미지와 기타 정보를 저장합니다. | ||
- Assignee : 이한음 | ||
""") | ||
@ApiResponse( | ||
responseCode = "201", | ||
content = @Content(schema = @Schema(implementation = CarouselPersistResponse.class))) | ||
ResponseEntity<CarouselPersistResponse> createCarousel( | ||
@Parameter( | ||
description = "캐러셀에 개시할 이미지의 ID 입니다.", | ||
example = "1", | ||
required = true | ||
) @Positive @RequestParam Long fileId, | ||
@Parameter( | ||
description = "캐러셀 생성 request 객체 입니다." | ||
) @Valid @RequestBody CarouselRequest request | ||
); | ||
|
||
@Operation(summary = "캐러셀 삭제 API", description = """ | ||
- Description : 이 API는 캐러셀을 삭제합니다. | ||
- Assignee : 이한음 | ||
""") | ||
@ApiResponse(responseCode = "204") | ||
ResponseEntity<Void> deleteCarousel( | ||
@Parameter( | ||
description = "캐러셀 ID는 URL 경로 변수 입니다.", | ||
example = "1", | ||
required = true | ||
) @Positive @PathVariable Long id | ||
); | ||
} |
45 changes: 45 additions & 0 deletions
45
...src/main/java/kgu/developers/admin/carousel/presentation/CarouselAdminControllerImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package kgu.developers.admin.carousel.presentation; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.access.prepost.PreAuthorize; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import jakarta.validation.Valid; | ||
import jakarta.validation.constraints.Positive; | ||
import kgu.developers.admin.carousel.application.CarouselAdminFacade; | ||
import kgu.developers.admin.carousel.presentation.request.CarouselRequest; | ||
import kgu.developers.admin.carousel.presentation.response.CarouselPersistResponse; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/carousels") | ||
@PreAuthorize("hasRole('ROLE_ADMIN')") | ||
public class CarouselAdminControllerImpl implements CarouselAdminController { | ||
private final CarouselAdminFacade carouselAdminFacade; | ||
|
||
@Override | ||
@PostMapping | ||
public ResponseEntity<CarouselPersistResponse> createCarousel( | ||
@Positive @RequestParam Long fileId, | ||
@Valid @RequestBody CarouselRequest request | ||
) { | ||
CarouselPersistResponse response = carouselAdminFacade.createCarousel(fileId, request); | ||
return ResponseEntity.ok(response); | ||
} | ||
|
||
@Override | ||
@DeleteMapping("/{id}") | ||
public ResponseEntity<Void> deleteCarousel( | ||
@Positive @PathVariable Long id | ||
) { | ||
carouselAdminFacade.deleteCarousel(id); | ||
return ResponseEntity.noContent().build(); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
...min/src/main/java/kgu/developers/admin/carousel/presentation/request/CarouselRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package kgu.developers.admin.carousel.presentation.request; | ||
|
||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; | ||
|
||
import org.hibernate.validator.constraints.URL; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import jakarta.validation.constraints.Size; | ||
|
||
public record CarouselRequest( | ||
@Schema(description = "캐러셀 설명", example = "경기대학교 AI컴퓨터공학부 메인 이미지", requiredMode = NOT_REQUIRED) | ||
@Size(max = 255, message = "설명은 255자를 초과할 수 없습니다") | ||
String text, | ||
|
||
@Schema(description = "캐러셀 이미지 링크", example = "https://www.kgu.ac.kr/", requiredMode = NOT_REQUIRED) | ||
@URL(message = "올바른 URL 형식이어야 합니다") | ||
String link | ||
) { | ||
} |
18 changes: 18 additions & 0 deletions
18
...ain/java/kgu/developers/admin/carousel/presentation/response/CarouselPersistResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package kgu.developers.admin.carousel.presentation.response; | ||
|
||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.Builder; | ||
|
||
@Builder | ||
public record CarouselPersistResponse( | ||
@Schema(description = "캐러셀 ID", example = "1", requiredMode = REQUIRED) | ||
Long id | ||
) { | ||
public static CarouselPersistResponse of(Long id) { | ||
return CarouselPersistResponse.builder() | ||
.id(id) | ||
.build(); | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
aics-admin/src/testFixtures/java/carousel/application/CarouselAdminFacadeTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package carousel.application; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import kgu.developers.admin.carousel.application.CarouselAdminFacade; | ||
import kgu.developers.admin.carousel.presentation.request.CarouselRequest; | ||
import kgu.developers.admin.carousel.presentation.response.CarouselPersistResponse; | ||
import kgu.developers.domain.carousel.application.command.CarouselCommandService; | ||
import kgu.developers.domain.file.application.query.FileQueryService; | ||
import kgu.developers.domain.file.domain.FileEntity; | ||
import mock.repository.FakeCarouselRepository; | ||
import mock.repository.FakeFileRepository; | ||
|
||
public class CarouselAdminFacadeTest { | ||
private CarouselAdminFacade carouselAdminFacade; | ||
|
||
private static final Long TEST_FILE_ID = 1L; | ||
private static final Long SAVE_TARGET_ID = 1L; | ||
|
||
@BeforeEach | ||
public void init() { | ||
initializeCarouselAdminFacade(); | ||
} | ||
|
||
private void initializeCarouselAdminFacade() { | ||
FakeFileRepository fakeFileRepository = new FakeFileRepository(); | ||
carouselAdminFacade = new CarouselAdminFacade( | ||
new CarouselCommandService( | ||
new FileQueryService(fakeFileRepository), | ||
new FakeCarouselRepository() | ||
) | ||
); | ||
saveTestFile(fakeFileRepository); | ||
} | ||
|
||
private static void saveTestFile(FakeFileRepository fakeFileRepository) { | ||
fakeFileRepository.save( | ||
FileEntity.create( | ||
"경기대학교 AI컴퓨터공학부 메인 이미지", | ||
"/files/carousel/main_image.jpg", | ||
1234L, | ||
"image/jpeg" | ||
) | ||
); | ||
} | ||
|
||
@Test | ||
@DisplayName("createCarousel은 Carousel을 생성한다") | ||
public void createCarousel_Success() { | ||
// given | ||
CarouselRequest request = new CarouselRequest("경기대학교 AI컴퓨터공학부 메인 이미지", "https://www.kgu.ac.kr/"); | ||
|
||
// when | ||
CarouselPersistResponse response = carouselAdminFacade.createCarousel(TEST_FILE_ID, request); | ||
|
||
// then | ||
assertEquals(SAVE_TARGET_ID, response.id()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
aics-api/src/main/java/kgu/developers/api/carousel/application/CarouselFacade.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package kgu.developers.api.carousel.application; | ||
|
||
import java.util.List; | ||
|
||
import org.springframework.stereotype.Component; | ||
|
||
import kgu.developers.api.carousel.presentation.response.CarouselListResponse; | ||
import kgu.developers.domain.carousel.application.query.CarouselQueryService; | ||
import kgu.developers.domain.carousel.domain.Carousel; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class CarouselFacade { | ||
private final CarouselQueryService carouselQueryService; | ||
|
||
public CarouselListResponse getCarousels() { | ||
List<Carousel> carousels = carouselQueryService.getAllCarousels(); | ||
return CarouselListResponse.from(carousels); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
aics-api/src/main/java/kgu/developers/api/carousel/presentation/CarouselController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package kgu.developers.api.carousel.presentation; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.media.Content; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import kgu.developers.api.carousel.presentation.response.CarouselListResponse; | ||
|
||
@Tag(name = "Carousel", description = "캐러셀 API") | ||
public interface CarouselController { | ||
|
||
@Operation(summary = "캐러셀 리스트 조회 API", description = """ | ||
- Description : 이 API는 모든 캐러셀 이미지와 기타 정보를 리스트로 조회합니다. | ||
- Assignee : 이한음 | ||
""") | ||
@ApiResponse( | ||
responseCode = "200", | ||
content = @Content(schema = @Schema(implementation = CarouselListResponse.class))) | ||
ResponseEntity<CarouselListResponse> getCarousels(); | ||
} |
24 changes: 24 additions & 0 deletions
24
aics-api/src/main/java/kgu/developers/api/carousel/presentation/CarouselControllerImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package kgu.developers.api.carousel.presentation; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import kgu.developers.api.carousel.application.CarouselFacade; | ||
import kgu.developers.api.carousel.presentation.response.CarouselListResponse; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/carousels") | ||
public class CarouselControllerImpl implements CarouselController { | ||
private final CarouselFacade carouselFacade; | ||
|
||
@Override | ||
@GetMapping | ||
public ResponseEntity<CarouselListResponse> getCarousels() { | ||
CarouselListResponse response = carouselFacade.getCarousels(); | ||
return ResponseEntity.ok(response); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
...src/main/java/kgu/developers/api/carousel/presentation/response/CarouselListResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package kgu.developers.api.carousel.presentation.response; | ||
|
||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; | ||
|
||
import java.util.List; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import kgu.developers.domain.carousel.domain.Carousel; | ||
import lombok.Builder; | ||
|
||
@Builder | ||
public record CarouselListResponse( | ||
@Schema(description = "캐러셀 리스트", | ||
example = """ | ||
[{ | ||
"id": 1, | ||
"text": "경기대학교 AI컴퓨터공학부 메인 이미지", | ||
"link": "https://www.kgu.ac.kr/", | ||
"file": { | ||
"id": 1, | ||
"physicalPath": "/cloud/carousel/3/2025-curriculum" | ||
} | ||
}] | ||
""", | ||
requiredMode = REQUIRED) | ||
List<CarouselResponse> contents | ||
) { | ||
public static CarouselListResponse from(List<Carousel> carousels) { | ||
return CarouselListResponse.builder() | ||
.contents(carousels.stream() | ||
.map(CarouselResponse::from) | ||
.toList()) | ||
.build(); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
...api/src/main/java/kgu/developers/api/carousel/presentation/response/CarouselResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package kgu.developers.api.carousel.presentation.response; | ||
|
||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; | ||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import kgu.developers.domain.carousel.domain.Carousel; | ||
import kgu.developers.domain.file.application.response.FilePathResponse; | ||
import lombok.Builder; | ||
|
||
@Builder | ||
public record CarouselResponse( | ||
@Schema(description = "캐러셀 ID", example = "1", requiredMode = REQUIRED) | ||
Long id, | ||
|
||
@Schema(description = "캐러셀 설명", example = "경기대학교 AI컴퓨터공학부 메인 이미지", requiredMode = NOT_REQUIRED) | ||
String text, | ||
|
||
@Schema(description = "캐러셀 이미지 링크", example = "https://www.kgu.ac.kr/", requiredMode = NOT_REQUIRED) | ||
String link, | ||
|
||
@Schema(description = "첨부 파일 정보", | ||
example = "{\"id\": 1, " | ||
+ "\"physicalPath\": \"/files/2025-curriculum\"}", | ||
requiredMode = NOT_REQUIRED) | ||
FilePathResponse file | ||
) { | ||
public static CarouselResponse from(Carousel carousel) { | ||
return CarouselResponse.builder() | ||
.id(carousel.getId()) | ||
.text(carousel.getText()) | ||
.link(carousel.getLink()) | ||
.file(carousel.getFile() != null ? FilePathResponse.from(carousel.getFile()) : null) | ||
.build(); | ||
} | ||
} |
Oops, something went wrong.