From 72984059babd64a1e4b38f97d56cb82c528489d7 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 16:32:23 +0900 Subject: [PATCH 01/69] =?UTF-8?q?feat(domain):=20Category=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zangsu --- .../codezap/category/domain/Category.java | 20 +++++++++++++++++++ .../codezap/template/domain/Template.java | 5 +++++ 2 files changed, 25 insertions(+) create mode 100644 backend/src/main/java/codezap/category/domain/Category.java diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java new file mode 100644 index 000000000..5654900b6 --- /dev/null +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -0,0 +1,20 @@ +package codezap.category.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import codezap.global.auditing.BaseTimeEntity; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class Category extends BaseTimeEntity { + @Id + private Long id; + + @Column(nullable = false) + private String name; +} diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index 1178d79c1..a00b75b63 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -5,7 +5,9 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import codezap.category.domain.Category; import codezap.global.auditing.BaseTimeEntity; import lombok.Getter; import lombok.NoArgsConstructor; @@ -22,6 +24,9 @@ public class Template extends BaseTimeEntity { @Column(nullable = false) private String title; + @ManyToOne(optional = false) + private Category category; + public Template(String title) { this.title = title; } From 865d93dbfbdb30faef9ac1d01b39bf10ea2de0a4 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 16:41:16 +0900 Subject: [PATCH 02/69] =?UTF-8?q?feat(domain):=20Tag=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zangsu --- .../java/codezap/template/domain/Tag.java | 21 +++++++++ .../codezap/template/domain/TemplateTag.java | 43 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 backend/src/main/java/codezap/template/domain/Tag.java create mode 100644 backend/src/main/java/codezap/template/domain/TemplateTag.java diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java new file mode 100644 index 000000000..73106efe6 --- /dev/null +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -0,0 +1,21 @@ +package codezap.template.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import codezap.global.auditing.BaseTimeEntity; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class Tag extends BaseTimeEntity { + + @Id + private Long id; + + @Column(nullable = false) + private String value; +} diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java new file mode 100644 index 000000000..7fa96742c --- /dev/null +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -0,0 +1,43 @@ +package codezap.template.domain; + +import java.io.Serializable; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; + +import codezap.global.auditing.BaseTimeEntity; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Entity +@RequiredArgsConstructor +@Getter +public class TemplateTag extends BaseTimeEntity { + + @Embeddable + @RequiredArgsConstructor + @Getter + @EqualsAndHashCode + private static class TemplateTagId implements Serializable { + private Long templateId; + private Long tagId; + } + + @EmbeddedId + private TemplateTagId id; + + @ManyToOne + @MapsId("templateId") + @JoinColumn(name = "template_id") + private Template template; + + @ManyToOne + @MapsId("tagId") + @JoinColumn(name = "tag_id") + private Tag tag; +} \ No newline at end of file From de7d1ae848a084ca79a30e542ef5a72bd63c98a6 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 20:46:04 +0900 Subject: [PATCH 03/69] =?UTF-8?q?refactor(domain):=20Tag=EC=99=80=20Catego?= =?UTF-8?q?ry=EC=97=90=20=EA=B8=B0=EB=B3=B8=20=ED=82=A4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zangsu --- backend/src/main/java/codezap/category/domain/Category.java | 3 +++ backend/src/main/java/codezap/template/domain/Tag.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 5654900b6..7d96246ed 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -2,6 +2,8 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import codezap.global.auditing.BaseTimeEntity; @@ -13,6 +15,7 @@ @NoArgsConstructor public class Category extends BaseTimeEntity { @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java index 73106efe6..7859a3f61 100644 --- a/backend/src/main/java/codezap/template/domain/Tag.java +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -2,6 +2,8 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import codezap.global.auditing.BaseTimeEntity; @@ -14,6 +16,7 @@ public class Tag extends BaseTimeEntity { @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) From b73064b6dc88ca3c32c084cff2f6cd1f0391256c Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 20:50:51 +0900 Subject: [PATCH 04/69] =?UTF-8?q?style(service):=20test=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B0=9C=ED=96=89=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zangsu --- .../java/codezap/template/service/TemplateServiceTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index 0bde5fd7a..fea66f4c4 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -34,15 +34,18 @@ @Sql(value = "/clear.sql", executionPhase = ExecutionPhase.AFTER_TEST_CLASS) class TemplateServiceTest { + @LocalServerPort + int port; + @Autowired private TemplateService templateService; - @LocalServerPort - int port; @Autowired private TemplateRepository templateRepository; + @Autowired private SnippetRepository snippetRepository; + @Autowired private ThumbnailSnippetRepository thumbnailSnippetRepository; From 988fff6734e8d2d3bdf0c0fc41e2be2705ecc3f9 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 20:54:28 +0900 Subject: [PATCH 05/69] =?UTF-8?q?feat(category):=20category=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 29 +++++++++++++ .../codezap/category/domain/Category.java | 5 +++ .../dto/request/CreateCategoryRequest.java | 6 +++ .../repository/CategoryRepository.java | 8 ++++ .../category/service/CategoryService.java | 24 +++++++++++ .../controller/CategoryControllerTest.java | 42 ++++++++++++++++++ .../category/service/CategoryServiceTest.java | 43 +++++++++++++++++++ backend/src/test/resources/clear.sql | 34 +++++++++++++++ 8 files changed, 191 insertions(+) create mode 100644 backend/src/main/java/codezap/category/controller/CategoryController.java create mode 100644 backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java create mode 100644 backend/src/main/java/codezap/category/repository/CategoryRepository.java create mode 100644 backend/src/main/java/codezap/category/service/CategoryService.java create mode 100644 backend/src/test/java/codezap/category/controller/CategoryControllerTest.java create mode 100644 backend/src/test/java/codezap/category/service/CategoryServiceTest.java diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java new file mode 100644 index 000000000..982706b6e --- /dev/null +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -0,0 +1,29 @@ +package codezap.category.controller; + +import java.net.URI; + +import org.springframework.http.ResponseEntity; +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.RestController; + +import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.service.CategoryService; + +@RestController +@RequestMapping("/categories") +public class CategoryController { + + private final CategoryService categoryService; + + public CategoryController(CategoryService categoryService) { + this.categoryService = categoryService; + } + + @PostMapping + public ResponseEntity createCategory(@RequestBody CreateCategoryRequest createCategoryRequest) { + return ResponseEntity.created(URI.create("/categories/" + categoryService.create(createCategoryRequest))) + .build(); + } +} diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 7d96246ed..0c4aa47da 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -20,4 +20,9 @@ public class Category extends BaseTimeEntity { @Column(nullable = false) private String name; + + public Category(String name) { + this.id = null; + this.name = name; + } } diff --git a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java new file mode 100644 index 000000000..892495166 --- /dev/null +++ b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java @@ -0,0 +1,6 @@ +package codezap.category.dto.request; + +public record CreateCategoryRequest( + String name +) { +} diff --git a/backend/src/main/java/codezap/category/repository/CategoryRepository.java b/backend/src/main/java/codezap/category/repository/CategoryRepository.java new file mode 100644 index 000000000..8b3644a89 --- /dev/null +++ b/backend/src/main/java/codezap/category/repository/CategoryRepository.java @@ -0,0 +1,8 @@ +package codezap.category.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import codezap.category.domain.Category; + +public interface CategoryRepository extends JpaRepository { +} diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java new file mode 100644 index 000000000..7dcb46b59 --- /dev/null +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -0,0 +1,24 @@ +package codezap.category.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import codezap.category.domain.Category; +import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.repository.CategoryRepository; + +@Service +public class CategoryService { + + private final CategoryRepository categoryRepository; + + public CategoryService(CategoryRepository categoryRepository) { + this.categoryRepository = categoryRepository; + } + + @Transactional + public Long create(CreateCategoryRequest createCategoryRequest) { + Category category = new Category(createCategoryRequest.name()); + return categoryRepository.save(category).getId(); + } +} diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java new file mode 100644 index 000000000..00d7cee6f --- /dev/null +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -0,0 +1,42 @@ +package codezap.category.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.Sql.ExecutionPhase; + +import codezap.category.dto.request.CreateCategoryRequest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@Sql(value = "/clear.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) +@Sql(value = "/clear.sql", executionPhase = ExecutionPhase.AFTER_TEST_CLASS) +class CategoryControllerTest { + + @LocalServerPort + int port; + + @BeforeEach + void setting() { + RestAssured.port = port; + } + + @Test + @DisplayName("카테고리 생성 성공") + void createCategorySuccess() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category"); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(createCategoryRequest) + .when().post("/categories") + .then().log().all() + .header("Location", "/categories/1") + .statusCode(201); + } +} \ No newline at end of file diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java new file mode 100644 index 000000000..1c98c92b7 --- /dev/null +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -0,0 +1,43 @@ +package codezap.category.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.Sql.ExecutionPhase; + +import codezap.category.dto.request.CreateCategoryRequest; +import io.restassured.RestAssured; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@Sql(value = "/clear.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) +@Sql(value = "/clear.sql", executionPhase = ExecutionPhase.AFTER_TEST_CLASS) +class CategoryServiceTest { + + @LocalServerPort + int port; + + @Autowired + private CategoryService categoryService; + + @BeforeEach + void setting() { + RestAssured.port = port; + } + + @Test + @DisplayName("카테고리 생성 성공") + void createCategorySuccess() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category1"); + + Long categoryId = categoryService.create(createCategoryRequest); + + assertThat(categoryId).isEqualTo(1L); + } +} \ No newline at end of file diff --git a/backend/src/test/resources/clear.sql b/backend/src/test/resources/clear.sql index 845a987b2..8cb83de09 100644 --- a/backend/src/test/resources/clear.sql +++ b/backend/src/test/resources/clear.sql @@ -1,16 +1,50 @@ DROP TABLE IF EXISTS thumbnail_snippet; DROP TABLE IF EXISTS snippet; +DROP TABLE IF EXISTS template_tag; +DROP TABLE IF EXISTS tag; DROP TABLE IF EXISTS template; +DROP TABLE IF EXISTS category; + +CREATE TABLE category +( + id BIGINT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + created_at DATETIME(6) NOT NULL, + modified_at DATETIME(6) NOT NULL, + PRIMARY KEY (id) +); CREATE TABLE template ( id BIGINT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, + category_id BIGINT NOT NULL, + created_at DATETIME(6) NOT NULL, + modified_at DATETIME(6) NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (category_id) REFERENCES category (id) +); + +create table tag +( + id BIGINT NOT NULL auto_increment, + value VARCHAR(255) NOT NULL, created_at DATETIME(6) NOT NULL, modified_at DATETIME(6) NOT NULL, PRIMARY KEY (id) ); +create table template_tag +( + template_id BIGINT NOT NULL, + tag_id BIGINT NOT NULL, + created_at DATETIME(6) NOT NULL, + modified_at DATETIME(6) NOT NULL, + PRIMARY KEY (template_id, tag_id), + FOREIGN KEY (template_id) REFERENCES template (id), + FOREIGN KEY (tag_id) REFERENCES tag (id) +); + CREATE TABLE snippet ( id BIGINT NOT NULL AUTO_INCREMENT, From 6b99ee7828411c0533daa2b7f3af9149983ff8ae Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 21:03:23 +0900 Subject: [PATCH 06/69] =?UTF-8?q?feat(category):=20category=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 4 +- .../dto/request/CreateCategoryRequest.java | 5 ++ .../controller/CategoryControllerTest.java | 47 ++++++++++++++----- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 982706b6e..6f546ef1e 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -2,6 +2,8 @@ import java.net.URI; +import jakarta.validation.Valid; + import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -22,7 +24,7 @@ public CategoryController(CategoryService categoryService) { } @PostMapping - public ResponseEntity createCategory(@RequestBody CreateCategoryRequest createCategoryRequest) { + public ResponseEntity createCategory(@Valid @RequestBody CreateCategoryRequest createCategoryRequest) { return ResponseEntity.created(URI.create("/categories/" + categoryService.create(createCategoryRequest))) .build(); } diff --git a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java index 892495166..7d1276e94 100644 --- a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java @@ -1,6 +1,11 @@ package codezap.category.dto.request; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + public record CreateCategoryRequest( + @NotNull(message = "카테고리 이름이 null 입니다.") + @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") String name ) { } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index 00d7cee6f..edfbf41a2 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -1,7 +1,10 @@ package codezap.category.controller; +import static org.hamcrest.Matchers.is; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -18,6 +21,8 @@ @Sql(value = "/clear.sql", executionPhase = ExecutionPhase.AFTER_TEST_CLASS) class CategoryControllerTest { + private static final int MAX_LENGTH = 255; + @LocalServerPort int port; @@ -26,17 +31,35 @@ void setting() { RestAssured.port = port; } - @Test - @DisplayName("카테고리 생성 성공") - void createCategorySuccess() { - CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category"); - - RestAssured.given().log().all() - .contentType(ContentType.JSON) - .body(createCategoryRequest) - .when().post("/categories") - .then().log().all() - .header("Location", "/categories/1") - .statusCode(201); + @Nested + @DisplayName("카테고리 생성 테스트") + class createCategoryTest { + @Test + @DisplayName("카테고리 생성 성공") + void createCategorySuccess() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("a".repeat(MAX_LENGTH)); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(createCategoryRequest) + .when().post("/categories") + .then().log().all() + .header("Location", "/categories/1") + .statusCode(201); + } + + @Test + @DisplayName("카테고리 생성 실패:") + void createCategoryFail() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("a".repeat(MAX_LENGTH + 1)); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(createCategoryRequest) + .when().post("/categories") + .then().log().all() + .statusCode(400) + .body("detail", is("카테고리 이름은 최대 255자까지 입력 가능합니다.")); + } } } \ No newline at end of file From 292555f0d35c7e5a4bd9356afcf2a17b3f9e15b6 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 21:29:18 +0900 Subject: [PATCH 07/69] =?UTF-8?q?feat(category):=20category=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=A4=91=EB=B3=B5=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/category/domain/Category.java | 2 +- .../repository/CategoryRepository.java | 1 + .../category/service/CategoryService.java | 8 ++++- .../category/service/CategoryServiceTest.java | 35 +++++++++++++++---- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 0c4aa47da..59c7ef843 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -18,7 +18,7 @@ public class Category extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(nullable = false) + @Column(nullable = false, unique = true) private String name; public Category(String name) { diff --git a/backend/src/main/java/codezap/category/repository/CategoryRepository.java b/backend/src/main/java/codezap/category/repository/CategoryRepository.java index 8b3644a89..fe335048a 100644 --- a/backend/src/main/java/codezap/category/repository/CategoryRepository.java +++ b/backend/src/main/java/codezap/category/repository/CategoryRepository.java @@ -5,4 +5,5 @@ import codezap.category.domain.Category; public interface CategoryRepository extends JpaRepository { + boolean existsByName(String name); } diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 7dcb46b59..b1e52b55e 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -1,11 +1,13 @@ package codezap.category.service; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; import codezap.category.repository.CategoryRepository; +import codezap.global.exception.CodeZapException; @Service public class CategoryService { @@ -18,7 +20,11 @@ public CategoryService(CategoryRepository categoryRepository) { @Transactional public Long create(CreateCategoryRequest createCategoryRequest) { - Category category = new Category(createCategoryRequest.name()); + String categoryName = createCategoryRequest.name(); + if(categoryRepository.existsByName(categoryName)) { + throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); + } + Category category = new Category(categoryName); return categoryRepository.save(category).getId(); } } diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 1c98c92b7..167ab9b12 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -1,9 +1,11 @@ package codezap.category.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -12,7 +14,10 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.repository.CategoryRepository; +import codezap.global.exception.CodeZapException; import io.restassured.RestAssured; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @@ -26,18 +31,36 @@ class CategoryServiceTest { @Autowired private CategoryService categoryService; + @Autowired + private CategoryRepository categoryRepository; + @BeforeEach void setting() { RestAssured.port = port; } - @Test - @DisplayName("카테고리 생성 성공") - void createCategorySuccess() { - CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category1"); + @Nested + @DisplayName("카테고리 생성 테스트") + class createCategoryTest { + @Test + @DisplayName("카테고리 생성 성공") + void createCategorySuccess() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category1"); + + Long categoryId = categoryService.create(createCategoryRequest); + + assertThat(categoryId).isEqualTo(1L); + } - Long categoryId = categoryService.create(createCategoryRequest); + @Test + @DisplayName("카테고리 생성 실패: 중복된 이름의 카테고리 이름 생성") + void createCategoryFailWithDuplicateName() { + categoryRepository.save(new Category("category")); + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category"); - assertThat(categoryId).isEqualTo(1L); + assertThatThrownBy(() -> categoryService.create(createCategoryRequest)) + .isInstanceOf(CodeZapException.class) + .hasMessage("이름이 " + createCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); + } } } \ No newline at end of file From f23f7ae051079687e44277d1b2a3a93b1e3e62cd Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 21:49:42 +0900 Subject: [PATCH 08/69] =?UTF-8?q?feat(category):=20category=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 7 +++++++ .../response/FindAllCategoriesResponse.java | 17 +++++++++++++++ .../response/FindCategoryByIdResponse.java | 12 +++++++++++ .../category/service/CategoryService.java | 5 +++++ .../controller/CategoryControllerTest.java | 21 +++++++++++++++++++ .../category/service/CategoryServiceTest.java | 12 +++++++++++ 6 files changed, 74 insertions(+) create mode 100644 backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java create mode 100644 backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 6f546ef1e..27e50a673 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -5,12 +5,14 @@ import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; 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.RestController; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.service.CategoryService; @RestController @@ -28,4 +30,9 @@ public ResponseEntity createCategory(@Valid @RequestBody CreateCategoryReq return ResponseEntity.created(URI.create("/categories/" + categoryService.create(createCategoryRequest))) .build(); } + + @GetMapping + public ResponseEntity getCategories() { + return ResponseEntity.ok(categoryService.findAll()); + } } diff --git a/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java new file mode 100644 index 000000000..e7df7c5a6 --- /dev/null +++ b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java @@ -0,0 +1,17 @@ +package codezap.category.dto.response; + +import java.util.List; + +import codezap.category.domain.Category; + +public record FindAllCategoriesResponse( + List categories +) { + public static FindAllCategoriesResponse from(List categories) { + return new FindAllCategoriesResponse( + categories.stream() + .map(FindCategoryByIdResponse::from) + .toList() + ); + } +} diff --git a/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java b/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java new file mode 100644 index 000000000..fe7909aec --- /dev/null +++ b/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java @@ -0,0 +1,12 @@ +package codezap.category.dto.response; + +import codezap.category.domain.Category; + +public record FindCategoryByIdResponse( + Long id, + String name +) { + public static FindCategoryByIdResponse from(Category category) { + return new FindCategoryByIdResponse(category.getId(), category.getName()); + } +} diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index b1e52b55e..02a83af88 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -6,6 +6,7 @@ import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; @@ -27,4 +28,8 @@ public Long create(CreateCategoryRequest createCategoryRequest) { Category category = new Category(categoryName); return categoryRepository.save(category).getId(); } + + public FindAllCategoriesResponse findAll() { + return FindAllCategoriesResponse.from(categoryRepository.findAll()); + } } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index edfbf41a2..16f519f83 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; @@ -13,6 +14,7 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.service.CategoryService; import io.restassured.RestAssured; import io.restassured.http.ContentType; @@ -26,6 +28,9 @@ class CategoryControllerTest { @LocalServerPort int port; + @Autowired + private CategoryService categoryService; + @BeforeEach void setting() { RestAssured.port = port; @@ -62,4 +67,20 @@ void createCategoryFail() { .body("detail", is("카테고리 이름은 최대 255자까지 입력 가능합니다.")); } } + + @Test + @DisplayName("카테고리 전체 조회 성공") + void findAllCategoriesSuccess() { + CreateCategoryRequest createCategoryRequest1 = new CreateCategoryRequest("category1"); + CreateCategoryRequest createCategoryRequest2 = new CreateCategoryRequest("category2"); + categoryService.create(createCategoryRequest1); + categoryService.create(createCategoryRequest2); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .when().get("/categories") + .then().log().all() + .statusCode(200) + .body("categories.size()", is(2)); + } } \ No newline at end of file diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 167ab9b12..944a184af 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -16,6 +16,7 @@ import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; import io.restassured.RestAssured; @@ -63,4 +64,15 @@ void createCategoryFailWithDuplicateName() { .hasMessage("이름이 " + createCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); } } + + @Test + @DisplayName("카테고리 전체 조회 테스트") + void findAllCategoriesSuccess() { + categoryRepository.save(new Category("category1")); + categoryRepository.save(new Category("category2")); + + FindAllCategoriesResponse findAllCategoriesResponse = categoryService.findAll(); + + assertThat(findAllCategoriesResponse.categories()).hasSize(2); + } } \ No newline at end of file From 62de1ab1065d824e514bea7bb98caf1f5bd73063 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 21:51:45 +0900 Subject: [PATCH 09/69] =?UTF-8?q?refactor(domain):=20lombok=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=88=9C=EC=84=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84=20=EC=88=9C=EC=84=9C?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/codezap/category/domain/Category.java | 2 +- backend/src/main/java/codezap/template/domain/Snippet.java | 2 +- backend/src/main/java/codezap/template/domain/Tag.java | 2 +- backend/src/main/java/codezap/template/domain/Template.java | 2 +- backend/src/main/java/codezap/template/domain/TemplateTag.java | 3 ++- .../main/java/codezap/template/domain/ThumbnailSnippet.java | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 59c7ef843..f28666022 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -11,8 +11,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class Category extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/backend/src/main/java/codezap/template/domain/Snippet.java b/backend/src/main/java/codezap/template/domain/Snippet.java index 8cf0291a5..c85b0ee36 100644 --- a/backend/src/main/java/codezap/template/domain/Snippet.java +++ b/backend/src/main/java/codezap/template/domain/Snippet.java @@ -16,8 +16,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class Snippet extends BaseTimeEntity { private static final String CODE_LINE_BREAK = "\n"; diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java index 7859a3f61..0c2208952 100644 --- a/backend/src/main/java/codezap/template/domain/Tag.java +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -11,8 +11,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class Tag extends BaseTimeEntity { @Id diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index a00b75b63..29e518068 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -13,8 +13,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class Template extends BaseTimeEntity { @Id diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java index 7fa96742c..72729dbb4 100644 --- a/backend/src/main/java/codezap/template/domain/TemplateTag.java +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -12,10 +12,11 @@ import codezap.global.auditing.BaseTimeEntity; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; @Entity -@RequiredArgsConstructor +@NoArgsConstructor @Getter public class TemplateTag extends BaseTimeEntity { diff --git a/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java b/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java index 8c6b5feea..920dc18d2 100644 --- a/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java +++ b/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java @@ -11,8 +11,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class ThumbnailSnippet extends BaseTimeEntity { @Id From aae4cae3cdf50aafb8607b8aa6eedadb962c7356 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Wed, 31 Jul 2024 13:26:34 +0900 Subject: [PATCH 10/69] =?UTF-8?q?refactor(domain):=20Tag=20value=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=EB=A5=BC=20name=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/codezap/template/domain/Tag.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java index 0c2208952..8098b5d29 100644 --- a/backend/src/main/java/codezap/template/domain/Tag.java +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -20,5 +20,5 @@ public class Tag extends BaseTimeEntity { private Long id; @Column(nullable = false) - private String value; + private String name; } From 00070a2b6430e84c5880fb80f59549dd9ae002c7 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Wed, 31 Jul 2024 15:32:14 +0900 Subject: [PATCH 11/69] =?UTF-8?q?feat(codezap):=20template=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EA=B8=B0=EB=8A=A5=EC=97=90=20tag=EC=99=80=20catego?= =?UTF-8?q?ry=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/category/domain/Category.java | 1 - .../repository/CategoryRepository.java | 8 ++++++ .../java/codezap/template/domain/Tag.java | 4 +++ .../codezap/template/domain/Template.java | 6 +++- .../codezap/template/domain/TemplateTag.java | 11 +++++++- .../dto/request/CreateTemplateRequest.java | 6 +++- .../template/repository/TagRepository.java | 8 ++++++ .../repository/TemplateTagRepository.java | 8 ++++++ .../template/service/TemplateService.java | 28 +++++++++++++++++-- 9 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/java/codezap/template/repository/TagRepository.java create mode 100644 backend/src/main/java/codezap/template/repository/TemplateTagRepository.java diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index f28666022..5ffb71ed2 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -22,7 +22,6 @@ public class Category extends BaseTimeEntity { private String name; public Category(String name) { - this.id = null; this.name = name; } } diff --git a/backend/src/main/java/codezap/category/repository/CategoryRepository.java b/backend/src/main/java/codezap/category/repository/CategoryRepository.java index fe335048a..0f710a2c4 100644 --- a/backend/src/main/java/codezap/category/repository/CategoryRepository.java +++ b/backend/src/main/java/codezap/category/repository/CategoryRepository.java @@ -1,9 +1,17 @@ package codezap.category.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.http.HttpStatus; import codezap.category.domain.Category; +import codezap.global.exception.CodeZapException; public interface CategoryRepository extends JpaRepository { + + default Category fetchById(Long id) { + return findById(id).orElseThrow( + () -> new CodeZapException(HttpStatus.NOT_FOUND, "식별자 " + id + "에 해당하는 카테고리가 존재하지 않습니다.")); + } + boolean existsByName(String name); } diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java index 8098b5d29..eb7bf6fd7 100644 --- a/backend/src/main/java/codezap/template/domain/Tag.java +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -21,4 +21,8 @@ public class Tag extends BaseTimeEntity { @Column(nullable = false) private String name; + + public Tag(String name) { + this.name = name; + } } diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index 29e518068..bd55d4103 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -24,11 +24,15 @@ public class Template extends BaseTimeEntity { @Column(nullable = false) private String title; + @Column(columnDefinition = "TEXT") + private String description; + @ManyToOne(optional = false) private Category category; - public Template(String title) { + public Template(String title, Category category) { this.title = title; + this.category = category; } public void updateTitle(String title) { diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java index 72729dbb4..0d46698ee 100644 --- a/backend/src/main/java/codezap/template/domain/TemplateTag.java +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -10,10 +10,12 @@ import jakarta.persistence.MapsId; import codezap.global.auditing.BaseTimeEntity; +import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; +import lombok.Setter; @Entity @NoArgsConstructor @@ -21,7 +23,8 @@ public class TemplateTag extends BaseTimeEntity { @Embeddable - @RequiredArgsConstructor + @NoArgsConstructor + @AllArgsConstructor @Getter @EqualsAndHashCode private static class TemplateTagId implements Serializable { @@ -41,4 +44,10 @@ private static class TemplateTagId implements Serializable { @MapsId("tagId") @JoinColumn(name = "tag_id") private Tag tag; + + public TemplateTag(Template template, Tag tag) { + this.id = new TemplateTagId(template.getId(), tag.getId()); + this.template = template; + this.tag = tag; + } } \ No newline at end of file diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index 9663ba500..9527ff657 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -18,7 +18,11 @@ public record CreateTemplateRequest( @Schema(description = "템플릿의 스니펫 내역") @NotNull(message = "스니펫 리스트가 null 입니다.") @Valid - List snippets + List snippets, + + Long categoryId, + + List tags ) implements ValidatedSnippetsOrdinalRequest { @Override public List extractSnippetsOrdinal() { diff --git a/backend/src/main/java/codezap/template/repository/TagRepository.java b/backend/src/main/java/codezap/template/repository/TagRepository.java new file mode 100644 index 000000000..e813662e8 --- /dev/null +++ b/backend/src/main/java/codezap/template/repository/TagRepository.java @@ -0,0 +1,8 @@ +package codezap.template.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import codezap.template.domain.Tag; + +public interface TagRepository extends JpaRepository { +} diff --git a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java new file mode 100644 index 000000000..5396f030c --- /dev/null +++ b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java @@ -0,0 +1,8 @@ +package codezap.template.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import codezap.template.domain.TemplateTag; + +public interface TemplateTagRepository extends JpaRepository { +} diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 57be444c7..8adb4f6aa 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -6,8 +6,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import codezap.category.domain.Category; +import codezap.category.repository.CategoryRepository; import codezap.template.domain.Snippet; +import codezap.template.domain.Tag; import codezap.template.domain.Template; +import codezap.template.domain.TemplateTag; import codezap.template.domain.ThumbnailSnippet; import codezap.template.dto.request.CreateSnippetRequest; import codezap.template.dto.request.CreateTemplateRequest; @@ -16,7 +20,9 @@ import codezap.template.dto.response.FindAllTemplatesResponse; import codezap.template.dto.response.FindTemplateByIdResponse; import codezap.template.repository.SnippetRepository; +import codezap.template.repository.TagRepository; import codezap.template.repository.TemplateRepository; +import codezap.template.repository.TemplateTagRepository; import codezap.template.repository.ThumbnailSnippetRepository; @Service @@ -27,19 +33,35 @@ public class TemplateService { private final ThumbnailSnippetRepository thumbnailSnippetRepository; private final TemplateRepository templateRepository; private final SnippetRepository snippetRepository; + private final CategoryRepository categoryRepository; + private final TagRepository tagRepository; + private final TemplateTagRepository templateTagRepository; public TemplateService(ThumbnailSnippetRepository thumbnailSnippetRepository, - TemplateRepository templateRepository, SnippetRepository snippetRepository + TemplateRepository templateRepository, SnippetRepository snippetRepository, + CategoryRepository categoryRepository, TagRepository tagRepository, + TemplateTagRepository templateTagRepository ) { this.thumbnailSnippetRepository = thumbnailSnippetRepository; this.templateRepository = templateRepository; this.snippetRepository = snippetRepository; + this.categoryRepository = categoryRepository; + this.tagRepository = tagRepository; + this.templateTagRepository = templateTagRepository; } @Transactional public Long create(CreateTemplateRequest createTemplateRequest) { - Template template = templateRepository.save( - new Template(createTemplateRequest.title())); + Category category = categoryRepository.fetchById(createTemplateRequest.categoryId()); + + Template template = templateRepository.save(new Template(createTemplateRequest.title(), category)); + + List tags = createTemplateRequest.tags().stream() + .map(Tag::new) + .map(tagRepository::save) + .toList(); + + tags.forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); createTemplateRequest.snippets() .forEach(createSnippetRequest -> createSnippet(createSnippetRequest, template)); From 7a3ed8f025972a7b647246146ea0007c8c944c37 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Wed, 31 Jul 2024 16:04:00 +0900 Subject: [PATCH 12/69] =?UTF-8?q?refactor(test):=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TemplateControllerTest.java | 48 +++++++++++++++---- .../repository/SnippetRepositoryTest.java | 10 +++- .../template/service/TemplateServiceTest.java | 12 ++++- backend/src/test/resources/clear.sql | 3 +- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index 12833e4ef..b8659010f 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -17,6 +17,8 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.service.CategoryService; import codezap.template.dto.request.CreateSnippetRequest; import codezap.template.dto.request.CreateTemplateRequest; import codezap.template.dto.request.UpdateSnippetRequest; @@ -35,6 +37,9 @@ class TemplateControllerTest { @Autowired private TemplateService templateService; + @Autowired + private CategoryService categoryService; + @LocalServerPort int port; @@ -52,9 +57,13 @@ class createTemplateTest { @CsvSource({"a, 65535", "ㄱ, 21845"}) void createTemplateSuccess(String repeatTarget, int maxLength) { String maxTitle = "a".repeat(MAX_LENGTH); + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest(maxTitle, - List.of(new CreateSnippetRequest("a".repeat(MAX_LENGTH), repeatTarget.repeat(maxLength), 1))); - + List.of(new CreateSnippetRequest("a".repeat(MAX_LENGTH), repeatTarget.repeat(maxLength), 1)), + 1L, + List.of("tag1", "tag2") + ); + RestAssured.given().log().all() .contentType(ContentType.JSON) .body(templateRequest) @@ -68,9 +77,13 @@ void createTemplateSuccess(String repeatTarget, int maxLength) { @DisplayName("템플릿 생성 실패: 템플릿 이름 길이 초과") void createTemplateFailWithLongTitle() { String exceededTitle = "a".repeat(MAX_LENGTH + 1); + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest(exceededTitle, - List.of(new CreateSnippetRequest("a", "content", 1))); - + List.of(new CreateSnippetRequest("a", "content", 1)), + 1L, + List.of("tag1", "tag2") + ); + RestAssured.given().log().all() .contentType(ContentType.JSON) .body(templateRequest) @@ -84,8 +97,12 @@ void createTemplateFailWithLongTitle() { @DisplayName("템플릿 생성 실패: 파일 이름 길이 초과") void createTemplateFailWithLongFileName() { String exceededTitle = "a".repeat(MAX_LENGTH + 1); + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest("title", - List.of(new CreateSnippetRequest(exceededTitle, "content", 1))); + List.of(new CreateSnippetRequest(exceededTitle, "content", 1)), + 1L, + List.of("tag1", "tag2") + ); RestAssured.given().log().all() .contentType(ContentType.JSON) @@ -100,8 +117,12 @@ void createTemplateFailWithLongFileName() { @DisplayName("템플릿 생성 실패: 파일 내용 길이 초과") @CsvSource({"a, 65536", "ㄱ, 21846"}) void createTemplateFailWithLongContent(String repeatTarget, int exceededLength) { + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest("title", - List.of(new CreateSnippetRequest("title", repeatTarget.repeat(exceededLength), 1))); + List.of(new CreateSnippetRequest("title", repeatTarget.repeat(exceededLength), 1)), + 1L, + List.of("tag1", "tag2") + ); RestAssured.given().log().all() .contentType(ContentType.JSON) @@ -116,9 +137,13 @@ void createTemplateFailWithLongContent(String repeatTarget, int exceededLength) @DisplayName("템플릿 생성 실패: 잘못된 스니펫 순서 입력") @CsvSource({"0, 1", "1, 3", "2, 1"}) void createTemplateFailWithWrongSnippetOrdinal(int firstIndex, int secondIndex) { + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest("title", List.of(new CreateSnippetRequest("title", "content", firstIndex), - new CreateSnippetRequest("title", "content", secondIndex))); + new CreateSnippetRequest("title", "content", secondIndex)), + 1L, + List.of("tag1", "tag2") + ); RestAssured.given().log().all() .contentType(ContentType.JSON) @@ -136,6 +161,7 @@ void findAllTemplatesSuccess() { // given CreateTemplateRequest templateRequest1 = createTemplateRequestWithTwoSnippets("title1"); CreateTemplateRequest templateRequest2 = createTemplateRequestWithTwoSnippets("title2"); + categoryService.create(new CreateCategoryRequest("category")); templateService.create(templateRequest1); templateService.create(templateRequest2); @@ -155,6 +181,7 @@ class findTemplateTest { @DisplayName("템플릿 상세 조회 성공") void findOneTemplateSuccess() { // given + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -187,6 +214,7 @@ class updateTemplateTest { @DisplayName("템플릿 수정 성공") void updateTemplateSuccess() { // given + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -217,6 +245,7 @@ void updateTemplateSuccess() { @CsvSource({"1, 2, 1", "3, 2, 1", "0, 2, 1"}) void updateTemplateFailWithWrongSnippetOrdinal(int createOrdinal1, int createOrdinal2, int updateOrdinal) { // given + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -251,6 +280,7 @@ class deleteTemplateTest { @DisplayName("템플릿 삭제 성공") void deleteTemplateSuccess() { // given + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -279,7 +309,9 @@ private static CreateTemplateRequest createTemplateRequestWithTwoSnippets(String List.of( new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2) - ) + ), + 1L, + List.of("tag1", "tag2") ); return templateRequest; } diff --git a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java index 750ce4055..075ee12c7 100644 --- a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java +++ b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java @@ -13,6 +13,8 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase; import org.springframework.transaction.annotation.Transactional; +import codezap.category.domain.Category; +import codezap.category.repository.CategoryRepository; import codezap.template.domain.Snippet; import codezap.template.domain.Template; @@ -26,11 +28,14 @@ class SnippetRepositoryTest { private SnippetRepository snippetRepository; @Autowired private TemplateRepository templateRepository; + @Autowired + private CategoryRepository categoryRepository; @Test @DisplayName("단일 스니펫 찾기 성공: 템플릿과 순서") void findOneSnippetSuccessWithTemplateAndOrdinal() { - Template template = templateRepository.save(new Template("title")); + Category category = categoryRepository.save(new Category("category")); + Template template = templateRepository.save(new Template("title", category)); Snippet snippet1 = snippetRepository.save(new Snippet(template, "filename1", "content1", 1)); Snippet snippet2 = snippetRepository.save(new Snippet(template, "filename2", "content2", 2)); @@ -47,7 +52,8 @@ void findOneSnippetSuccessWithTemplateAndOrdinal() { @Test @DisplayName("스니펫 리스트 찾기 성공: 템플릿과 순서") void findSnippetsSuccessWithTemplateAndOrdinal() { - Template template = templateRepository.save(new Template("title")); + Category category = categoryRepository.save(new Category("category")); + Template template = templateRepository.save(new Template("title", category)); Snippet snippet1 = snippetRepository.save(new Snippet(template, "filename1", "content1", 1)); Snippet snippet2 = snippetRepository.save(new Snippet(template, "filename2", "content2", 2)); Snippet snippet3 = snippetRepository.save(new Snippet(template, "filename3", "content3", 2)); diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index fea66f4c4..27cb2591d 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -15,6 +15,8 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +import codezap.category.domain.Category; +import codezap.category.repository.CategoryRepository; import codezap.template.domain.Snippet; import codezap.template.domain.Template; import codezap.template.domain.ThumbnailSnippet; @@ -48,6 +50,8 @@ class TemplateServiceTest { @Autowired private ThumbnailSnippetRepository thumbnailSnippetRepository; + @Autowired + private CategoryRepository categoryRepository; @BeforeEach void setting() { @@ -59,6 +63,7 @@ void setting() { void createTemplateSuccess() { // given CreateTemplateRequest createTemplateRequest = makeTemplateRequest("title"); + categoryRepository.save(new Category("category")); // when templateService.create(createTemplateRequest); @@ -143,7 +148,9 @@ private CreateTemplateRequest makeTemplateRequest(String title) { List.of( new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2) - ) + ), + 1L, + List.of("tag1", "tag2") ); } @@ -162,7 +169,8 @@ private UpdateTemplateRequest makeUpdateTemplateRequest(String title) { } private Template saveTemplate(CreateTemplateRequest createTemplateRequest) { - Template savedTemplate = templateRepository.save(new Template(createTemplateRequest.title())); + Category category = categoryRepository.save(new Category("category")); + Template savedTemplate = templateRepository.save(new Template(createTemplateRequest.title(), category)); Snippet savedFirstSnippet = snippetRepository.save(new Snippet(savedTemplate, "filename1", "content1", 1)); snippetRepository.save(new Snippet(savedTemplate, "filename2", "content2", 2)); thumbnailSnippetRepository.save(new ThumbnailSnippet(savedTemplate, savedFirstSnippet)); diff --git a/backend/src/test/resources/clear.sql b/backend/src/test/resources/clear.sql index 8cb83de09..9aada0497 100644 --- a/backend/src/test/resources/clear.sql +++ b/backend/src/test/resources/clear.sql @@ -18,6 +18,7 @@ CREATE TABLE template ( id BIGINT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, + description TEXT, category_id BIGINT NOT NULL, created_at DATETIME(6) NOT NULL, modified_at DATETIME(6) NOT NULL, @@ -28,7 +29,7 @@ CREATE TABLE template create table tag ( id BIGINT NOT NULL auto_increment, - value VARCHAR(255) NOT NULL, + name VARCHAR(255) NOT NULL, created_at DATETIME(6) NOT NULL, modified_at DATETIME(6) NOT NULL, PRIMARY KEY (id) From 100d7ecee9f73d160f189eb149edf10a5ae3fbac Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Wed, 31 Jul 2024 16:32:18 +0900 Subject: [PATCH 13/69] =?UTF-8?q?feat(test):=20=ED=85=9C=ED=94=8C=EB=A6=BF?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=EC=97=90=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC,=20=ED=83=9C=EA=B7=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/service/CategoryService.java | 2 +- .../codezap/template/domain/TemplateTag.java | 2 -- .../dto/response/FindTagByIdResponse.java | 12 +++++++++++ .../response/FindTemplateByIdResponse.java | 21 ++++++++++++++++++- .../repository/TemplateTagRepository.java | 5 +++++ .../template/service/TemplateService.java | 5 ++++- .../controller/TemplateControllerTest.java | 5 ++++- .../template/service/TemplateServiceTest.java | 19 ++++++++++++++++- 8 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 02a83af88..e4c59e5de 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -22,7 +22,7 @@ public CategoryService(CategoryRepository categoryRepository) { @Transactional public Long create(CreateCategoryRequest createCategoryRequest) { String categoryName = createCategoryRequest.name(); - if(categoryRepository.existsByName(categoryName)) { + if (categoryRepository.existsByName(categoryName)) { throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); } Category category = new Category(categoryName); diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java index 0d46698ee..95214758d 100644 --- a/backend/src/main/java/codezap/template/domain/TemplateTag.java +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -14,8 +14,6 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; -import lombok.Setter; @Entity @NoArgsConstructor diff --git a/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java new file mode 100644 index 000000000..8052780b0 --- /dev/null +++ b/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java @@ -0,0 +1,12 @@ +package codezap.template.dto.response; + +import codezap.template.domain.Tag; + +public record FindTagByIdResponse( + Long id, + String name +) { + public static FindTagByIdResponse from(Tag tag) { + return new FindTagByIdResponse(tag.getId(), tag.getName()); + } +} diff --git a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java index 6fccceb7c..734741750 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java @@ -3,25 +3,36 @@ import java.time.LocalDateTime; import java.util.List; +import codezap.category.dto.response.FindCategoryByIdResponse; import codezap.template.domain.Snippet; +import codezap.template.domain.Tag; import codezap.template.domain.Template; import io.swagger.v3.oas.annotations.media.Schema; public record FindTemplateByIdResponse( @Schema(description = "템플릿 식별자", example = "0") Long id, + @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") String title, + @Schema(description = "스니펫 목록") List snippets, + + FindCategoryByIdResponse category, + + List tags, + @Schema(description = "템플릿 수정 시간", example = "2024-11-11 12:00", type = "string") LocalDateTime modifiedAt ) { - public static FindTemplateByIdResponse of(Template template, List snippets) { + public static FindTemplateByIdResponse of(Template template, List snippets, List tags) { return new FindTemplateByIdResponse( template.getId(), template.getTitle(), mapToFindAllSnippetByTemplateResponse(snippets), + FindCategoryByIdResponse.from(template.getCategory()), + mapToFindTagByTemplateResponse(tags), template.getModifiedAt() ); } @@ -33,4 +44,12 @@ private static List mapToFindAllSnippetByTempl .map(FindAllSnippetByTemplateResponse::from) .toList(); } + + private static List mapToFindTagByTemplateResponse( + List tags + ) { + return tags.stream() + .map(FindTagByIdResponse::from) + .toList(); + } } diff --git a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java index 5396f030c..b49d7355f 100644 --- a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java @@ -1,8 +1,13 @@ package codezap.template.repository; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; +import codezap.template.domain.Template; import codezap.template.domain.TemplateTag; public interface TemplateTagRepository extends JpaRepository { + + List findAllByTemplate(Template template); } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 8adb4f6aa..538ff606d 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -78,7 +78,10 @@ public FindAllTemplatesResponse findAll() { public FindTemplateByIdResponse findById(Long id) { Template template = templateRepository.fetchById(id); List snippets = snippetRepository.findAllByTemplate(template); - return FindTemplateByIdResponse.of(template, snippets); + List tags = templateTagRepository.findAllByTemplate(template).stream() + .map(TemplateTag::getTag) + .toList(); + return FindTemplateByIdResponse.of(template, snippets, tags); } @Transactional diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index b8659010f..47d11ab65 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -191,7 +191,10 @@ void findOneTemplateSuccess() { .then().log().all() .statusCode(200) .body("title", is(templateRequest.title()), - "snippets.size()", is(2)); + "snippets.size()", is(2), + "category.id", is(1), + "category.name", is("category"), + "tags.size()", is(2)); } @Test diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index 27cb2591d..10d97d771 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -18,7 +18,9 @@ import codezap.category.domain.Category; import codezap.category.repository.CategoryRepository; import codezap.template.domain.Snippet; +import codezap.template.domain.Tag; import codezap.template.domain.Template; +import codezap.template.domain.TemplateTag; import codezap.template.domain.ThumbnailSnippet; import codezap.template.dto.request.CreateSnippetRequest; import codezap.template.dto.request.CreateTemplateRequest; @@ -27,7 +29,9 @@ import codezap.template.dto.response.FindAllTemplatesResponse; import codezap.template.dto.response.FindTemplateByIdResponse; import codezap.template.repository.SnippetRepository; +import codezap.template.repository.TagRepository; import codezap.template.repository.TemplateRepository; +import codezap.template.repository.TemplateTagRepository; import codezap.template.repository.ThumbnailSnippetRepository; import io.restassured.RestAssured; @@ -50,9 +54,15 @@ class TemplateServiceTest { @Autowired private ThumbnailSnippetRepository thumbnailSnippetRepository; + @Autowired private CategoryRepository categoryRepository; + @Autowired + private TemplateTagRepository templateTagRepository; + @Autowired + private TagRepository tagRepository; + @BeforeEach void setting() { RestAssured.port = port; @@ -99,7 +109,10 @@ void findOneTemplateSuccess() { // then assertAll( () -> assertThat(foundTemplate.title()).isEqualTo(template.getTitle()), - () -> assertThat(foundTemplate.snippets()).hasSize(snippetRepository.findAllByTemplate(template).size()) + () -> assertThat(foundTemplate.snippets()).hasSize( + snippetRepository.findAllByTemplate(template).size()), + () -> assertThat(foundTemplate.category().id()).isEqualTo(template.getCategory().getId()), + () -> assertThat(foundTemplate.tags()).hasSize(2) ); } @@ -174,6 +187,10 @@ private Template saveTemplate(CreateTemplateRequest createTemplateRequest) { Snippet savedFirstSnippet = snippetRepository.save(new Snippet(savedTemplate, "filename1", "content1", 1)); snippetRepository.save(new Snippet(savedTemplate, "filename2", "content2", 2)); thumbnailSnippetRepository.save(new ThumbnailSnippet(savedTemplate, savedFirstSnippet)); + createTemplateRequest.tags().stream() + .map(Tag::new) + .map(tagRepository::save) + .forEach(tag -> templateTagRepository.save(new TemplateTag(savedTemplate, tag))); return savedTemplate; } From 22bbb482fd0f478a5ac40a6569a976485ca9c18d Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Thu, 1 Aug 2024 11:28:56 +0900 Subject: [PATCH 14/69] =?UTF-8?q?feat(template):=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=EC=97=90=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EB=B0=8F=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/template/domain/Template.java | 3 ++- .../codezap/template/domain/TemplateTag.java | 2 +- .../dto/request/UpdateTemplateRequest.java | 6 ++++- .../template/repository/TagRepository.java | 3 +++ .../repository/TemplateTagRepository.java | 2 ++ .../template/service/TemplateService.java | 14 ++++++++++- .../controller/TemplateControllerTest.java | 14 ++++++++--- .../template/service/TemplateServiceTest.java | 25 +++++++++++++++---- 8 files changed, 56 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index bd55d4103..5bdfa6446 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -35,7 +35,8 @@ public Template(String title, Category category) { this.category = category; } - public void updateTitle(String title) { + public void updateTitle(String title, Category category) { this.title = title; + this.category = category; } } diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java index 95214758d..eefb2ca0b 100644 --- a/backend/src/main/java/codezap/template/domain/TemplateTag.java +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -48,4 +48,4 @@ public TemplateTag(Template template, Tag tag) { this.template = template; this.tag = tag; } -} \ No newline at end of file +} diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java index 06e2cfcaf..16fd59c91 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java @@ -23,7 +23,11 @@ public record UpdateTemplateRequest( @Schema(description = "삭제한 스니펫 식별자") @NotNull(message = "deleteSnippetIds 리스트가 null 입니다.") - List deleteSnippetIds + List deleteSnippetIds, + + Long categoryId, + + List tags ) implements ValidatedSnippetsOrdinalRequest { @Override public List extractSnippetsOrdinal() { diff --git a/backend/src/main/java/codezap/template/repository/TagRepository.java b/backend/src/main/java/codezap/template/repository/TagRepository.java index e813662e8..612dee2b3 100644 --- a/backend/src/main/java/codezap/template/repository/TagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TagRepository.java @@ -5,4 +5,7 @@ import codezap.template.domain.Tag; public interface TagRepository extends JpaRepository { + boolean existsByName(String name); + + Tag findByName(String name); } diff --git a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java index b49d7355f..6b0ab4f6e 100644 --- a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java @@ -10,4 +10,6 @@ public interface TemplateTagRepository extends JpaRepository { List findAllByTemplate(Template template); + + void deleteAllByTemplate(Template template); } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 538ff606d..b610bed92 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -86,8 +86,9 @@ public FindTemplateByIdResponse findById(Long id) { @Transactional public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) { + Category category = categoryRepository.fetchById(updateTemplateRequest.categoryId()); Template template = templateRepository.fetchById(templateId); - template.updateTitle(updateTemplateRequest.title()); + template.updateTitle(updateTemplateRequest.title(), category); updateTemplateRequest.updateSnippets().forEach(this::updateSnippet); updateTemplateRequest.createSnippets() @@ -100,6 +101,17 @@ public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) } updateTemplateRequest.deleteSnippetIds().forEach(snippetRepository::deleteById); + + templateTagRepository.deleteAllByTemplate(template); + updateTemplateRequest.tags().stream() + .map(Tag::new) + .filter(tag -> !tagRepository.existsByName(tag.getName())) + .forEach(tagRepository::save); + + List tags = updateTemplateRequest.tags().stream() + .map(tagRepository::findByName) + .toList(); + tags.forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); } @Transactional diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index 47d11ab65..c45ac1c18 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -217,7 +217,8 @@ class updateTemplateTest { @DisplayName("템플릿 수정 성공") void updateTemplateSuccess() { // given - categoryService.create(new CreateCategoryRequest("category")); + categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category2")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -230,7 +231,9 @@ void updateTemplateSuccess() { List.of( new UpdateSnippetRequest(2L, "updateFilename2", "updateContent2", 1) ), - List.of(1L) + List.of(1L), + 2L, + List.of("tag1", "tag3") ); // when & then @@ -248,7 +251,8 @@ void updateTemplateSuccess() { @CsvSource({"1, 2, 1", "3, 2, 1", "0, 2, 1"}) void updateTemplateFailWithWrongSnippetOrdinal(int createOrdinal1, int createOrdinal2, int updateOrdinal) { // given - categoryService.create(new CreateCategoryRequest("category")); + categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category2")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -261,7 +265,9 @@ void updateTemplateFailWithWrongSnippetOrdinal(int createOrdinal1, int createOrd List.of( new UpdateSnippetRequest(2L, "updateFilename2", "updateContent2", updateOrdinal) ), - List.of(1L) + List.of(1L), + 2L, + List.of("tag1", "tag3") ); // when & then diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index 10d97d771..ba802e6b6 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -76,10 +76,15 @@ void createTemplateSuccess() { categoryRepository.save(new Category("category")); // when - templateService.create(createTemplateRequest); + Long id = templateService.create(createTemplateRequest); + Template template = templateRepository.fetchById(id); // then - assertThat(templateRepository.findAll()).hasSize(1); + assertAll( + () -> assertThat(templateRepository.findAll()).hasSize(1), + () -> assertThat(template.getTitle()).isEqualTo(createTemplateRequest.title()), + () -> assertThat(template.getCategory().getName()).isEqualTo("category") + ); } @Test @@ -122,18 +127,26 @@ void updateTemplateSuccess() { // given CreateTemplateRequest createdTemplate = makeTemplateRequest("title"); Template template = saveTemplate(createdTemplate); + categoryRepository.save(new Category("category2")); // when UpdateTemplateRequest updateTemplateRequest = makeUpdateTemplateRequest("updateTitle"); templateService.update(template.getId(), updateTemplateRequest); + Template updateTemplate = templateRepository.fetchById(template.getId()); List snippets = snippetRepository.findAllByTemplate(template); ThumbnailSnippet thumbnailSnippet = thumbnailSnippetRepository.findById(template.getId()).get(); + List tags = templateTagRepository.findAllByTemplate(updateTemplate).stream() + .map(TemplateTag::getTag) + .toList(); // then assertAll( - () -> assertThat(updateTemplateRequest.title()).isEqualTo("updateTitle"), + () -> assertThat(updateTemplate.getTitle()).isEqualTo("updateTitle"), () -> assertThat(thumbnailSnippet.getSnippet().getId()).isEqualTo(2L), - () -> assertThat(snippets).hasSize(3) + () -> assertThat(snippets).hasSize(3), + () -> assertThat(updateTemplate.getCategory().getId()).isEqualTo(2L), + () -> assertThat(tags).hasSize(2), + () -> assertThat(tags.get(1).getName()).isEqualTo("tag3") ); } @@ -177,7 +190,9 @@ private UpdateTemplateRequest makeUpdateTemplateRequest(String title) { List.of( new UpdateSnippetRequest(2L, "filename2", "content2", 1) ), - List.of(1L) + List.of(1L), + 2L, + List.of("tag1", "tag3") ); } From d8d40bc3cf77a7199d00eed5b5744861b027fc36 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Thu, 1 Aug 2024 11:35:46 +0900 Subject: [PATCH 15/69] =?UTF-8?q?feat(template):=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/codezap/template/repository/TemplateTagRepository.java | 2 ++ .../src/main/java/codezap/template/service/TemplateService.java | 1 + 2 files changed, 3 insertions(+) diff --git a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java index 6b0ab4f6e..41fe972d3 100644 --- a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java @@ -12,4 +12,6 @@ public interface TemplateTagRepository extends JpaRepository List findAllByTemplate(Template template); void deleteAllByTemplate(Template template); + + void deleteAllByTemplateId(Long id); } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index b610bed92..e6f185b08 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -118,6 +118,7 @@ public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) public void deleteById(Long id) { thumbnailSnippetRepository.deleteByTemplateId(id); snippetRepository.deleteByTemplateId(id); + templateTagRepository.deleteAllByTemplateId(id); templateRepository.deleteById(id); } From 457a8560f2e76c1dc04e4b8ee20dfca9446abbce Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 13:10:02 +0900 Subject: [PATCH 16/69] =?UTF-8?q?docs(category):=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20swagger=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 17 ++++++- .../SpringDocCategoryController.java | 49 +++++++++++++++++++ .../dto/request/CreateCategoryRequest.java | 3 ++ .../dto/request/UpdateCategoryRequest.java | 17 +++++++ .../response/FindAllCategoriesResponse.java | 2 + .../response/FindCategoryByIdResponse.java | 3 ++ 6 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java create mode 100644 backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 27e50a673..2acc801b4 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -5,19 +5,23 @@ import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.service.CategoryService; @RestController @RequestMapping("/categories") -public class CategoryController { +public class CategoryController implements SpringDocCategoryController{ private final CategoryService categoryService; @@ -35,4 +39,15 @@ public ResponseEntity createCategory(@Valid @RequestBody CreateCategoryReq public ResponseEntity getCategories() { return ResponseEntity.ok(categoryService.findAll()); } + + @PutMapping("/{id}") + public ResponseEntity updateCategory(@PathVariable Long id, + @Valid @RequestBody UpdateCategoryRequest updateCategoryRequest) { + return null; + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteCategory(@PathVariable Long id) { + return null; + } } diff --git a/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java new file mode 100644 index 000000000..621d23002 --- /dev/null +++ b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java @@ -0,0 +1,49 @@ +package codezap.category.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; +import codezap.category.dto.response.FindAllCategoriesResponse; +import codezap.global.swagger.error.ApiErrorResponse; +import codezap.global.swagger.error.ErrorCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.headers.Header; +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; + +@Tag(name = "카테고리 CRUD API", description = "카테고리 생성, 목록 조회, 삭제, 수정 API") +public interface SpringDocCategoryController { + + @Operation(summary = "카테고리 생성", description = """ + 새로운 카테고리를 생성합니다. \n + 새로운 카테고리의 이름이 필요합니다. \n + """) + @ApiResponse(responseCode = "201", description = "카테고리 생성 성공", headers = { + @Header(name = "생성된 카테고리의 API 경로", example = "/categories/1")}) + @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories", errorCases = { + @ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "카테고리 이름이 null 입니다."), + @ErrorCase(description = "카테고리 이름이 255자를 초과한 경우", exampleMessage = "카테고리 이름은 최대 255자까지 입력 가능합니다.") + }) + ResponseEntity createCategory(CreateCategoryRequest createCategoryRequest); + + @Operation(summary = "카테고리 목록 조회", description = "생성된 모든 카테고리를 조회합니다.") + @ApiResponse(responseCode = "200", description = "조회 성공", + content = {@Content(schema = @Schema(implementation = FindAllCategoriesResponse.class))}) + ResponseEntity getCategories(); + + @Operation(summary = "카테고리 수정", description = "해당하는 식별자의 카테고리를 수정합니다.") + @ApiResponse(responseCode = "200", description = "카테고리 수정 성공") + @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories/1", errorCases = { + @ErrorCase(description = "해당하는 id 값인 카테고리가 없는 경우", + exampleMessage = "식별자 1에 해당하는 카테고리가 존재하지 않습니다."), + }) + ResponseEntity updateCategory(Long id, UpdateCategoryRequest updateCategoryRequest); + + @Operation(summary = "카테고리 삭제", description = "해당하는 식별자의 카테고리를 삭제합니다.") + @ApiResponse(responseCode = "204", description = "카테고리 삭제 성공") + ResponseEntity deleteCategory(Long id); +} diff --git a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java index 7d1276e94..86946b8a8 100644 --- a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java @@ -3,7 +3,10 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import io.swagger.v3.oas.annotations.media.Schema; + public record CreateCategoryRequest( + @Schema(description = "카테고리 이름", example = "Spring") @NotNull(message = "카테고리 이름이 null 입니다.") @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") String name diff --git a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java new file mode 100644 index 000000000..5a8bfb467 --- /dev/null +++ b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java @@ -0,0 +1,17 @@ +package codezap.category.dto.request; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record UpdateCategoryRequest( + @Schema(description = "카테고리 식별자", example = "1") + @NotNull(message = "카테고리 id가 null 입니다.") + Long id, + @Schema(description = "카테고리 이름", example = "Spring") + @NotNull(message = "카테고리 이름이 null 입니다.") + @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") + String name +) { +} diff --git a/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java index e7df7c5a6..b17e2168f 100644 --- a/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java +++ b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java @@ -3,8 +3,10 @@ import java.util.List; import codezap.category.domain.Category; +import io.swagger.v3.oas.annotations.media.Schema; public record FindAllCategoriesResponse( + @Schema(description = "카테고리 목록") List categories ) { public static FindAllCategoriesResponse from(List categories) { diff --git a/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java b/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java index fe7909aec..999a7a46a 100644 --- a/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java +++ b/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java @@ -1,9 +1,12 @@ package codezap.category.dto.response; import codezap.category.domain.Category; +import io.swagger.v3.oas.annotations.media.Schema; public record FindCategoryByIdResponse( + @Schema(description = "카테고리 식별자", example = "1") Long id, + @Schema(description = "카테고리 이름", example = "Spring") String name ) { public static FindCategoryByIdResponse from(Category category) { From f11917b9a32998e73381f5a569f1b722889eeb5e Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 18:29:20 +0900 Subject: [PATCH 17/69] =?UTF-8?q?docs(category):=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 3 +- .../codezap/category/domain/Category.java | 4 ++ .../dto/request/UpdateCategoryRequest.java | 3 - .../category/service/CategoryService.java | 11 ++++ .../controller/CategoryControllerTest.java | 61 ++++++++++++++++++- .../category/service/CategoryServiceTest.java | 16 ++++- 6 files changed, 92 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 2acc801b4..311ed556f 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -43,7 +43,8 @@ public ResponseEntity getCategories() { @PutMapping("/{id}") public ResponseEntity updateCategory(@PathVariable Long id, @Valid @RequestBody UpdateCategoryRequest updateCategoryRequest) { - return null; + categoryService.update(id, updateCategoryRequest); + return ResponseEntity.ok().build(); } @DeleteMapping("/{id}") diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 5ffb71ed2..b44c07ddb 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -24,4 +24,8 @@ public class Category extends BaseTimeEntity { public Category(String name) { this.name = name; } + + public void updateName(String name) { + this.name = name; + } } diff --git a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java index 5a8bfb467..4ed48bf0c 100644 --- a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java @@ -6,9 +6,6 @@ import io.swagger.v3.oas.annotations.media.Schema; public record UpdateCategoryRequest( - @Schema(description = "카테고리 식별자", example = "1") - @NotNull(message = "카테고리 id가 null 입니다.") - Long id, @Schema(description = "카테고리 이름", example = "Spring") @NotNull(message = "카테고리 이름이 null 입니다.") @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index e4c59e5de..151a63945 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -6,6 +6,7 @@ import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; @@ -32,4 +33,14 @@ public Long create(CreateCategoryRequest createCategoryRequest) { public FindAllCategoriesResponse findAll() { return FindAllCategoriesResponse.from(categoryRepository.findAll()); } + + @Transactional + public void update(Long id, UpdateCategoryRequest updateCategoryRequest) { + if (categoryRepository.existsByName(updateCategoryRequest.name())) { + throw new CodeZapException(HttpStatus.CONFLICT, + "이름이 " + updateCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); + } + Category category = categoryRepository.fetchById(id); + category.updateName(updateCategoryRequest.name()); + } } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index 16f519f83..18f66462d 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -14,6 +14,7 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.service.CategoryService; import io.restassured.RestAssured; import io.restassured.http.ContentType; @@ -83,4 +84,62 @@ void findAllCategoriesSuccess() { .statusCode(200) .body("categories.size()", is(2)); } -} \ No newline at end of file + + @Nested + @DisplayName("카테고리 수정 테스트") + class updateCategoryTest { + + Long savedCategoryId; + + @BeforeEach + void saveCategory() { + savedCategoryId = categoryService.create(new CreateCategoryRequest("category1")); + } + + @Test + @DisplayName("카테고리 수정 성공") + void updateCategorySuccess() { + UpdateCategoryRequest updateCategoryRequest = new UpdateCategoryRequest("a".repeat(MAX_LENGTH)); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(updateCategoryRequest) + .when().put("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(200); + } + + @Test + @DisplayName("카테고리 수정 실패: 카테고리 이름 길이 초과") + void updateCategoryFailWithLongName() { + UpdateCategoryRequest updateCategoryRequest = new UpdateCategoryRequest("a".repeat(MAX_LENGTH + 1)); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(updateCategoryRequest) + .when().put("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(400) + .body("detail", is("카테고리 이름은 최대 255자까지 입력 가능합니다.")); + } + + @Test + @DisplayName("카테고리 수정 실패: 중복된 이름의 카테고리 존재") + void updateCategoryFailWithDuplicatedName() { + //given + String duplicatedName = "duplicatedName"; + categoryService.create(new CreateCategoryRequest(duplicatedName)); + + UpdateCategoryRequest createCategoryRequest = new UpdateCategoryRequest(duplicatedName); + + //when & then + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(createCategoryRequest) + .when().put("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(409) + .body("detail", is("이름이 " + duplicatedName + "인 카테고리가 이미 존재하고 있습니다.")); + } + } +} diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 944a184af..6dab5569f 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -16,6 +16,7 @@ import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; @@ -75,4 +76,17 @@ void findAllCategoriesSuccess() { assertThat(findAllCategoriesResponse.categories()).hasSize(2); } -} \ No newline at end of file + + @Test + @DisplayName("카테고리 수정 성공") + void updateCategorySuccess() { + //given + Category savedCategory = categoryRepository.save(new Category("category1")); + + //when + categoryService.update(savedCategory.getId(), new UpdateCategoryRequest("updateName")); + + //then + assertThat(categoryRepository.fetchById(savedCategory.getId()).getName()).isEqualTo("updateName"); + } +} From c4ff2645d4fe8e37f51446343281a2384b15d5f6 Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 18:30:00 +0900 Subject: [PATCH 18/69] =?UTF-8?q?refactor(controller):=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EB=B0=8F=20DisplayName=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/category/controller/CategoryControllerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index 18f66462d..b80149a6e 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -55,8 +55,8 @@ void createCategorySuccess() { } @Test - @DisplayName("카테고리 생성 실패:") - void createCategoryFail() { + @DisplayName("카테고리 생성 실패: 카테고리 이름 길이 초과") + void createCategoryFailWithLongName() { CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("a".repeat(MAX_LENGTH + 1)); RestAssured.given().log().all() From 13a537cf3e9ae292300dc3a727506e7425b2e54f Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 20:55:24 +0900 Subject: [PATCH 19/69] =?UTF-8?q?feat(category):=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 4 +- .../category/service/CategoryService.java | 27 ++++++--- .../repository/TemplateRepository.java | 2 + .../controller/CategoryControllerTest.java | 57 +++++++++++++++++++ .../category/service/CategoryServiceTest.java | 13 +++++ 5 files changed, 94 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 311ed556f..ee6179d34 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -49,6 +49,8 @@ public ResponseEntity updateCategory(@PathVariable Long id, @DeleteMapping("/{id}") public ResponseEntity deleteCategory(@PathVariable Long id) { - return null; + categoryService.deleteById(id); + return ResponseEntity.noContent() + .build(); } } diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 151a63945..0e84c0801 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -10,22 +10,23 @@ import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; +import codezap.template.repository.TemplateRepository; @Service public class CategoryService { private final CategoryRepository categoryRepository; + private final TemplateRepository templateRepository; - public CategoryService(CategoryRepository categoryRepository) { + public CategoryService(CategoryRepository categoryRepository, TemplateRepository templateRepository) { this.categoryRepository = categoryRepository; + this.templateRepository = templateRepository; } @Transactional public Long create(CreateCategoryRequest createCategoryRequest) { String categoryName = createCategoryRequest.name(); - if (categoryRepository.existsByName(categoryName)) { - throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); - } + validateDuplicatedCategory(categoryName); Category category = new Category(categoryName); return categoryRepository.save(category).getId(); } @@ -36,11 +37,21 @@ public FindAllCategoriesResponse findAll() { @Transactional public void update(Long id, UpdateCategoryRequest updateCategoryRequest) { - if (categoryRepository.existsByName(updateCategoryRequest.name())) { - throw new CodeZapException(HttpStatus.CONFLICT, - "이름이 " + updateCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); - } + validateDuplicatedCategory(updateCategoryRequest.name()); Category category = categoryRepository.fetchById(id); category.updateName(updateCategoryRequest.name()); } + + public void deleteById(Long id) { + if (templateRepository.existsByCategoryId(id)) { + throw new CodeZapException(HttpStatus.BAD_REQUEST, "템플릿이 존재하는 카테고리는 삭제할 수 없습니다."); + } + categoryRepository.deleteById(id); + } + + private void validateDuplicatedCategory(String categoryName) { + if (categoryRepository.existsByName(categoryName)) { + throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); + } + } } diff --git a/backend/src/main/java/codezap/template/repository/TemplateRepository.java b/backend/src/main/java/codezap/template/repository/TemplateRepository.java index 47b00b246..127eef4cf 100644 --- a/backend/src/main/java/codezap/template/repository/TemplateRepository.java +++ b/backend/src/main/java/codezap/template/repository/TemplateRepository.java @@ -12,4 +12,6 @@ default Template fetchById(Long id) { return findById(id).orElseThrow( () -> new CodeZapException(HttpStatus.NOT_FOUND, "식별자 " + id + "에 해당하는 템플릿이 존재하지 않습니다.")); } + + boolean existsByCategoryId(Long categoryId); } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index b80149a6e..ec3d83feb 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -2,6 +2,8 @@ import static org.hamcrest.Matchers.is; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -16,6 +18,9 @@ import codezap.category.dto.request.CreateCategoryRequest; import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.service.CategoryService; +import codezap.template.dto.request.CreateSnippetRequest; +import codezap.template.dto.request.CreateTemplateRequest; +import codezap.template.service.TemplateService; import io.restassured.RestAssured; import io.restassured.http.ContentType; @@ -31,6 +36,8 @@ class CategoryControllerTest { @Autowired private CategoryService categoryService; + @Autowired + private TemplateService templateService; @BeforeEach void setting() { @@ -142,4 +149,54 @@ void updateCategoryFailWithDuplicatedName() { .body("detail", is("이름이 " + duplicatedName + "인 카테고리가 이미 존재하고 있습니다.")); } } + + + @Nested + @DisplayName("카테고리 삭제 테스트") + class deleteCategoryTest { + + Long savedCategoryId; + + @BeforeEach + void saveCategory() { + savedCategoryId = categoryService.create(new CreateCategoryRequest("category1")); + } + + @Test + @DisplayName("카테고리 삭제 성공") + void deleteCategorySuccess() { + RestAssured.given().log().all() + .when().delete("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(204); + } + + @Test + @DisplayName("카테고리 수정 성공: 존재하지 않는 카테고리의 삭제 요청") + void updateCategoryFailWithDuplicatedName() { + RestAssured.given().log().all() + .when().delete("/categories/" + savedCategoryId + 1) + .then().log().all() + .statusCode(204); + } + + @Test + @DisplayName("카테고리 삭제 실패: 템플릿이 존재하는 카테고리는 삭제 불가능") + void updateCategoryFailWithLongName() { + //given + templateService.create(new CreateTemplateRequest( + "title", + List.of(new CreateSnippetRequest("filename", "content", 1)), + savedCategoryId, + List.of("tag1", "tag2") + )); + + //when & then + RestAssured.given().log().all() + .when().delete("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(400) + .body("detail", is("템플릿이 존재하는 카테고리는 삭제할 수 없습니다.")); + } + } } diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 6dab5569f..d80d23413 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -89,4 +89,17 @@ void updateCategorySuccess() { //then assertThat(categoryRepository.fetchById(savedCategory.getId()).getName()).isEqualTo("updateName"); } + + @Test + @DisplayName("카테고리 삭제 성공") + void deleteCategorySuccess() { + //given + Category savedCategory = categoryRepository.save(new Category("category1")); + + //when + categoryService.deleteById(savedCategory.getId()); + + //then + assertThat(categoryRepository.findById(savedCategory.getId())).isEmpty(); + } } From fd8faf6cb10e6d58991578fd3db7338348688386 Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 21:01:55 +0900 Subject: [PATCH 20/69] =?UTF-8?q?docs(category):=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EB=AC=B8=EC=84=9C=ED=99=94=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 존재하고 있습니다 -> 존재합니다 로 변경 - 응답에 중복된 이름에 대한 예외 추가 --- .../controller/SpringDocCategoryController.java | 10 +++++++++- .../java/codezap/category/service/CategoryService.java | 2 +- .../category/controller/CategoryControllerTest.java | 2 +- .../codezap/category/service/CategoryServiceTest.java | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java index 621d23002..f3ad05bdf 100644 --- a/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java +++ b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java @@ -26,7 +26,9 @@ public interface SpringDocCategoryController { @Header(name = "생성된 카테고리의 API 경로", example = "/categories/1")}) @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories", errorCases = { @ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "카테고리 이름이 null 입니다."), - @ErrorCase(description = "카테고리 이름이 255자를 초과한 경우", exampleMessage = "카테고리 이름은 최대 255자까지 입력 가능합니다.") + @ErrorCase(description = "카테고리 이름이 255자를 초과한 경우", exampleMessage = "카테고리 이름은 최대 255자까지 입력 가능합니다."), + @ErrorCase(description = "동일한 이름의 카테고리가 존재하는 경우", + exampleMessage = "이름이 Spring 인 카테고리가 이미 존재합니다.") }) ResponseEntity createCategory(CreateCategoryRequest createCategoryRequest); @@ -40,10 +42,16 @@ public interface SpringDocCategoryController { @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories/1", errorCases = { @ErrorCase(description = "해당하는 id 값인 카테고리가 없는 경우", exampleMessage = "식별자 1에 해당하는 카테고리가 존재하지 않습니다."), + @ErrorCase(description = "동일한 이름의 카테고리가 존재하는 경우", + exampleMessage = "이름이 Spring 인 카테고리가 이미 존재합니다.") }) ResponseEntity updateCategory(Long id, UpdateCategoryRequest updateCategoryRequest); @Operation(summary = "카테고리 삭제", description = "해당하는 식별자의 카테고리를 삭제합니다.") @ApiResponse(responseCode = "204", description = "카테고리 삭제 성공") + @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories/1", errorCases = { + @ErrorCase(description = "삭제하려는 카테고리에 템플릿이 존재하는 경우", + exampleMessage = "템플릿이 존재하는 카테고리는 삭제할 수 없습니다."), + }) ResponseEntity deleteCategory(Long id); } diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 0e84c0801..48a9f5191 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -51,7 +51,7 @@ public void deleteById(Long id) { private void validateDuplicatedCategory(String categoryName) { if (categoryRepository.existsByName(categoryName)) { - throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); + throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재합니다."); } } } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index ec3d83feb..71b21afd3 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -146,7 +146,7 @@ void updateCategoryFailWithDuplicatedName() { .when().put("/categories/" + savedCategoryId) .then().log().all() .statusCode(409) - .body("detail", is("이름이 " + duplicatedName + "인 카테고리가 이미 존재하고 있습니다.")); + .body("detail", is("이름이 " + duplicatedName + "인 카테고리가 이미 존재합니다.")); } } diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index d80d23413..11a811216 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -62,7 +62,7 @@ void createCategoryFailWithDuplicateName() { assertThatThrownBy(() -> categoryService.create(createCategoryRequest)) .isInstanceOf(CodeZapException.class) - .hasMessage("이름이 " + createCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); + .hasMessage("이름이 " + createCategoryRequest.name() + "인 카테고리가 이미 존재합니다."); } } From 7122fd46b5ac1135728b211ecd7b2676429b7d84 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 16:32:23 +0900 Subject: [PATCH 21/69] =?UTF-8?q?feat(domain):=20Category=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zangsu --- .../codezap/category/domain/Category.java | 20 +++++++++++++++++++ .../codezap/template/domain/Template.java | 5 +++++ 2 files changed, 25 insertions(+) create mode 100644 backend/src/main/java/codezap/category/domain/Category.java diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java new file mode 100644 index 000000000..5654900b6 --- /dev/null +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -0,0 +1,20 @@ +package codezap.category.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import codezap.global.auditing.BaseTimeEntity; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class Category extends BaseTimeEntity { + @Id + private Long id; + + @Column(nullable = false) + private String name; +} diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index 1178d79c1..a00b75b63 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -5,7 +5,9 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import codezap.category.domain.Category; import codezap.global.auditing.BaseTimeEntity; import lombok.Getter; import lombok.NoArgsConstructor; @@ -22,6 +24,9 @@ public class Template extends BaseTimeEntity { @Column(nullable = false) private String title; + @ManyToOne(optional = false) + private Category category; + public Template(String title) { this.title = title; } From d13b2567dd757d391679d7d3f9b1d5105147bb43 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 16:41:16 +0900 Subject: [PATCH 22/69] =?UTF-8?q?feat(domain):=20Tag=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zangsu --- .../java/codezap/template/domain/Tag.java | 21 +++++++++ .../codezap/template/domain/TemplateTag.java | 43 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 backend/src/main/java/codezap/template/domain/Tag.java create mode 100644 backend/src/main/java/codezap/template/domain/TemplateTag.java diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java new file mode 100644 index 000000000..73106efe6 --- /dev/null +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -0,0 +1,21 @@ +package codezap.template.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import codezap.global.auditing.BaseTimeEntity; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class Tag extends BaseTimeEntity { + + @Id + private Long id; + + @Column(nullable = false) + private String value; +} diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java new file mode 100644 index 000000000..7fa96742c --- /dev/null +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -0,0 +1,43 @@ +package codezap.template.domain; + +import java.io.Serializable; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; + +import codezap.global.auditing.BaseTimeEntity; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Entity +@RequiredArgsConstructor +@Getter +public class TemplateTag extends BaseTimeEntity { + + @Embeddable + @RequiredArgsConstructor + @Getter + @EqualsAndHashCode + private static class TemplateTagId implements Serializable { + private Long templateId; + private Long tagId; + } + + @EmbeddedId + private TemplateTagId id; + + @ManyToOne + @MapsId("templateId") + @JoinColumn(name = "template_id") + private Template template; + + @ManyToOne + @MapsId("tagId") + @JoinColumn(name = "tag_id") + private Tag tag; +} \ No newline at end of file From dd7fbb70840c44d0dae5afff6ea086bb8064f452 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 20:46:04 +0900 Subject: [PATCH 23/69] =?UTF-8?q?refactor(domain):=20Tag=EC=99=80=20Catego?= =?UTF-8?q?ry=EC=97=90=20=EA=B8=B0=EB=B3=B8=20=ED=82=A4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zangsu --- backend/src/main/java/codezap/category/domain/Category.java | 3 +++ backend/src/main/java/codezap/template/domain/Tag.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 5654900b6..7d96246ed 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -2,6 +2,8 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import codezap.global.auditing.BaseTimeEntity; @@ -13,6 +15,7 @@ @NoArgsConstructor public class Category extends BaseTimeEntity { @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java index 73106efe6..7859a3f61 100644 --- a/backend/src/main/java/codezap/template/domain/Tag.java +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -2,6 +2,8 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import codezap.global.auditing.BaseTimeEntity; @@ -14,6 +16,7 @@ public class Tag extends BaseTimeEntity { @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) From 7ebdb48f4138d7d8670836443d2d9aad35389146 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 20:50:51 +0900 Subject: [PATCH 24/69] =?UTF-8?q?style(service):=20test=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B0=9C=ED=96=89=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zangsu --- .../java/codezap/template/service/TemplateServiceTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index 0bde5fd7a..fea66f4c4 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -34,15 +34,18 @@ @Sql(value = "/clear.sql", executionPhase = ExecutionPhase.AFTER_TEST_CLASS) class TemplateServiceTest { + @LocalServerPort + int port; + @Autowired private TemplateService templateService; - @LocalServerPort - int port; @Autowired private TemplateRepository templateRepository; + @Autowired private SnippetRepository snippetRepository; + @Autowired private ThumbnailSnippetRepository thumbnailSnippetRepository; From 4ee79287ea54f4d287eb437b3ceafd36837b5e95 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 20:54:28 +0900 Subject: [PATCH 25/69] =?UTF-8?q?feat(category):=20category=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 29 +++++++++++++ .../codezap/category/domain/Category.java | 5 +++ .../dto/request/CreateCategoryRequest.java | 6 +++ .../repository/CategoryRepository.java | 8 ++++ .../category/service/CategoryService.java | 24 +++++++++++ .../controller/CategoryControllerTest.java | 42 ++++++++++++++++++ .../category/service/CategoryServiceTest.java | 43 +++++++++++++++++++ backend/src/test/resources/clear.sql | 34 +++++++++++++++ 8 files changed, 191 insertions(+) create mode 100644 backend/src/main/java/codezap/category/controller/CategoryController.java create mode 100644 backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java create mode 100644 backend/src/main/java/codezap/category/repository/CategoryRepository.java create mode 100644 backend/src/main/java/codezap/category/service/CategoryService.java create mode 100644 backend/src/test/java/codezap/category/controller/CategoryControllerTest.java create mode 100644 backend/src/test/java/codezap/category/service/CategoryServiceTest.java diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java new file mode 100644 index 000000000..982706b6e --- /dev/null +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -0,0 +1,29 @@ +package codezap.category.controller; + +import java.net.URI; + +import org.springframework.http.ResponseEntity; +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.RestController; + +import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.service.CategoryService; + +@RestController +@RequestMapping("/categories") +public class CategoryController { + + private final CategoryService categoryService; + + public CategoryController(CategoryService categoryService) { + this.categoryService = categoryService; + } + + @PostMapping + public ResponseEntity createCategory(@RequestBody CreateCategoryRequest createCategoryRequest) { + return ResponseEntity.created(URI.create("/categories/" + categoryService.create(createCategoryRequest))) + .build(); + } +} diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 7d96246ed..0c4aa47da 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -20,4 +20,9 @@ public class Category extends BaseTimeEntity { @Column(nullable = false) private String name; + + public Category(String name) { + this.id = null; + this.name = name; + } } diff --git a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java new file mode 100644 index 000000000..892495166 --- /dev/null +++ b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java @@ -0,0 +1,6 @@ +package codezap.category.dto.request; + +public record CreateCategoryRequest( + String name +) { +} diff --git a/backend/src/main/java/codezap/category/repository/CategoryRepository.java b/backend/src/main/java/codezap/category/repository/CategoryRepository.java new file mode 100644 index 000000000..8b3644a89 --- /dev/null +++ b/backend/src/main/java/codezap/category/repository/CategoryRepository.java @@ -0,0 +1,8 @@ +package codezap.category.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import codezap.category.domain.Category; + +public interface CategoryRepository extends JpaRepository { +} diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java new file mode 100644 index 000000000..7dcb46b59 --- /dev/null +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -0,0 +1,24 @@ +package codezap.category.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import codezap.category.domain.Category; +import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.repository.CategoryRepository; + +@Service +public class CategoryService { + + private final CategoryRepository categoryRepository; + + public CategoryService(CategoryRepository categoryRepository) { + this.categoryRepository = categoryRepository; + } + + @Transactional + public Long create(CreateCategoryRequest createCategoryRequest) { + Category category = new Category(createCategoryRequest.name()); + return categoryRepository.save(category).getId(); + } +} diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java new file mode 100644 index 000000000..00d7cee6f --- /dev/null +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -0,0 +1,42 @@ +package codezap.category.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.Sql.ExecutionPhase; + +import codezap.category.dto.request.CreateCategoryRequest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@Sql(value = "/clear.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) +@Sql(value = "/clear.sql", executionPhase = ExecutionPhase.AFTER_TEST_CLASS) +class CategoryControllerTest { + + @LocalServerPort + int port; + + @BeforeEach + void setting() { + RestAssured.port = port; + } + + @Test + @DisplayName("카테고리 생성 성공") + void createCategorySuccess() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category"); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(createCategoryRequest) + .when().post("/categories") + .then().log().all() + .header("Location", "/categories/1") + .statusCode(201); + } +} \ No newline at end of file diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java new file mode 100644 index 000000000..1c98c92b7 --- /dev/null +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -0,0 +1,43 @@ +package codezap.category.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.Sql.ExecutionPhase; + +import codezap.category.dto.request.CreateCategoryRequest; +import io.restassured.RestAssured; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@Sql(value = "/clear.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) +@Sql(value = "/clear.sql", executionPhase = ExecutionPhase.AFTER_TEST_CLASS) +class CategoryServiceTest { + + @LocalServerPort + int port; + + @Autowired + private CategoryService categoryService; + + @BeforeEach + void setting() { + RestAssured.port = port; + } + + @Test + @DisplayName("카테고리 생성 성공") + void createCategorySuccess() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category1"); + + Long categoryId = categoryService.create(createCategoryRequest); + + assertThat(categoryId).isEqualTo(1L); + } +} \ No newline at end of file diff --git a/backend/src/test/resources/clear.sql b/backend/src/test/resources/clear.sql index 845a987b2..8cb83de09 100644 --- a/backend/src/test/resources/clear.sql +++ b/backend/src/test/resources/clear.sql @@ -1,16 +1,50 @@ DROP TABLE IF EXISTS thumbnail_snippet; DROP TABLE IF EXISTS snippet; +DROP TABLE IF EXISTS template_tag; +DROP TABLE IF EXISTS tag; DROP TABLE IF EXISTS template; +DROP TABLE IF EXISTS category; + +CREATE TABLE category +( + id BIGINT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + created_at DATETIME(6) NOT NULL, + modified_at DATETIME(6) NOT NULL, + PRIMARY KEY (id) +); CREATE TABLE template ( id BIGINT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, + category_id BIGINT NOT NULL, + created_at DATETIME(6) NOT NULL, + modified_at DATETIME(6) NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (category_id) REFERENCES category (id) +); + +create table tag +( + id BIGINT NOT NULL auto_increment, + value VARCHAR(255) NOT NULL, created_at DATETIME(6) NOT NULL, modified_at DATETIME(6) NOT NULL, PRIMARY KEY (id) ); +create table template_tag +( + template_id BIGINT NOT NULL, + tag_id BIGINT NOT NULL, + created_at DATETIME(6) NOT NULL, + modified_at DATETIME(6) NOT NULL, + PRIMARY KEY (template_id, tag_id), + FOREIGN KEY (template_id) REFERENCES template (id), + FOREIGN KEY (tag_id) REFERENCES tag (id) +); + CREATE TABLE snippet ( id BIGINT NOT NULL AUTO_INCREMENT, From 786a089ad6dafd644b51a0440c6cb552e6eb2067 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 21:03:23 +0900 Subject: [PATCH 26/69] =?UTF-8?q?feat(category):=20category=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 4 +- .../dto/request/CreateCategoryRequest.java | 5 ++ .../controller/CategoryControllerTest.java | 47 ++++++++++++++----- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 982706b6e..6f546ef1e 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -2,6 +2,8 @@ import java.net.URI; +import jakarta.validation.Valid; + import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -22,7 +24,7 @@ public CategoryController(CategoryService categoryService) { } @PostMapping - public ResponseEntity createCategory(@RequestBody CreateCategoryRequest createCategoryRequest) { + public ResponseEntity createCategory(@Valid @RequestBody CreateCategoryRequest createCategoryRequest) { return ResponseEntity.created(URI.create("/categories/" + categoryService.create(createCategoryRequest))) .build(); } diff --git a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java index 892495166..7d1276e94 100644 --- a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java @@ -1,6 +1,11 @@ package codezap.category.dto.request; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + public record CreateCategoryRequest( + @NotNull(message = "카테고리 이름이 null 입니다.") + @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") String name ) { } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index 00d7cee6f..edfbf41a2 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -1,7 +1,10 @@ package codezap.category.controller; +import static org.hamcrest.Matchers.is; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -18,6 +21,8 @@ @Sql(value = "/clear.sql", executionPhase = ExecutionPhase.AFTER_TEST_CLASS) class CategoryControllerTest { + private static final int MAX_LENGTH = 255; + @LocalServerPort int port; @@ -26,17 +31,35 @@ void setting() { RestAssured.port = port; } - @Test - @DisplayName("카테고리 생성 성공") - void createCategorySuccess() { - CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category"); - - RestAssured.given().log().all() - .contentType(ContentType.JSON) - .body(createCategoryRequest) - .when().post("/categories") - .then().log().all() - .header("Location", "/categories/1") - .statusCode(201); + @Nested + @DisplayName("카테고리 생성 테스트") + class createCategoryTest { + @Test + @DisplayName("카테고리 생성 성공") + void createCategorySuccess() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("a".repeat(MAX_LENGTH)); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(createCategoryRequest) + .when().post("/categories") + .then().log().all() + .header("Location", "/categories/1") + .statusCode(201); + } + + @Test + @DisplayName("카테고리 생성 실패:") + void createCategoryFail() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("a".repeat(MAX_LENGTH + 1)); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(createCategoryRequest) + .when().post("/categories") + .then().log().all() + .statusCode(400) + .body("detail", is("카테고리 이름은 최대 255자까지 입력 가능합니다.")); + } } } \ No newline at end of file From 778f92a2cc2f4e67bd359c836e29ba959017aaa3 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 21:29:18 +0900 Subject: [PATCH 27/69] =?UTF-8?q?feat(category):=20category=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=A4=91=EB=B3=B5=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/category/domain/Category.java | 2 +- .../repository/CategoryRepository.java | 1 + .../category/service/CategoryService.java | 8 ++++- .../category/service/CategoryServiceTest.java | 35 +++++++++++++++---- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 0c4aa47da..59c7ef843 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -18,7 +18,7 @@ public class Category extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(nullable = false) + @Column(nullable = false, unique = true) private String name; public Category(String name) { diff --git a/backend/src/main/java/codezap/category/repository/CategoryRepository.java b/backend/src/main/java/codezap/category/repository/CategoryRepository.java index 8b3644a89..fe335048a 100644 --- a/backend/src/main/java/codezap/category/repository/CategoryRepository.java +++ b/backend/src/main/java/codezap/category/repository/CategoryRepository.java @@ -5,4 +5,5 @@ import codezap.category.domain.Category; public interface CategoryRepository extends JpaRepository { + boolean existsByName(String name); } diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 7dcb46b59..b1e52b55e 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -1,11 +1,13 @@ package codezap.category.service; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; import codezap.category.repository.CategoryRepository; +import codezap.global.exception.CodeZapException; @Service public class CategoryService { @@ -18,7 +20,11 @@ public CategoryService(CategoryRepository categoryRepository) { @Transactional public Long create(CreateCategoryRequest createCategoryRequest) { - Category category = new Category(createCategoryRequest.name()); + String categoryName = createCategoryRequest.name(); + if(categoryRepository.existsByName(categoryName)) { + throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); + } + Category category = new Category(categoryName); return categoryRepository.save(category).getId(); } } diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 1c98c92b7..167ab9b12 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -1,9 +1,11 @@ package codezap.category.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -12,7 +14,10 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.repository.CategoryRepository; +import codezap.global.exception.CodeZapException; import io.restassured.RestAssured; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @@ -26,18 +31,36 @@ class CategoryServiceTest { @Autowired private CategoryService categoryService; + @Autowired + private CategoryRepository categoryRepository; + @BeforeEach void setting() { RestAssured.port = port; } - @Test - @DisplayName("카테고리 생성 성공") - void createCategorySuccess() { - CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category1"); + @Nested + @DisplayName("카테고리 생성 테스트") + class createCategoryTest { + @Test + @DisplayName("카테고리 생성 성공") + void createCategorySuccess() { + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category1"); + + Long categoryId = categoryService.create(createCategoryRequest); + + assertThat(categoryId).isEqualTo(1L); + } - Long categoryId = categoryService.create(createCategoryRequest); + @Test + @DisplayName("카테고리 생성 실패: 중복된 이름의 카테고리 이름 생성") + void createCategoryFailWithDuplicateName() { + categoryRepository.save(new Category("category")); + CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("category"); - assertThat(categoryId).isEqualTo(1L); + assertThatThrownBy(() -> categoryService.create(createCategoryRequest)) + .isInstanceOf(CodeZapException.class) + .hasMessage("이름이 " + createCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); + } } } \ No newline at end of file From d64dfa272945302d8d2bc9ea05e8e9084e2b93ee Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 21:49:42 +0900 Subject: [PATCH 28/69] =?UTF-8?q?feat(category):=20category=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 7 +++++++ .../response/FindAllCategoriesResponse.java | 17 +++++++++++++++ .../response/FindCategoryByIdResponse.java | 12 +++++++++++ .../category/service/CategoryService.java | 5 +++++ .../controller/CategoryControllerTest.java | 21 +++++++++++++++++++ .../category/service/CategoryServiceTest.java | 12 +++++++++++ 6 files changed, 74 insertions(+) create mode 100644 backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java create mode 100644 backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 6f546ef1e..27e50a673 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -5,12 +5,14 @@ import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; 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.RestController; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.service.CategoryService; @RestController @@ -28,4 +30,9 @@ public ResponseEntity createCategory(@Valid @RequestBody CreateCategoryReq return ResponseEntity.created(URI.create("/categories/" + categoryService.create(createCategoryRequest))) .build(); } + + @GetMapping + public ResponseEntity getCategories() { + return ResponseEntity.ok(categoryService.findAll()); + } } diff --git a/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java new file mode 100644 index 000000000..e7df7c5a6 --- /dev/null +++ b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java @@ -0,0 +1,17 @@ +package codezap.category.dto.response; + +import java.util.List; + +import codezap.category.domain.Category; + +public record FindAllCategoriesResponse( + List categories +) { + public static FindAllCategoriesResponse from(List categories) { + return new FindAllCategoriesResponse( + categories.stream() + .map(FindCategoryByIdResponse::from) + .toList() + ); + } +} diff --git a/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java b/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java new file mode 100644 index 000000000..fe7909aec --- /dev/null +++ b/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java @@ -0,0 +1,12 @@ +package codezap.category.dto.response; + +import codezap.category.domain.Category; + +public record FindCategoryByIdResponse( + Long id, + String name +) { + public static FindCategoryByIdResponse from(Category category) { + return new FindCategoryByIdResponse(category.getId(), category.getName()); + } +} diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index b1e52b55e..02a83af88 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -6,6 +6,7 @@ import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; @@ -27,4 +28,8 @@ public Long create(CreateCategoryRequest createCategoryRequest) { Category category = new Category(categoryName); return categoryRepository.save(category).getId(); } + + public FindAllCategoriesResponse findAll() { + return FindAllCategoriesResponse.from(categoryRepository.findAll()); + } } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index edfbf41a2..16f519f83 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; @@ -13,6 +14,7 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.service.CategoryService; import io.restassured.RestAssured; import io.restassured.http.ContentType; @@ -26,6 +28,9 @@ class CategoryControllerTest { @LocalServerPort int port; + @Autowired + private CategoryService categoryService; + @BeforeEach void setting() { RestAssured.port = port; @@ -62,4 +67,20 @@ void createCategoryFail() { .body("detail", is("카테고리 이름은 최대 255자까지 입력 가능합니다.")); } } + + @Test + @DisplayName("카테고리 전체 조회 성공") + void findAllCategoriesSuccess() { + CreateCategoryRequest createCategoryRequest1 = new CreateCategoryRequest("category1"); + CreateCategoryRequest createCategoryRequest2 = new CreateCategoryRequest("category2"); + categoryService.create(createCategoryRequest1); + categoryService.create(createCategoryRequest2); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .when().get("/categories") + .then().log().all() + .statusCode(200) + .body("categories.size()", is(2)); + } } \ No newline at end of file diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 167ab9b12..944a184af 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -16,6 +16,7 @@ import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; import io.restassured.RestAssured; @@ -63,4 +64,15 @@ void createCategoryFailWithDuplicateName() { .hasMessage("이름이 " + createCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); } } + + @Test + @DisplayName("카테고리 전체 조회 테스트") + void findAllCategoriesSuccess() { + categoryRepository.save(new Category("category1")); + categoryRepository.save(new Category("category2")); + + FindAllCategoriesResponse findAllCategoriesResponse = categoryService.findAll(); + + assertThat(findAllCategoriesResponse.categories()).hasSize(2); + } } \ No newline at end of file From 05fe337a6fff440339f1f7b1fa6435a3473fe48e Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 30 Jul 2024 21:51:45 +0900 Subject: [PATCH 29/69] =?UTF-8?q?refactor(domain):=20lombok=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=88=9C=EC=84=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84=20=EC=88=9C=EC=84=9C?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/codezap/category/domain/Category.java | 2 +- backend/src/main/java/codezap/template/domain/Snippet.java | 2 +- backend/src/main/java/codezap/template/domain/Tag.java | 2 +- backend/src/main/java/codezap/template/domain/Template.java | 2 +- backend/src/main/java/codezap/template/domain/TemplateTag.java | 3 ++- .../main/java/codezap/template/domain/ThumbnailSnippet.java | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 59c7ef843..f28666022 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -11,8 +11,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class Category extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/backend/src/main/java/codezap/template/domain/Snippet.java b/backend/src/main/java/codezap/template/domain/Snippet.java index 8cf0291a5..c85b0ee36 100644 --- a/backend/src/main/java/codezap/template/domain/Snippet.java +++ b/backend/src/main/java/codezap/template/domain/Snippet.java @@ -16,8 +16,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class Snippet extends BaseTimeEntity { private static final String CODE_LINE_BREAK = "\n"; diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java index 7859a3f61..0c2208952 100644 --- a/backend/src/main/java/codezap/template/domain/Tag.java +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -11,8 +11,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class Tag extends BaseTimeEntity { @Id diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index a00b75b63..29e518068 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -13,8 +13,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class Template extends BaseTimeEntity { @Id diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java index 7fa96742c..72729dbb4 100644 --- a/backend/src/main/java/codezap/template/domain/TemplateTag.java +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -12,10 +12,11 @@ import codezap.global.auditing.BaseTimeEntity; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; @Entity -@RequiredArgsConstructor +@NoArgsConstructor @Getter public class TemplateTag extends BaseTimeEntity { diff --git a/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java b/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java index 8c6b5feea..920dc18d2 100644 --- a/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java +++ b/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java @@ -11,8 +11,8 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor +@Getter public class ThumbnailSnippet extends BaseTimeEntity { @Id From 2fc9651a1ec6fb8de4fd62d70f25eb1704625eb7 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Wed, 31 Jul 2024 13:26:34 +0900 Subject: [PATCH 30/69] =?UTF-8?q?refactor(domain):=20Tag=20value=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=EB=A5=BC=20name=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/codezap/template/domain/Tag.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java index 0c2208952..8098b5d29 100644 --- a/backend/src/main/java/codezap/template/domain/Tag.java +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -20,5 +20,5 @@ public class Tag extends BaseTimeEntity { private Long id; @Column(nullable = false) - private String value; + private String name; } From d12f79781e8d6969ca581346997e03816cf2876c Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Wed, 31 Jul 2024 15:32:14 +0900 Subject: [PATCH 31/69] =?UTF-8?q?feat(codezap):=20template=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EA=B8=B0=EB=8A=A5=EC=97=90=20tag=EC=99=80=20catego?= =?UTF-8?q?ry=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/category/domain/Category.java | 1 - .../repository/CategoryRepository.java | 8 ++++++ .../java/codezap/template/domain/Tag.java | 4 +++ .../codezap/template/domain/Template.java | 6 +++- .../codezap/template/domain/TemplateTag.java | 11 +++++++- .../dto/request/CreateTemplateRequest.java | 6 +++- .../template/repository/TagRepository.java | 8 ++++++ .../repository/TemplateTagRepository.java | 8 ++++++ .../template/service/TemplateService.java | 28 +++++++++++++++++-- 9 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/java/codezap/template/repository/TagRepository.java create mode 100644 backend/src/main/java/codezap/template/repository/TemplateTagRepository.java diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index f28666022..5ffb71ed2 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -22,7 +22,6 @@ public class Category extends BaseTimeEntity { private String name; public Category(String name) { - this.id = null; this.name = name; } } diff --git a/backend/src/main/java/codezap/category/repository/CategoryRepository.java b/backend/src/main/java/codezap/category/repository/CategoryRepository.java index fe335048a..0f710a2c4 100644 --- a/backend/src/main/java/codezap/category/repository/CategoryRepository.java +++ b/backend/src/main/java/codezap/category/repository/CategoryRepository.java @@ -1,9 +1,17 @@ package codezap.category.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.http.HttpStatus; import codezap.category.domain.Category; +import codezap.global.exception.CodeZapException; public interface CategoryRepository extends JpaRepository { + + default Category fetchById(Long id) { + return findById(id).orElseThrow( + () -> new CodeZapException(HttpStatus.NOT_FOUND, "식별자 " + id + "에 해당하는 카테고리가 존재하지 않습니다.")); + } + boolean existsByName(String name); } diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java index 8098b5d29..eb7bf6fd7 100644 --- a/backend/src/main/java/codezap/template/domain/Tag.java +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -21,4 +21,8 @@ public class Tag extends BaseTimeEntity { @Column(nullable = false) private String name; + + public Tag(String name) { + this.name = name; + } } diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index 29e518068..bd55d4103 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -24,11 +24,15 @@ public class Template extends BaseTimeEntity { @Column(nullable = false) private String title; + @Column(columnDefinition = "TEXT") + private String description; + @ManyToOne(optional = false) private Category category; - public Template(String title) { + public Template(String title, Category category) { this.title = title; + this.category = category; } public void updateTitle(String title) { diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java index 72729dbb4..0d46698ee 100644 --- a/backend/src/main/java/codezap/template/domain/TemplateTag.java +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -10,10 +10,12 @@ import jakarta.persistence.MapsId; import codezap.global.auditing.BaseTimeEntity; +import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; +import lombok.Setter; @Entity @NoArgsConstructor @@ -21,7 +23,8 @@ public class TemplateTag extends BaseTimeEntity { @Embeddable - @RequiredArgsConstructor + @NoArgsConstructor + @AllArgsConstructor @Getter @EqualsAndHashCode private static class TemplateTagId implements Serializable { @@ -41,4 +44,10 @@ private static class TemplateTagId implements Serializable { @MapsId("tagId") @JoinColumn(name = "tag_id") private Tag tag; + + public TemplateTag(Template template, Tag tag) { + this.id = new TemplateTagId(template.getId(), tag.getId()); + this.template = template; + this.tag = tag; + } } \ No newline at end of file diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index 9663ba500..9527ff657 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -18,7 +18,11 @@ public record CreateTemplateRequest( @Schema(description = "템플릿의 스니펫 내역") @NotNull(message = "스니펫 리스트가 null 입니다.") @Valid - List snippets + List snippets, + + Long categoryId, + + List tags ) implements ValidatedSnippetsOrdinalRequest { @Override public List extractSnippetsOrdinal() { diff --git a/backend/src/main/java/codezap/template/repository/TagRepository.java b/backend/src/main/java/codezap/template/repository/TagRepository.java new file mode 100644 index 000000000..e813662e8 --- /dev/null +++ b/backend/src/main/java/codezap/template/repository/TagRepository.java @@ -0,0 +1,8 @@ +package codezap.template.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import codezap.template.domain.Tag; + +public interface TagRepository extends JpaRepository { +} diff --git a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java new file mode 100644 index 000000000..5396f030c --- /dev/null +++ b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java @@ -0,0 +1,8 @@ +package codezap.template.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import codezap.template.domain.TemplateTag; + +public interface TemplateTagRepository extends JpaRepository { +} diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 57be444c7..8adb4f6aa 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -6,8 +6,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import codezap.category.domain.Category; +import codezap.category.repository.CategoryRepository; import codezap.template.domain.Snippet; +import codezap.template.domain.Tag; import codezap.template.domain.Template; +import codezap.template.domain.TemplateTag; import codezap.template.domain.ThumbnailSnippet; import codezap.template.dto.request.CreateSnippetRequest; import codezap.template.dto.request.CreateTemplateRequest; @@ -16,7 +20,9 @@ import codezap.template.dto.response.FindAllTemplatesResponse; import codezap.template.dto.response.FindTemplateByIdResponse; import codezap.template.repository.SnippetRepository; +import codezap.template.repository.TagRepository; import codezap.template.repository.TemplateRepository; +import codezap.template.repository.TemplateTagRepository; import codezap.template.repository.ThumbnailSnippetRepository; @Service @@ -27,19 +33,35 @@ public class TemplateService { private final ThumbnailSnippetRepository thumbnailSnippetRepository; private final TemplateRepository templateRepository; private final SnippetRepository snippetRepository; + private final CategoryRepository categoryRepository; + private final TagRepository tagRepository; + private final TemplateTagRepository templateTagRepository; public TemplateService(ThumbnailSnippetRepository thumbnailSnippetRepository, - TemplateRepository templateRepository, SnippetRepository snippetRepository + TemplateRepository templateRepository, SnippetRepository snippetRepository, + CategoryRepository categoryRepository, TagRepository tagRepository, + TemplateTagRepository templateTagRepository ) { this.thumbnailSnippetRepository = thumbnailSnippetRepository; this.templateRepository = templateRepository; this.snippetRepository = snippetRepository; + this.categoryRepository = categoryRepository; + this.tagRepository = tagRepository; + this.templateTagRepository = templateTagRepository; } @Transactional public Long create(CreateTemplateRequest createTemplateRequest) { - Template template = templateRepository.save( - new Template(createTemplateRequest.title())); + Category category = categoryRepository.fetchById(createTemplateRequest.categoryId()); + + Template template = templateRepository.save(new Template(createTemplateRequest.title(), category)); + + List tags = createTemplateRequest.tags().stream() + .map(Tag::new) + .map(tagRepository::save) + .toList(); + + tags.forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); createTemplateRequest.snippets() .forEach(createSnippetRequest -> createSnippet(createSnippetRequest, template)); From 8df56e453543a3582c63ef0dc768482b6ee294cd Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Wed, 31 Jul 2024 16:04:00 +0900 Subject: [PATCH 32/69] =?UTF-8?q?refactor(test):=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TemplateControllerTest.java | 48 +++++++++++++++---- .../repository/SnippetRepositoryTest.java | 10 +++- .../template/service/TemplateServiceTest.java | 12 ++++- backend/src/test/resources/clear.sql | 3 +- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index 12833e4ef..b8659010f 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -17,6 +17,8 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.service.CategoryService; import codezap.template.dto.request.CreateSnippetRequest; import codezap.template.dto.request.CreateTemplateRequest; import codezap.template.dto.request.UpdateSnippetRequest; @@ -35,6 +37,9 @@ class TemplateControllerTest { @Autowired private TemplateService templateService; + @Autowired + private CategoryService categoryService; + @LocalServerPort int port; @@ -52,9 +57,13 @@ class createTemplateTest { @CsvSource({"a, 65535", "ㄱ, 21845"}) void createTemplateSuccess(String repeatTarget, int maxLength) { String maxTitle = "a".repeat(MAX_LENGTH); + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest(maxTitle, - List.of(new CreateSnippetRequest("a".repeat(MAX_LENGTH), repeatTarget.repeat(maxLength), 1))); - + List.of(new CreateSnippetRequest("a".repeat(MAX_LENGTH), repeatTarget.repeat(maxLength), 1)), + 1L, + List.of("tag1", "tag2") + ); + RestAssured.given().log().all() .contentType(ContentType.JSON) .body(templateRequest) @@ -68,9 +77,13 @@ void createTemplateSuccess(String repeatTarget, int maxLength) { @DisplayName("템플릿 생성 실패: 템플릿 이름 길이 초과") void createTemplateFailWithLongTitle() { String exceededTitle = "a".repeat(MAX_LENGTH + 1); + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest(exceededTitle, - List.of(new CreateSnippetRequest("a", "content", 1))); - + List.of(new CreateSnippetRequest("a", "content", 1)), + 1L, + List.of("tag1", "tag2") + ); + RestAssured.given().log().all() .contentType(ContentType.JSON) .body(templateRequest) @@ -84,8 +97,12 @@ void createTemplateFailWithLongTitle() { @DisplayName("템플릿 생성 실패: 파일 이름 길이 초과") void createTemplateFailWithLongFileName() { String exceededTitle = "a".repeat(MAX_LENGTH + 1); + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest("title", - List.of(new CreateSnippetRequest(exceededTitle, "content", 1))); + List.of(new CreateSnippetRequest(exceededTitle, "content", 1)), + 1L, + List.of("tag1", "tag2") + ); RestAssured.given().log().all() .contentType(ContentType.JSON) @@ -100,8 +117,12 @@ void createTemplateFailWithLongFileName() { @DisplayName("템플릿 생성 실패: 파일 내용 길이 초과") @CsvSource({"a, 65536", "ㄱ, 21846"}) void createTemplateFailWithLongContent(String repeatTarget, int exceededLength) { + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest("title", - List.of(new CreateSnippetRequest("title", repeatTarget.repeat(exceededLength), 1))); + List.of(new CreateSnippetRequest("title", repeatTarget.repeat(exceededLength), 1)), + 1L, + List.of("tag1", "tag2") + ); RestAssured.given().log().all() .contentType(ContentType.JSON) @@ -116,9 +137,13 @@ void createTemplateFailWithLongContent(String repeatTarget, int exceededLength) @DisplayName("템플릿 생성 실패: 잘못된 스니펫 순서 입력") @CsvSource({"0, 1", "1, 3", "2, 1"}) void createTemplateFailWithWrongSnippetOrdinal(int firstIndex, int secondIndex) { + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest("title", List.of(new CreateSnippetRequest("title", "content", firstIndex), - new CreateSnippetRequest("title", "content", secondIndex))); + new CreateSnippetRequest("title", "content", secondIndex)), + 1L, + List.of("tag1", "tag2") + ); RestAssured.given().log().all() .contentType(ContentType.JSON) @@ -136,6 +161,7 @@ void findAllTemplatesSuccess() { // given CreateTemplateRequest templateRequest1 = createTemplateRequestWithTwoSnippets("title1"); CreateTemplateRequest templateRequest2 = createTemplateRequestWithTwoSnippets("title2"); + categoryService.create(new CreateCategoryRequest("category")); templateService.create(templateRequest1); templateService.create(templateRequest2); @@ -155,6 +181,7 @@ class findTemplateTest { @DisplayName("템플릿 상세 조회 성공") void findOneTemplateSuccess() { // given + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -187,6 +214,7 @@ class updateTemplateTest { @DisplayName("템플릿 수정 성공") void updateTemplateSuccess() { // given + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -217,6 +245,7 @@ void updateTemplateSuccess() { @CsvSource({"1, 2, 1", "3, 2, 1", "0, 2, 1"}) void updateTemplateFailWithWrongSnippetOrdinal(int createOrdinal1, int createOrdinal2, int updateOrdinal) { // given + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -251,6 +280,7 @@ class deleteTemplateTest { @DisplayName("템플릿 삭제 성공") void deleteTemplateSuccess() { // given + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -279,7 +309,9 @@ private static CreateTemplateRequest createTemplateRequestWithTwoSnippets(String List.of( new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2) - ) + ), + 1L, + List.of("tag1", "tag2") ); return templateRequest; } diff --git a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java index 750ce4055..075ee12c7 100644 --- a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java +++ b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java @@ -13,6 +13,8 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase; import org.springframework.transaction.annotation.Transactional; +import codezap.category.domain.Category; +import codezap.category.repository.CategoryRepository; import codezap.template.domain.Snippet; import codezap.template.domain.Template; @@ -26,11 +28,14 @@ class SnippetRepositoryTest { private SnippetRepository snippetRepository; @Autowired private TemplateRepository templateRepository; + @Autowired + private CategoryRepository categoryRepository; @Test @DisplayName("단일 스니펫 찾기 성공: 템플릿과 순서") void findOneSnippetSuccessWithTemplateAndOrdinal() { - Template template = templateRepository.save(new Template("title")); + Category category = categoryRepository.save(new Category("category")); + Template template = templateRepository.save(new Template("title", category)); Snippet snippet1 = snippetRepository.save(new Snippet(template, "filename1", "content1", 1)); Snippet snippet2 = snippetRepository.save(new Snippet(template, "filename2", "content2", 2)); @@ -47,7 +52,8 @@ void findOneSnippetSuccessWithTemplateAndOrdinal() { @Test @DisplayName("스니펫 리스트 찾기 성공: 템플릿과 순서") void findSnippetsSuccessWithTemplateAndOrdinal() { - Template template = templateRepository.save(new Template("title")); + Category category = categoryRepository.save(new Category("category")); + Template template = templateRepository.save(new Template("title", category)); Snippet snippet1 = snippetRepository.save(new Snippet(template, "filename1", "content1", 1)); Snippet snippet2 = snippetRepository.save(new Snippet(template, "filename2", "content2", 2)); Snippet snippet3 = snippetRepository.save(new Snippet(template, "filename3", "content3", 2)); diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index fea66f4c4..27cb2591d 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -15,6 +15,8 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +import codezap.category.domain.Category; +import codezap.category.repository.CategoryRepository; import codezap.template.domain.Snippet; import codezap.template.domain.Template; import codezap.template.domain.ThumbnailSnippet; @@ -48,6 +50,8 @@ class TemplateServiceTest { @Autowired private ThumbnailSnippetRepository thumbnailSnippetRepository; + @Autowired + private CategoryRepository categoryRepository; @BeforeEach void setting() { @@ -59,6 +63,7 @@ void setting() { void createTemplateSuccess() { // given CreateTemplateRequest createTemplateRequest = makeTemplateRequest("title"); + categoryRepository.save(new Category("category")); // when templateService.create(createTemplateRequest); @@ -143,7 +148,9 @@ private CreateTemplateRequest makeTemplateRequest(String title) { List.of( new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2) - ) + ), + 1L, + List.of("tag1", "tag2") ); } @@ -162,7 +169,8 @@ private UpdateTemplateRequest makeUpdateTemplateRequest(String title) { } private Template saveTemplate(CreateTemplateRequest createTemplateRequest) { - Template savedTemplate = templateRepository.save(new Template(createTemplateRequest.title())); + Category category = categoryRepository.save(new Category("category")); + Template savedTemplate = templateRepository.save(new Template(createTemplateRequest.title(), category)); Snippet savedFirstSnippet = snippetRepository.save(new Snippet(savedTemplate, "filename1", "content1", 1)); snippetRepository.save(new Snippet(savedTemplate, "filename2", "content2", 2)); thumbnailSnippetRepository.save(new ThumbnailSnippet(savedTemplate, savedFirstSnippet)); diff --git a/backend/src/test/resources/clear.sql b/backend/src/test/resources/clear.sql index 8cb83de09..9aada0497 100644 --- a/backend/src/test/resources/clear.sql +++ b/backend/src/test/resources/clear.sql @@ -18,6 +18,7 @@ CREATE TABLE template ( id BIGINT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, + description TEXT, category_id BIGINT NOT NULL, created_at DATETIME(6) NOT NULL, modified_at DATETIME(6) NOT NULL, @@ -28,7 +29,7 @@ CREATE TABLE template create table tag ( id BIGINT NOT NULL auto_increment, - value VARCHAR(255) NOT NULL, + name VARCHAR(255) NOT NULL, created_at DATETIME(6) NOT NULL, modified_at DATETIME(6) NOT NULL, PRIMARY KEY (id) From fae0611fcd5f4880a50c465c45f92d04e46b562f Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Wed, 31 Jul 2024 16:32:18 +0900 Subject: [PATCH 33/69] =?UTF-8?q?feat(test):=20=ED=85=9C=ED=94=8C=EB=A6=BF?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=EC=97=90=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC,=20=ED=83=9C=EA=B7=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/service/CategoryService.java | 2 +- .../codezap/template/domain/TemplateTag.java | 2 -- .../dto/response/FindTagByIdResponse.java | 12 +++++++++++ .../response/FindTemplateByIdResponse.java | 21 ++++++++++++++++++- .../repository/TemplateTagRepository.java | 5 +++++ .../template/service/TemplateService.java | 5 ++++- .../controller/TemplateControllerTest.java | 5 ++++- .../template/service/TemplateServiceTest.java | 19 ++++++++++++++++- 8 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 02a83af88..e4c59e5de 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -22,7 +22,7 @@ public CategoryService(CategoryRepository categoryRepository) { @Transactional public Long create(CreateCategoryRequest createCategoryRequest) { String categoryName = createCategoryRequest.name(); - if(categoryRepository.existsByName(categoryName)) { + if (categoryRepository.existsByName(categoryName)) { throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); } Category category = new Category(categoryName); diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java index 0d46698ee..95214758d 100644 --- a/backend/src/main/java/codezap/template/domain/TemplateTag.java +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -14,8 +14,6 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; -import lombok.Setter; @Entity @NoArgsConstructor diff --git a/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java new file mode 100644 index 000000000..8052780b0 --- /dev/null +++ b/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java @@ -0,0 +1,12 @@ +package codezap.template.dto.response; + +import codezap.template.domain.Tag; + +public record FindTagByIdResponse( + Long id, + String name +) { + public static FindTagByIdResponse from(Tag tag) { + return new FindTagByIdResponse(tag.getId(), tag.getName()); + } +} diff --git a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java index 6fccceb7c..734741750 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java @@ -3,25 +3,36 @@ import java.time.LocalDateTime; import java.util.List; +import codezap.category.dto.response.FindCategoryByIdResponse; import codezap.template.domain.Snippet; +import codezap.template.domain.Tag; import codezap.template.domain.Template; import io.swagger.v3.oas.annotations.media.Schema; public record FindTemplateByIdResponse( @Schema(description = "템플릿 식별자", example = "0") Long id, + @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") String title, + @Schema(description = "스니펫 목록") List snippets, + + FindCategoryByIdResponse category, + + List tags, + @Schema(description = "템플릿 수정 시간", example = "2024-11-11 12:00", type = "string") LocalDateTime modifiedAt ) { - public static FindTemplateByIdResponse of(Template template, List snippets) { + public static FindTemplateByIdResponse of(Template template, List snippets, List tags) { return new FindTemplateByIdResponse( template.getId(), template.getTitle(), mapToFindAllSnippetByTemplateResponse(snippets), + FindCategoryByIdResponse.from(template.getCategory()), + mapToFindTagByTemplateResponse(tags), template.getModifiedAt() ); } @@ -33,4 +44,12 @@ private static List mapToFindAllSnippetByTempl .map(FindAllSnippetByTemplateResponse::from) .toList(); } + + private static List mapToFindTagByTemplateResponse( + List tags + ) { + return tags.stream() + .map(FindTagByIdResponse::from) + .toList(); + } } diff --git a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java index 5396f030c..b49d7355f 100644 --- a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java @@ -1,8 +1,13 @@ package codezap.template.repository; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; +import codezap.template.domain.Template; import codezap.template.domain.TemplateTag; public interface TemplateTagRepository extends JpaRepository { + + List findAllByTemplate(Template template); } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 8adb4f6aa..538ff606d 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -78,7 +78,10 @@ public FindAllTemplatesResponse findAll() { public FindTemplateByIdResponse findById(Long id) { Template template = templateRepository.fetchById(id); List snippets = snippetRepository.findAllByTemplate(template); - return FindTemplateByIdResponse.of(template, snippets); + List tags = templateTagRepository.findAllByTemplate(template).stream() + .map(TemplateTag::getTag) + .toList(); + return FindTemplateByIdResponse.of(template, snippets, tags); } @Transactional diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index b8659010f..47d11ab65 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -191,7 +191,10 @@ void findOneTemplateSuccess() { .then().log().all() .statusCode(200) .body("title", is(templateRequest.title()), - "snippets.size()", is(2)); + "snippets.size()", is(2), + "category.id", is(1), + "category.name", is("category"), + "tags.size()", is(2)); } @Test diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index 27cb2591d..10d97d771 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -18,7 +18,9 @@ import codezap.category.domain.Category; import codezap.category.repository.CategoryRepository; import codezap.template.domain.Snippet; +import codezap.template.domain.Tag; import codezap.template.domain.Template; +import codezap.template.domain.TemplateTag; import codezap.template.domain.ThumbnailSnippet; import codezap.template.dto.request.CreateSnippetRequest; import codezap.template.dto.request.CreateTemplateRequest; @@ -27,7 +29,9 @@ import codezap.template.dto.response.FindAllTemplatesResponse; import codezap.template.dto.response.FindTemplateByIdResponse; import codezap.template.repository.SnippetRepository; +import codezap.template.repository.TagRepository; import codezap.template.repository.TemplateRepository; +import codezap.template.repository.TemplateTagRepository; import codezap.template.repository.ThumbnailSnippetRepository; import io.restassured.RestAssured; @@ -50,9 +54,15 @@ class TemplateServiceTest { @Autowired private ThumbnailSnippetRepository thumbnailSnippetRepository; + @Autowired private CategoryRepository categoryRepository; + @Autowired + private TemplateTagRepository templateTagRepository; + @Autowired + private TagRepository tagRepository; + @BeforeEach void setting() { RestAssured.port = port; @@ -99,7 +109,10 @@ void findOneTemplateSuccess() { // then assertAll( () -> assertThat(foundTemplate.title()).isEqualTo(template.getTitle()), - () -> assertThat(foundTemplate.snippets()).hasSize(snippetRepository.findAllByTemplate(template).size()) + () -> assertThat(foundTemplate.snippets()).hasSize( + snippetRepository.findAllByTemplate(template).size()), + () -> assertThat(foundTemplate.category().id()).isEqualTo(template.getCategory().getId()), + () -> assertThat(foundTemplate.tags()).hasSize(2) ); } @@ -174,6 +187,10 @@ private Template saveTemplate(CreateTemplateRequest createTemplateRequest) { Snippet savedFirstSnippet = snippetRepository.save(new Snippet(savedTemplate, "filename1", "content1", 1)); snippetRepository.save(new Snippet(savedTemplate, "filename2", "content2", 2)); thumbnailSnippetRepository.save(new ThumbnailSnippet(savedTemplate, savedFirstSnippet)); + createTemplateRequest.tags().stream() + .map(Tag::new) + .map(tagRepository::save) + .forEach(tag -> templateTagRepository.save(new TemplateTag(savedTemplate, tag))); return savedTemplate; } From a36eb9a29db48932411948575e324f3ad2da2f73 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Thu, 1 Aug 2024 11:28:56 +0900 Subject: [PATCH 34/69] =?UTF-8?q?feat(template):=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=EC=97=90=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EB=B0=8F=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/template/domain/Template.java | 3 ++- .../codezap/template/domain/TemplateTag.java | 2 +- .../dto/request/UpdateTemplateRequest.java | 6 ++++- .../template/repository/TagRepository.java | 3 +++ .../repository/TemplateTagRepository.java | 2 ++ .../template/service/TemplateService.java | 14 ++++++++++- .../controller/TemplateControllerTest.java | 14 ++++++++--- .../template/service/TemplateServiceTest.java | 25 +++++++++++++++---- 8 files changed, 56 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index bd55d4103..5bdfa6446 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -35,7 +35,8 @@ public Template(String title, Category category) { this.category = category; } - public void updateTitle(String title) { + public void updateTitle(String title, Category category) { this.title = title; + this.category = category; } } diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java index 95214758d..eefb2ca0b 100644 --- a/backend/src/main/java/codezap/template/domain/TemplateTag.java +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -48,4 +48,4 @@ public TemplateTag(Template template, Tag tag) { this.template = template; this.tag = tag; } -} \ No newline at end of file +} diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java index 06e2cfcaf..16fd59c91 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java @@ -23,7 +23,11 @@ public record UpdateTemplateRequest( @Schema(description = "삭제한 스니펫 식별자") @NotNull(message = "deleteSnippetIds 리스트가 null 입니다.") - List deleteSnippetIds + List deleteSnippetIds, + + Long categoryId, + + List tags ) implements ValidatedSnippetsOrdinalRequest { @Override public List extractSnippetsOrdinal() { diff --git a/backend/src/main/java/codezap/template/repository/TagRepository.java b/backend/src/main/java/codezap/template/repository/TagRepository.java index e813662e8..612dee2b3 100644 --- a/backend/src/main/java/codezap/template/repository/TagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TagRepository.java @@ -5,4 +5,7 @@ import codezap.template.domain.Tag; public interface TagRepository extends JpaRepository { + boolean existsByName(String name); + + Tag findByName(String name); } diff --git a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java index b49d7355f..6b0ab4f6e 100644 --- a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java @@ -10,4 +10,6 @@ public interface TemplateTagRepository extends JpaRepository { List findAllByTemplate(Template template); + + void deleteAllByTemplate(Template template); } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 538ff606d..b610bed92 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -86,8 +86,9 @@ public FindTemplateByIdResponse findById(Long id) { @Transactional public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) { + Category category = categoryRepository.fetchById(updateTemplateRequest.categoryId()); Template template = templateRepository.fetchById(templateId); - template.updateTitle(updateTemplateRequest.title()); + template.updateTitle(updateTemplateRequest.title(), category); updateTemplateRequest.updateSnippets().forEach(this::updateSnippet); updateTemplateRequest.createSnippets() @@ -100,6 +101,17 @@ public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) } updateTemplateRequest.deleteSnippetIds().forEach(snippetRepository::deleteById); + + templateTagRepository.deleteAllByTemplate(template); + updateTemplateRequest.tags().stream() + .map(Tag::new) + .filter(tag -> !tagRepository.existsByName(tag.getName())) + .forEach(tagRepository::save); + + List tags = updateTemplateRequest.tags().stream() + .map(tagRepository::findByName) + .toList(); + tags.forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); } @Transactional diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index 47d11ab65..c45ac1c18 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -217,7 +217,8 @@ class updateTemplateTest { @DisplayName("템플릿 수정 성공") void updateTemplateSuccess() { // given - categoryService.create(new CreateCategoryRequest("category")); + categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category2")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -230,7 +231,9 @@ void updateTemplateSuccess() { List.of( new UpdateSnippetRequest(2L, "updateFilename2", "updateContent2", 1) ), - List.of(1L) + List.of(1L), + 2L, + List.of("tag1", "tag3") ); // when & then @@ -248,7 +251,8 @@ void updateTemplateSuccess() { @CsvSource({"1, 2, 1", "3, 2, 1", "0, 2, 1"}) void updateTemplateFailWithWrongSnippetOrdinal(int createOrdinal1, int createOrdinal2, int updateOrdinal) { // given - categoryService.create(new CreateCategoryRequest("category")); + categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category2")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); templateService.create(templateRequest); @@ -261,7 +265,9 @@ void updateTemplateFailWithWrongSnippetOrdinal(int createOrdinal1, int createOrd List.of( new UpdateSnippetRequest(2L, "updateFilename2", "updateContent2", updateOrdinal) ), - List.of(1L) + List.of(1L), + 2L, + List.of("tag1", "tag3") ); // when & then diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index 10d97d771..ba802e6b6 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -76,10 +76,15 @@ void createTemplateSuccess() { categoryRepository.save(new Category("category")); // when - templateService.create(createTemplateRequest); + Long id = templateService.create(createTemplateRequest); + Template template = templateRepository.fetchById(id); // then - assertThat(templateRepository.findAll()).hasSize(1); + assertAll( + () -> assertThat(templateRepository.findAll()).hasSize(1), + () -> assertThat(template.getTitle()).isEqualTo(createTemplateRequest.title()), + () -> assertThat(template.getCategory().getName()).isEqualTo("category") + ); } @Test @@ -122,18 +127,26 @@ void updateTemplateSuccess() { // given CreateTemplateRequest createdTemplate = makeTemplateRequest("title"); Template template = saveTemplate(createdTemplate); + categoryRepository.save(new Category("category2")); // when UpdateTemplateRequest updateTemplateRequest = makeUpdateTemplateRequest("updateTitle"); templateService.update(template.getId(), updateTemplateRequest); + Template updateTemplate = templateRepository.fetchById(template.getId()); List snippets = snippetRepository.findAllByTemplate(template); ThumbnailSnippet thumbnailSnippet = thumbnailSnippetRepository.findById(template.getId()).get(); + List tags = templateTagRepository.findAllByTemplate(updateTemplate).stream() + .map(TemplateTag::getTag) + .toList(); // then assertAll( - () -> assertThat(updateTemplateRequest.title()).isEqualTo("updateTitle"), + () -> assertThat(updateTemplate.getTitle()).isEqualTo("updateTitle"), () -> assertThat(thumbnailSnippet.getSnippet().getId()).isEqualTo(2L), - () -> assertThat(snippets).hasSize(3) + () -> assertThat(snippets).hasSize(3), + () -> assertThat(updateTemplate.getCategory().getId()).isEqualTo(2L), + () -> assertThat(tags).hasSize(2), + () -> assertThat(tags.get(1).getName()).isEqualTo("tag3") ); } @@ -177,7 +190,9 @@ private UpdateTemplateRequest makeUpdateTemplateRequest(String title) { List.of( new UpdateSnippetRequest(2L, "filename2", "content2", 1) ), - List.of(1L) + List.of(1L), + 2L, + List.of("tag1", "tag3") ); } From 7c6ff8772af2ab95797364e4073a42e9bc1e7682 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Thu, 1 Aug 2024 11:35:46 +0900 Subject: [PATCH 35/69] =?UTF-8?q?feat(template):=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/codezap/template/repository/TemplateTagRepository.java | 2 ++ .../src/main/java/codezap/template/service/TemplateService.java | 1 + 2 files changed, 3 insertions(+) diff --git a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java index 6b0ab4f6e..41fe972d3 100644 --- a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java @@ -12,4 +12,6 @@ public interface TemplateTagRepository extends JpaRepository List findAllByTemplate(Template template); void deleteAllByTemplate(Template template); + + void deleteAllByTemplateId(Long id); } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index b610bed92..e6f185b08 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -118,6 +118,7 @@ public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) public void deleteById(Long id) { thumbnailSnippetRepository.deleteByTemplateId(id); snippetRepository.deleteByTemplateId(id); + templateTagRepository.deleteAllByTemplateId(id); templateRepository.deleteById(id); } From d713868e1bc9a19680502b1fd2c496daf6992c31 Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 13:10:02 +0900 Subject: [PATCH 36/69] =?UTF-8?q?docs(category):=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20swagger=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 17 ++++++- .../SpringDocCategoryController.java | 49 +++++++++++++++++++ .../dto/request/CreateCategoryRequest.java | 3 ++ .../dto/request/UpdateCategoryRequest.java | 17 +++++++ .../response/FindAllCategoriesResponse.java | 2 + .../response/FindCategoryByIdResponse.java | 3 ++ 6 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java create mode 100644 backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 27e50a673..2acc801b4 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -5,19 +5,23 @@ import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.service.CategoryService; @RestController @RequestMapping("/categories") -public class CategoryController { +public class CategoryController implements SpringDocCategoryController{ private final CategoryService categoryService; @@ -35,4 +39,15 @@ public ResponseEntity createCategory(@Valid @RequestBody CreateCategoryReq public ResponseEntity getCategories() { return ResponseEntity.ok(categoryService.findAll()); } + + @PutMapping("/{id}") + public ResponseEntity updateCategory(@PathVariable Long id, + @Valid @RequestBody UpdateCategoryRequest updateCategoryRequest) { + return null; + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteCategory(@PathVariable Long id) { + return null; + } } diff --git a/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java new file mode 100644 index 000000000..621d23002 --- /dev/null +++ b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java @@ -0,0 +1,49 @@ +package codezap.category.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; +import codezap.category.dto.response.FindAllCategoriesResponse; +import codezap.global.swagger.error.ApiErrorResponse; +import codezap.global.swagger.error.ErrorCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.headers.Header; +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; + +@Tag(name = "카테고리 CRUD API", description = "카테고리 생성, 목록 조회, 삭제, 수정 API") +public interface SpringDocCategoryController { + + @Operation(summary = "카테고리 생성", description = """ + 새로운 카테고리를 생성합니다. \n + 새로운 카테고리의 이름이 필요합니다. \n + """) + @ApiResponse(responseCode = "201", description = "카테고리 생성 성공", headers = { + @Header(name = "생성된 카테고리의 API 경로", example = "/categories/1")}) + @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories", errorCases = { + @ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "카테고리 이름이 null 입니다."), + @ErrorCase(description = "카테고리 이름이 255자를 초과한 경우", exampleMessage = "카테고리 이름은 최대 255자까지 입력 가능합니다.") + }) + ResponseEntity createCategory(CreateCategoryRequest createCategoryRequest); + + @Operation(summary = "카테고리 목록 조회", description = "생성된 모든 카테고리를 조회합니다.") + @ApiResponse(responseCode = "200", description = "조회 성공", + content = {@Content(schema = @Schema(implementation = FindAllCategoriesResponse.class))}) + ResponseEntity getCategories(); + + @Operation(summary = "카테고리 수정", description = "해당하는 식별자의 카테고리를 수정합니다.") + @ApiResponse(responseCode = "200", description = "카테고리 수정 성공") + @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories/1", errorCases = { + @ErrorCase(description = "해당하는 id 값인 카테고리가 없는 경우", + exampleMessage = "식별자 1에 해당하는 카테고리가 존재하지 않습니다."), + }) + ResponseEntity updateCategory(Long id, UpdateCategoryRequest updateCategoryRequest); + + @Operation(summary = "카테고리 삭제", description = "해당하는 식별자의 카테고리를 삭제합니다.") + @ApiResponse(responseCode = "204", description = "카테고리 삭제 성공") + ResponseEntity deleteCategory(Long id); +} diff --git a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java index 7d1276e94..86946b8a8 100644 --- a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java @@ -3,7 +3,10 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import io.swagger.v3.oas.annotations.media.Schema; + public record CreateCategoryRequest( + @Schema(description = "카테고리 이름", example = "Spring") @NotNull(message = "카테고리 이름이 null 입니다.") @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") String name diff --git a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java new file mode 100644 index 000000000..5a8bfb467 --- /dev/null +++ b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java @@ -0,0 +1,17 @@ +package codezap.category.dto.request; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record UpdateCategoryRequest( + @Schema(description = "카테고리 식별자", example = "1") + @NotNull(message = "카테고리 id가 null 입니다.") + Long id, + @Schema(description = "카테고리 이름", example = "Spring") + @NotNull(message = "카테고리 이름이 null 입니다.") + @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") + String name +) { +} diff --git a/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java index e7df7c5a6..b17e2168f 100644 --- a/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java +++ b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java @@ -3,8 +3,10 @@ import java.util.List; import codezap.category.domain.Category; +import io.swagger.v3.oas.annotations.media.Schema; public record FindAllCategoriesResponse( + @Schema(description = "카테고리 목록") List categories ) { public static FindAllCategoriesResponse from(List categories) { diff --git a/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java b/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java index fe7909aec..999a7a46a 100644 --- a/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java +++ b/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java @@ -1,9 +1,12 @@ package codezap.category.dto.response; import codezap.category.domain.Category; +import io.swagger.v3.oas.annotations.media.Schema; public record FindCategoryByIdResponse( + @Schema(description = "카테고리 식별자", example = "1") Long id, + @Schema(description = "카테고리 이름", example = "Spring") String name ) { public static FindCategoryByIdResponse from(Category category) { From 1b8b6a175a30f7101ee2b77965fdc3d6462b824f Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 18:29:20 +0900 Subject: [PATCH 37/69] =?UTF-8?q?docs(category):=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 3 +- .../codezap/category/domain/Category.java | 4 ++ .../dto/request/UpdateCategoryRequest.java | 3 - .../category/service/CategoryService.java | 11 ++++ .../controller/CategoryControllerTest.java | 61 ++++++++++++++++++- .../category/service/CategoryServiceTest.java | 16 ++++- 6 files changed, 92 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 2acc801b4..311ed556f 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -43,7 +43,8 @@ public ResponseEntity getCategories() { @PutMapping("/{id}") public ResponseEntity updateCategory(@PathVariable Long id, @Valid @RequestBody UpdateCategoryRequest updateCategoryRequest) { - return null; + categoryService.update(id, updateCategoryRequest); + return ResponseEntity.ok().build(); } @DeleteMapping("/{id}") diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index 5ffb71ed2..b44c07ddb 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -24,4 +24,8 @@ public class Category extends BaseTimeEntity { public Category(String name) { this.name = name; } + + public void updateName(String name) { + this.name = name; + } } diff --git a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java index 5a8bfb467..4ed48bf0c 100644 --- a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java @@ -6,9 +6,6 @@ import io.swagger.v3.oas.annotations.media.Schema; public record UpdateCategoryRequest( - @Schema(description = "카테고리 식별자", example = "1") - @NotNull(message = "카테고리 id가 null 입니다.") - Long id, @Schema(description = "카테고리 이름", example = "Spring") @NotNull(message = "카테고리 이름이 null 입니다.") @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index e4c59e5de..151a63945 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -6,6 +6,7 @@ import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; @@ -32,4 +33,14 @@ public Long create(CreateCategoryRequest createCategoryRequest) { public FindAllCategoriesResponse findAll() { return FindAllCategoriesResponse.from(categoryRepository.findAll()); } + + @Transactional + public void update(Long id, UpdateCategoryRequest updateCategoryRequest) { + if (categoryRepository.existsByName(updateCategoryRequest.name())) { + throw new CodeZapException(HttpStatus.CONFLICT, + "이름이 " + updateCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); + } + Category category = categoryRepository.fetchById(id); + category.updateName(updateCategoryRequest.name()); + } } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index 16f519f83..18f66462d 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -14,6 +14,7 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.service.CategoryService; import io.restassured.RestAssured; import io.restassured.http.ContentType; @@ -83,4 +84,62 @@ void findAllCategoriesSuccess() { .statusCode(200) .body("categories.size()", is(2)); } -} \ No newline at end of file + + @Nested + @DisplayName("카테고리 수정 테스트") + class updateCategoryTest { + + Long savedCategoryId; + + @BeforeEach + void saveCategory() { + savedCategoryId = categoryService.create(new CreateCategoryRequest("category1")); + } + + @Test + @DisplayName("카테고리 수정 성공") + void updateCategorySuccess() { + UpdateCategoryRequest updateCategoryRequest = new UpdateCategoryRequest("a".repeat(MAX_LENGTH)); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(updateCategoryRequest) + .when().put("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(200); + } + + @Test + @DisplayName("카테고리 수정 실패: 카테고리 이름 길이 초과") + void updateCategoryFailWithLongName() { + UpdateCategoryRequest updateCategoryRequest = new UpdateCategoryRequest("a".repeat(MAX_LENGTH + 1)); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(updateCategoryRequest) + .when().put("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(400) + .body("detail", is("카테고리 이름은 최대 255자까지 입력 가능합니다.")); + } + + @Test + @DisplayName("카테고리 수정 실패: 중복된 이름의 카테고리 존재") + void updateCategoryFailWithDuplicatedName() { + //given + String duplicatedName = "duplicatedName"; + categoryService.create(new CreateCategoryRequest(duplicatedName)); + + UpdateCategoryRequest createCategoryRequest = new UpdateCategoryRequest(duplicatedName); + + //when & then + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(createCategoryRequest) + .when().put("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(409) + .body("detail", is("이름이 " + duplicatedName + "인 카테고리가 이미 존재하고 있습니다.")); + } + } +} diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 944a184af..6dab5569f 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -16,6 +16,7 @@ import codezap.category.domain.Category; import codezap.category.dto.request.CreateCategoryRequest; +import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; @@ -75,4 +76,17 @@ void findAllCategoriesSuccess() { assertThat(findAllCategoriesResponse.categories()).hasSize(2); } -} \ No newline at end of file + + @Test + @DisplayName("카테고리 수정 성공") + void updateCategorySuccess() { + //given + Category savedCategory = categoryRepository.save(new Category("category1")); + + //when + categoryService.update(savedCategory.getId(), new UpdateCategoryRequest("updateName")); + + //then + assertThat(categoryRepository.fetchById(savedCategory.getId()).getName()).isEqualTo("updateName"); + } +} From 03eb850ba5b82df480c6643cfcde43bea263fd24 Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 18:30:00 +0900 Subject: [PATCH 38/69] =?UTF-8?q?refactor(controller):=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EB=B0=8F=20DisplayName=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/category/controller/CategoryControllerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index 18f66462d..b80149a6e 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -55,8 +55,8 @@ void createCategorySuccess() { } @Test - @DisplayName("카테고리 생성 실패:") - void createCategoryFail() { + @DisplayName("카테고리 생성 실패: 카테고리 이름 길이 초과") + void createCategoryFailWithLongName() { CreateCategoryRequest createCategoryRequest = new CreateCategoryRequest("a".repeat(MAX_LENGTH + 1)); RestAssured.given().log().all() From ba022159424b9e7aed8456d788ee6d86adbd668f Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 20:55:24 +0900 Subject: [PATCH 39/69] =?UTF-8?q?feat(category):=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 4 +- .../category/service/CategoryService.java | 27 ++++++--- .../repository/TemplateRepository.java | 2 + .../controller/CategoryControllerTest.java | 57 +++++++++++++++++++ .../category/service/CategoryServiceTest.java | 13 +++++ 5 files changed, 94 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 311ed556f..ee6179d34 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -49,6 +49,8 @@ public ResponseEntity updateCategory(@PathVariable Long id, @DeleteMapping("/{id}") public ResponseEntity deleteCategory(@PathVariable Long id) { - return null; + categoryService.deleteById(id); + return ResponseEntity.noContent() + .build(); } } diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 151a63945..0e84c0801 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -10,22 +10,23 @@ import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.repository.CategoryRepository; import codezap.global.exception.CodeZapException; +import codezap.template.repository.TemplateRepository; @Service public class CategoryService { private final CategoryRepository categoryRepository; + private final TemplateRepository templateRepository; - public CategoryService(CategoryRepository categoryRepository) { + public CategoryService(CategoryRepository categoryRepository, TemplateRepository templateRepository) { this.categoryRepository = categoryRepository; + this.templateRepository = templateRepository; } @Transactional public Long create(CreateCategoryRequest createCategoryRequest) { String categoryName = createCategoryRequest.name(); - if (categoryRepository.existsByName(categoryName)) { - throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); - } + validateDuplicatedCategory(categoryName); Category category = new Category(categoryName); return categoryRepository.save(category).getId(); } @@ -36,11 +37,21 @@ public FindAllCategoriesResponse findAll() { @Transactional public void update(Long id, UpdateCategoryRequest updateCategoryRequest) { - if (categoryRepository.existsByName(updateCategoryRequest.name())) { - throw new CodeZapException(HttpStatus.CONFLICT, - "이름이 " + updateCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); - } + validateDuplicatedCategory(updateCategoryRequest.name()); Category category = categoryRepository.fetchById(id); category.updateName(updateCategoryRequest.name()); } + + public void deleteById(Long id) { + if (templateRepository.existsByCategoryId(id)) { + throw new CodeZapException(HttpStatus.BAD_REQUEST, "템플릿이 존재하는 카테고리는 삭제할 수 없습니다."); + } + categoryRepository.deleteById(id); + } + + private void validateDuplicatedCategory(String categoryName) { + if (categoryRepository.existsByName(categoryName)) { + throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); + } + } } diff --git a/backend/src/main/java/codezap/template/repository/TemplateRepository.java b/backend/src/main/java/codezap/template/repository/TemplateRepository.java index 47b00b246..127eef4cf 100644 --- a/backend/src/main/java/codezap/template/repository/TemplateRepository.java +++ b/backend/src/main/java/codezap/template/repository/TemplateRepository.java @@ -12,4 +12,6 @@ default Template fetchById(Long id) { return findById(id).orElseThrow( () -> new CodeZapException(HttpStatus.NOT_FOUND, "식별자 " + id + "에 해당하는 템플릿이 존재하지 않습니다.")); } + + boolean existsByCategoryId(Long categoryId); } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index b80149a6e..ec3d83feb 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -2,6 +2,8 @@ import static org.hamcrest.Matchers.is; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -16,6 +18,9 @@ import codezap.category.dto.request.CreateCategoryRequest; import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.service.CategoryService; +import codezap.template.dto.request.CreateSnippetRequest; +import codezap.template.dto.request.CreateTemplateRequest; +import codezap.template.service.TemplateService; import io.restassured.RestAssured; import io.restassured.http.ContentType; @@ -31,6 +36,8 @@ class CategoryControllerTest { @Autowired private CategoryService categoryService; + @Autowired + private TemplateService templateService; @BeforeEach void setting() { @@ -142,4 +149,54 @@ void updateCategoryFailWithDuplicatedName() { .body("detail", is("이름이 " + duplicatedName + "인 카테고리가 이미 존재하고 있습니다.")); } } + + + @Nested + @DisplayName("카테고리 삭제 테스트") + class deleteCategoryTest { + + Long savedCategoryId; + + @BeforeEach + void saveCategory() { + savedCategoryId = categoryService.create(new CreateCategoryRequest("category1")); + } + + @Test + @DisplayName("카테고리 삭제 성공") + void deleteCategorySuccess() { + RestAssured.given().log().all() + .when().delete("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(204); + } + + @Test + @DisplayName("카테고리 수정 성공: 존재하지 않는 카테고리의 삭제 요청") + void updateCategoryFailWithDuplicatedName() { + RestAssured.given().log().all() + .when().delete("/categories/" + savedCategoryId + 1) + .then().log().all() + .statusCode(204); + } + + @Test + @DisplayName("카테고리 삭제 실패: 템플릿이 존재하는 카테고리는 삭제 불가능") + void updateCategoryFailWithLongName() { + //given + templateService.create(new CreateTemplateRequest( + "title", + List.of(new CreateSnippetRequest("filename", "content", 1)), + savedCategoryId, + List.of("tag1", "tag2") + )); + + //when & then + RestAssured.given().log().all() + .when().delete("/categories/" + savedCategoryId) + .then().log().all() + .statusCode(400) + .body("detail", is("템플릿이 존재하는 카테고리는 삭제할 수 없습니다.")); + } + } } diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 6dab5569f..d80d23413 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -89,4 +89,17 @@ void updateCategorySuccess() { //then assertThat(categoryRepository.fetchById(savedCategory.getId()).getName()).isEqualTo("updateName"); } + + @Test + @DisplayName("카테고리 삭제 성공") + void deleteCategorySuccess() { + //given + Category savedCategory = categoryRepository.save(new Category("category1")); + + //when + categoryService.deleteById(savedCategory.getId()); + + //then + assertThat(categoryRepository.findById(savedCategory.getId())).isEmpty(); + } } From ebab110693bb1b6694259a07e15f8e47c8c384f7 Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 1 Aug 2024 21:01:55 +0900 Subject: [PATCH 40/69] =?UTF-8?q?docs(category):=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EB=AC=B8=EC=84=9C=ED=99=94=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 존재하고 있습니다 -> 존재합니다 로 변경 - 응답에 중복된 이름에 대한 예외 추가 --- .../controller/SpringDocCategoryController.java | 10 +++++++++- .../java/codezap/category/service/CategoryService.java | 2 +- .../category/controller/CategoryControllerTest.java | 2 +- .../codezap/category/service/CategoryServiceTest.java | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java index 621d23002..f3ad05bdf 100644 --- a/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java +++ b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java @@ -26,7 +26,9 @@ public interface SpringDocCategoryController { @Header(name = "생성된 카테고리의 API 경로", example = "/categories/1")}) @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories", errorCases = { @ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "카테고리 이름이 null 입니다."), - @ErrorCase(description = "카테고리 이름이 255자를 초과한 경우", exampleMessage = "카테고리 이름은 최대 255자까지 입력 가능합니다.") + @ErrorCase(description = "카테고리 이름이 255자를 초과한 경우", exampleMessage = "카테고리 이름은 최대 255자까지 입력 가능합니다."), + @ErrorCase(description = "동일한 이름의 카테고리가 존재하는 경우", + exampleMessage = "이름이 Spring 인 카테고리가 이미 존재합니다.") }) ResponseEntity createCategory(CreateCategoryRequest createCategoryRequest); @@ -40,10 +42,16 @@ public interface SpringDocCategoryController { @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories/1", errorCases = { @ErrorCase(description = "해당하는 id 값인 카테고리가 없는 경우", exampleMessage = "식별자 1에 해당하는 카테고리가 존재하지 않습니다."), + @ErrorCase(description = "동일한 이름의 카테고리가 존재하는 경우", + exampleMessage = "이름이 Spring 인 카테고리가 이미 존재합니다.") }) ResponseEntity updateCategory(Long id, UpdateCategoryRequest updateCategoryRequest); @Operation(summary = "카테고리 삭제", description = "해당하는 식별자의 카테고리를 삭제합니다.") @ApiResponse(responseCode = "204", description = "카테고리 삭제 성공") + @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories/1", errorCases = { + @ErrorCase(description = "삭제하려는 카테고리에 템플릿이 존재하는 경우", + exampleMessage = "템플릿이 존재하는 카테고리는 삭제할 수 없습니다."), + }) ResponseEntity deleteCategory(Long id); } diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 0e84c0801..48a9f5191 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -51,7 +51,7 @@ public void deleteById(Long id) { private void validateDuplicatedCategory(String categoryName) { if (categoryRepository.existsByName(categoryName)) { - throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재하고 있습니다."); + throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재합니다."); } } } diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index ec3d83feb..71b21afd3 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -146,7 +146,7 @@ void updateCategoryFailWithDuplicatedName() { .when().put("/categories/" + savedCategoryId) .then().log().all() .statusCode(409) - .body("detail", is("이름이 " + duplicatedName + "인 카테고리가 이미 존재하고 있습니다.")); + .body("detail", is("이름이 " + duplicatedName + "인 카테고리가 이미 존재합니다.")); } } diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index d80d23413..11a811216 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -62,7 +62,7 @@ void createCategoryFailWithDuplicateName() { assertThatThrownBy(() -> categoryService.create(createCategoryRequest)) .isInstanceOf(CodeZapException.class) - .hasMessage("이름이 " + createCategoryRequest.name() + "인 카테고리가 이미 존재하고 있습니다."); + .hasMessage("이름이 " + createCategoryRequest.name() + "인 카테고리가 이미 존재합니다."); } } From c087ac389e05dd6cdf2a1a09fd77b9f6dc5ebf72 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Thu, 1 Aug 2024 16:41:10 +0900 Subject: [PATCH 41/69] =?UTF-8?q?feat(template):=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20dto=EC=97=90=20description=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/template/domain/Template.java | 6 +++-- .../dto/request/CreateTemplateRequest.java | 2 ++ .../dto/request/UpdateTemplateRequest.java | 2 ++ .../response/FindTemplateByIdResponse.java | 3 +++ .../template/service/TemplateService.java | 10 ++++++-- .../controller/TemplateControllerTest.java | 25 ++++++++++++++----- .../repository/SnippetRepositoryTest.java | 4 +-- .../template/service/TemplateServiceTest.java | 6 +++-- 8 files changed, 44 insertions(+), 14 deletions(-) diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index 5bdfa6446..75aff1906 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -30,13 +30,15 @@ public class Template extends BaseTimeEntity { @ManyToOne(optional = false) private Category category; - public Template(String title, Category category) { + public Template(String title, String description, Category category) { this.title = title; + this.description = description; this.category = category; } - public void updateTitle(String title, Category category) { + public void updateTemplate(String title, String description, Category category) { this.title = title; + this.description = description; this.category = category; } } diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index 9527ff657..4b9fac6fd 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -15,6 +15,8 @@ public record CreateTemplateRequest( @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.") String title, + String description, + @Schema(description = "템플릿의 스니펫 내역") @NotNull(message = "스니펫 리스트가 null 입니다.") @Valid diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java index 16fd59c91..25860a00f 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java @@ -13,6 +13,8 @@ public record UpdateTemplateRequest( @NotNull(message = "템플릿 이름이 null 입니다.") String title, + String description, + @Schema(description = "새로 추가한 스니펫 내역") @NotNull(message = "createSnippets 리스트가 null 입니다.") List createSnippets, diff --git a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java index 734741750..272b2c60b 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java @@ -16,6 +16,8 @@ public record FindTemplateByIdResponse( @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") String title, + String description, + @Schema(description = "스니펫 목록") List snippets, @@ -30,6 +32,7 @@ public static FindTemplateByIdResponse of(Template template, List snipp return new FindTemplateByIdResponse( template.getId(), template.getTitle(), + template.getDescription(), mapToFindAllSnippetByTemplateResponse(snippets), FindCategoryByIdResponse.from(template.getCategory()), mapToFindTagByTemplateResponse(tags), diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index e6f185b08..d8f2e6fd3 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -54,7 +54,13 @@ public TemplateService(ThumbnailSnippetRepository thumbnailSnippetRepository, public Long create(CreateTemplateRequest createTemplateRequest) { Category category = categoryRepository.fetchById(createTemplateRequest.categoryId()); - Template template = templateRepository.save(new Template(createTemplateRequest.title(), category)); + Template template = templateRepository.save( + new Template( + createTemplateRequest.title(), + createTemplateRequest.description(), + category + ) + ); List tags = createTemplateRequest.tags().stream() .map(Tag::new) @@ -88,7 +94,7 @@ public FindTemplateByIdResponse findById(Long id) { public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) { Category category = categoryRepository.fetchById(updateTemplateRequest.categoryId()); Template template = templateRepository.fetchById(templateId); - template.updateTitle(updateTemplateRequest.title(), category); + template.updateTemplate(updateTemplateRequest.title(), updateTemplateRequest.description(), category); updateTemplateRequest.updateSnippets().forEach(this::updateSnippet); updateTemplateRequest.createSnippets() diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index c45ac1c18..43f56fa3a 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -58,7 +58,9 @@ class createTemplateTest { void createTemplateSuccess(String repeatTarget, int maxLength) { String maxTitle = "a".repeat(MAX_LENGTH); categoryService.create(new CreateCategoryRequest("category")); - CreateTemplateRequest templateRequest = new CreateTemplateRequest(maxTitle, + CreateTemplateRequest templateRequest = new CreateTemplateRequest( + maxTitle, + "description", List.of(new CreateSnippetRequest("a".repeat(MAX_LENGTH), repeatTarget.repeat(maxLength), 1)), 1L, List.of("tag1", "tag2") @@ -78,7 +80,9 @@ void createTemplateSuccess(String repeatTarget, int maxLength) { void createTemplateFailWithLongTitle() { String exceededTitle = "a".repeat(MAX_LENGTH + 1); categoryService.create(new CreateCategoryRequest("category")); - CreateTemplateRequest templateRequest = new CreateTemplateRequest(exceededTitle, + CreateTemplateRequest templateRequest = new CreateTemplateRequest( + exceededTitle, + "description", List.of(new CreateSnippetRequest("a", "content", 1)), 1L, List.of("tag1", "tag2") @@ -98,7 +102,9 @@ void createTemplateFailWithLongTitle() { void createTemplateFailWithLongFileName() { String exceededTitle = "a".repeat(MAX_LENGTH + 1); categoryService.create(new CreateCategoryRequest("category")); - CreateTemplateRequest templateRequest = new CreateTemplateRequest("title", + CreateTemplateRequest templateRequest = new CreateTemplateRequest( + "title", + "description", List.of(new CreateSnippetRequest(exceededTitle, "content", 1)), 1L, List.of("tag1", "tag2") @@ -118,7 +124,9 @@ void createTemplateFailWithLongFileName() { @CsvSource({"a, 65536", "ㄱ, 21846"}) void createTemplateFailWithLongContent(String repeatTarget, int exceededLength) { categoryService.create(new CreateCategoryRequest("category")); - CreateTemplateRequest templateRequest = new CreateTemplateRequest("title", + CreateTemplateRequest templateRequest = new CreateTemplateRequest( + "title", + "description", List.of(new CreateSnippetRequest("title", repeatTarget.repeat(exceededLength), 1)), 1L, List.of("tag1", "tag2") @@ -138,7 +146,9 @@ void createTemplateFailWithLongContent(String repeatTarget, int exceededLength) @CsvSource({"0, 1", "1, 3", "2, 1"}) void createTemplateFailWithWrongSnippetOrdinal(int firstIndex, int secondIndex) { categoryService.create(new CreateCategoryRequest("category")); - CreateTemplateRequest templateRequest = new CreateTemplateRequest("title", + CreateTemplateRequest templateRequest = new CreateTemplateRequest( + "title", + "description", List.of(new CreateSnippetRequest("title", "content", firstIndex), new CreateSnippetRequest("title", "content", secondIndex)), 1L, @@ -159,9 +169,9 @@ void createTemplateFailWithWrongSnippetOrdinal(int firstIndex, int secondIndex) @DisplayName("템플릿 전체 조회 성공") void findAllTemplatesSuccess() { // given + categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest1 = createTemplateRequestWithTwoSnippets("title1"); CreateTemplateRequest templateRequest2 = createTemplateRequestWithTwoSnippets("title2"); - categoryService.create(new CreateCategoryRequest("category")); templateService.create(templateRequest1); templateService.create(templateRequest2); @@ -224,6 +234,7 @@ void updateTemplateSuccess() { UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( "updateTitle", + "description", List.of( new CreateSnippetRequest("filename3", "content3", 2), new CreateSnippetRequest("filename4", "content4", 3) @@ -258,6 +269,7 @@ void updateTemplateFailWithWrongSnippetOrdinal(int createOrdinal1, int createOrd UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( "updateTitle", + "description", List.of( new CreateSnippetRequest("filename3", "content3", createOrdinal1), new CreateSnippetRequest("filename4", "content4", createOrdinal2) @@ -315,6 +327,7 @@ void deleteTemplateSuccessWithNotFoundTemplate() { private static CreateTemplateRequest createTemplateRequestWithTwoSnippets(String title) { CreateTemplateRequest templateRequest = new CreateTemplateRequest( title, + "description", List.of( new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2) diff --git a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java index 075ee12c7..5ce655dad 100644 --- a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java +++ b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java @@ -35,7 +35,7 @@ class SnippetRepositoryTest { @DisplayName("단일 스니펫 찾기 성공: 템플릿과 순서") void findOneSnippetSuccessWithTemplateAndOrdinal() { Category category = categoryRepository.save(new Category("category")); - Template template = templateRepository.save(new Template("title", category)); + Template template = templateRepository.save(new Template("title", "description", category)); Snippet snippet1 = snippetRepository.save(new Snippet(template, "filename1", "content1", 1)); Snippet snippet2 = snippetRepository.save(new Snippet(template, "filename2", "content2", 2)); @@ -53,7 +53,7 @@ void findOneSnippetSuccessWithTemplateAndOrdinal() { @DisplayName("스니펫 리스트 찾기 성공: 템플릿과 순서") void findSnippetsSuccessWithTemplateAndOrdinal() { Category category = categoryRepository.save(new Category("category")); - Template template = templateRepository.save(new Template("title", category)); + Template template = templateRepository.save(new Template("title","description", category)); Snippet snippet1 = snippetRepository.save(new Snippet(template, "filename1", "content1", 1)); Snippet snippet2 = snippetRepository.save(new Snippet(template, "filename2", "content2", 2)); Snippet snippet3 = snippetRepository.save(new Snippet(template, "filename3", "content3", 2)); diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index ba802e6b6..1e5bd7b18 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -72,8 +72,8 @@ void setting() { @DisplayName("템플릿 생성 성공") void createTemplateSuccess() { // given - CreateTemplateRequest createTemplateRequest = makeTemplateRequest("title"); categoryRepository.save(new Category("category")); + CreateTemplateRequest createTemplateRequest = makeTemplateRequest("title"); // when Long id = templateService.create(createTemplateRequest); @@ -171,6 +171,7 @@ void deleteTemplateSuccess() { private CreateTemplateRequest makeTemplateRequest(String title) { return new CreateTemplateRequest( title, + "description", List.of( new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2) @@ -183,6 +184,7 @@ private CreateTemplateRequest makeTemplateRequest(String title) { private UpdateTemplateRequest makeUpdateTemplateRequest(String title) { return new UpdateTemplateRequest( title, + "description", List.of( new CreateSnippetRequest("filename3", "content3", 2), new CreateSnippetRequest("filename4", "content4", 3) @@ -198,7 +200,7 @@ private UpdateTemplateRequest makeUpdateTemplateRequest(String title) { private Template saveTemplate(CreateTemplateRequest createTemplateRequest) { Category category = categoryRepository.save(new Category("category")); - Template savedTemplate = templateRepository.save(new Template(createTemplateRequest.title(), category)); + Template savedTemplate = templateRepository.save(new Template(createTemplateRequest.title(), createTemplateRequest.description(), category)); Snippet savedFirstSnippet = snippetRepository.save(new Snippet(savedTemplate, "filename1", "content1", 1)); snippetRepository.save(new Snippet(savedTemplate, "filename2", "content2", 2)); thumbnailSnippetRepository.save(new ThumbnailSnippet(savedTemplate, savedFirstSnippet)); From 5ecab426fd4413451080a6ef6e01476df0642100 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Thu, 1 Aug 2024 16:41:37 +0900 Subject: [PATCH 42/69] =?UTF-8?q?feat(cors):=20cors=20=ED=97=A4=EB=8D=94?= =?UTF-8?q?=20=EA=B6=8C=ED=95=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/codezap/global/cors/WebCorsConfiguration.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/codezap/global/cors/WebCorsConfiguration.java b/backend/src/main/java/codezap/global/cors/WebCorsConfiguration.java index b69d3863d..d451e9840 100644 --- a/backend/src/main/java/codezap/global/cors/WebCorsConfiguration.java +++ b/backend/src/main/java/codezap/global/cors/WebCorsConfiguration.java @@ -10,6 +10,7 @@ public class WebCorsConfiguration implements WebMvcConfigurer { public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") - .allowedMethods("*"); + .allowedMethods("*") + .exposedHeaders("*"); } } From 96e8aae607acb958c26d26ac42e8ddc381b0cf7b Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Fri, 2 Aug 2024 11:56:28 +0900 Subject: [PATCH 43/69] =?UTF-8?q?docs(codezap):=20template=20=EB=B0=8F=20c?= =?UTF-8?q?ategory=20=EB=AC=B8=EC=84=9C=ED=99=94=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/controller/SpringDocTemplateController.java | 4 ++-- .../codezap/template/dto/request/CreateTemplateRequest.java | 3 +++ .../codezap/template/dto/request/UpdateTemplateRequest.java | 3 +++ .../dto/response/FindAllSnippetByTemplateResponse.java | 3 +++ .../codezap/template/dto/response/FindTagByIdResponse.java | 4 ++++ .../template/dto/response/FindTemplateByIdResponse.java | 3 +++ .../template/dto/response/FindThumbnailSnippetResponse.java | 1 + .../codezap/category/controller/CategoryControllerTest.java | 1 + 8 files changed, 20 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java b/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java index 99f1a6bdb..f125a25f6 100644 --- a/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java +++ b/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java @@ -21,7 +21,7 @@ public interface SpringDocTemplateController { @Operation(summary = "템플릿 생성", description = """ 새로운 템플릿을 생성합니다. \n - 템플릿의 제목, 썸네일 스니펫의 순서, 스니펫 목록이 필요합니다. \n + 템플릿의 제목, 썸네일 스니펫의 순서, 스니펫 목록, 카테고리 ID, 태그 목록이 필요합니다. \n 스니펫 목록은 파일 이름, 소스 코드, 해당 스니펫의 순서가 필요합니다. \n * 썸네일 스니펫은 1로 고정입니다. (2024.07.15 기준) \n * 모든 스니펫 순서는 1부터 시작합니다. \n @@ -31,7 +31,7 @@ public interface SpringDocTemplateController { @Header(name = "생성된 템플릿의 API 경로", example = "/templates/1")}) @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/templates/1", errorCases = { @ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "템플릿 이름 null 입니다."), - @ErrorCase(description = "제목 또는 스니펫 파일명이 255자를 초과한 경우", exampleMessage = "제목은 최대 255자까지 입력 가능합니다."), + @ErrorCase(description = "제목 또는 스니펫 파일 또는 태그 이름이 255자를 초과한 경우", exampleMessage = "제목은 최대 255자까지 입력 가능합니다."), @ErrorCase(description = "썸네일 스니펫의 순서가 1이 아닌 경우", exampleMessage = "썸네일 스니펫의 순서가 잘못되었습니다."), @ErrorCase(description = "스니펫 순서가 잘못된 경우", exampleMessage = "스니펫 순서가 잘못되었습니다."), @ErrorCase(description = "스니펫 내용 65,535 byte를 초과한 경우", exampleMessage = "파일 내용은 최대 65,535 byte까지 입력 가능합니다.") diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index 4b9fac6fd..b9216a053 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -15,6 +15,7 @@ public record CreateTemplateRequest( @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.") String title, + @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") String description, @Schema(description = "템플릿의 스니펫 내역") @@ -22,8 +23,10 @@ public record CreateTemplateRequest( @Valid List snippets, + @Schema(description = "카테고리 ID", example = "1") Long categoryId, + @Schema(description = "태그 리스트") List tags ) implements ValidatedSnippetsOrdinalRequest { @Override diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java index 25860a00f..b24948a7a 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java @@ -13,6 +13,7 @@ public record UpdateTemplateRequest( @NotNull(message = "템플릿 이름이 null 입니다.") String title, + @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") String description, @Schema(description = "새로 추가한 스니펫 내역") @@ -27,8 +28,10 @@ public record UpdateTemplateRequest( @NotNull(message = "deleteSnippetIds 리스트가 null 입니다.") List deleteSnippetIds, + @Schema(description = "카테고리 ID", example = "1") Long categoryId, + @Schema(description = "태그 리스트") List tags ) implements ValidatedSnippetsOrdinalRequest { @Override diff --git a/backend/src/main/java/codezap/template/dto/response/FindAllSnippetByTemplateResponse.java b/backend/src/main/java/codezap/template/dto/response/FindAllSnippetByTemplateResponse.java index 34815a2f7..d2520a934 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindAllSnippetByTemplateResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindAllSnippetByTemplateResponse.java @@ -6,10 +6,13 @@ public record FindAllSnippetByTemplateResponse( @Schema(description = "파일 식별자", example = "0") Long id, + @Schema(description = "파일 이름", example = "Main.java") String filename, + @Schema(description = "소스 코드", example = "public class Main { // ...") String content, + @Schema(description = "스니펫의 순서", example = "1") int ordinal ) { diff --git a/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java index 8052780b0..60d6a9173 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java @@ -1,9 +1,13 @@ package codezap.template.dto.response; import codezap.template.domain.Tag; +import io.swagger.v3.oas.annotations.media.Schema; public record FindTagByIdResponse( + @Schema(description = "태그 식별자", example = "1") Long id, + + @Schema(description = "태그 이름", example = "스프링") String name ) { public static FindTagByIdResponse from(Tag tag) { diff --git a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java index 272b2c60b..b9451b777 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java @@ -16,13 +16,16 @@ public record FindTemplateByIdResponse( @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") String title, + @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") String description, @Schema(description = "스니펫 목록") List snippets, + @Schema(description = "카테고리 정보") FindCategoryByIdResponse category, + @Schema(description = "태그 목록") List tags, @Schema(description = "템플릿 수정 시간", example = "2024-11-11 12:00", type = "string") diff --git a/backend/src/main/java/codezap/template/dto/response/FindThumbnailSnippetResponse.java b/backend/src/main/java/codezap/template/dto/response/FindThumbnailSnippetResponse.java index 1cdf7303e..de47fbe4f 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindThumbnailSnippetResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindThumbnailSnippetResponse.java @@ -6,6 +6,7 @@ public record FindThumbnailSnippetResponse( @Schema(description = "파일 이름", example = "Main.java") String filename, + @Schema(description = "목록 조회 시 보여질 코드", example = "public class Main { // ...") String thumbnailContent ) { diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index 71b21afd3..0f79d6da4 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -186,6 +186,7 @@ void updateCategoryFailWithLongName() { //given templateService.create(new CreateTemplateRequest( "title", + "description", List.of(new CreateSnippetRequest("filename", "content", 1)), savedCategoryId, List.of("tag1", "tag2") From bc2fc49977da4430fe49b19caafc9b97b1b8d9f0 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Fri, 2 Aug 2024 14:04:17 +0900 Subject: [PATCH 44/69] =?UTF-8?q?refactor(template):=20dto=20=EC=A0=9C?= =?UTF-8?q?=EC=95=BD=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CreateTemplateRequest.java | 5 + .../dto/request/UpdateTemplateRequest.java | 10 ++ .../controller/TemplateControllerTest.java | 164 +++++++++++++++++- 3 files changed, 178 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index b9216a053..b7e217fb0 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -6,6 +6,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import codezap.global.validation.ByteLength; import codezap.template.dto.request.validation.ValidatedSnippetsOrdinalRequest; import io.swagger.v3.oas.annotations.media.Schema; @@ -16,6 +17,8 @@ public record CreateTemplateRequest( String title, @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") + @NotNull(message = "템플릿 설명이 null 입니다.") + @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.") String description, @Schema(description = "템플릿의 스니펫 내역") @@ -24,9 +27,11 @@ public record CreateTemplateRequest( List snippets, @Schema(description = "카테고리 ID", example = "1") + @NotNull(message = "카테고리 id가 null 입니다.") Long categoryId, @Schema(description = "태그 리스트") + @NotNull(message = "태그 리스트가 null 입니다.") List tags ) implements ValidatedSnippetsOrdinalRequest { @Override diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java index b24948a7a..52ebfbcaf 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java @@ -3,25 +3,33 @@ import java.util.List; import java.util.stream.Stream; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import codezap.global.validation.ByteLength; import codezap.template.dto.request.validation.ValidatedSnippetsOrdinalRequest; import io.swagger.v3.oas.annotations.media.Schema; public record UpdateTemplateRequest( @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") @NotNull(message = "템플릿 이름이 null 입니다.") + @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.") String title, @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") + @NotNull(message = "템플릿 설명이 null 입니다.") + @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.") String description, @Schema(description = "새로 추가한 스니펫 내역") @NotNull(message = "createSnippets 리스트가 null 입니다.") + @Valid List createSnippets, @Schema(description = "삭제, 생성 스니펫을 제외한 모든 스니펫 내역") @NotNull(message = "updateSnippets 리스트가 null 입니다.") + @Valid List updateSnippets, @Schema(description = "삭제한 스니펫 식별자") @@ -29,9 +37,11 @@ public record UpdateTemplateRequest( List deleteSnippetIds, @Schema(description = "카테고리 ID", example = "1") + @NotNull(message = "카테고리 id가 null 입니다.") Long categoryId, @Schema(description = "태그 리스트") + @NotNull(message = "태그 리스트가 null 입니다.") List tags ) implements ValidatedSnippetsOrdinalRequest { @Override diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index 43f56fa3a..ee5abca0e 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -60,7 +60,7 @@ void createTemplateSuccess(String repeatTarget, int maxLength) { categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = new CreateTemplateRequest( maxTitle, - "description", + repeatTarget.repeat(maxLength), List.of(new CreateSnippetRequest("a".repeat(MAX_LENGTH), repeatTarget.repeat(maxLength), 1)), 1L, List.of("tag1", "tag2") @@ -141,6 +141,28 @@ void createTemplateFailWithLongContent(String repeatTarget, int exceededLength) .body("detail", is("파일 내용은 최대 65,535 Byte까지 입력 가능합니다.")); } + @ParameterizedTest + @DisplayName("템플릿 생성 실패: 템플릿 설명 길이 초과") + @CsvSource({"a, 65536", "ㄱ, 21846"}) + void createTemplateFailWithLongDescription(String repeatTarget, int exceededLength) { + categoryService.create(new CreateCategoryRequest("category")); + CreateTemplateRequest templateRequest = new CreateTemplateRequest( + "title", + repeatTarget.repeat(exceededLength), + List.of(new CreateSnippetRequest("title", "content", 1)), + 1L, + List.of("tag1", "tag2") + ); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(templateRequest) + .when().post("/templates") + .then().log().all() + .statusCode(400) + .body("detail", is("템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.")); + } + @ParameterizedTest @DisplayName("템플릿 생성 실패: 잘못된 스니펫 순서 입력") @CsvSource({"0, 1", "1, 3", "2, 1"}) @@ -256,6 +278,146 @@ void updateTemplateSuccess() { .statusCode(200); } + @Test + @DisplayName("템플릿 수정 실패: 템플릿 이름 길이 초과") + void updateTemplateFailWithLongName() { + // given + String exceededTitle = "a".repeat(MAX_LENGTH + 1); + categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category2")); + CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); + templateService.create(templateRequest); + + UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( + exceededTitle, + "description", + List.of( + new CreateSnippetRequest("filename3", "content3", 2), + new CreateSnippetRequest("filename4", "content4", 3) + ), + List.of( + new UpdateSnippetRequest(2L, "updateFilename2", "updateContent2", 1) + ), + List.of(1L), + 2L, + List.of("tag1", "tag3") + ); + + // when & then + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(updateTemplateRequest) + .when().post("/templates/1") + .then().log().all() + .statusCode(400) + .body("detail", is("템플릿 이름은 최대 255자까지 입력 가능합니다.")); + } + + @Test + @DisplayName("템플릿 생성 실패: 파일 이름 길이 초과") + void updateTemplateFailWithLongFileName() { + // given + String exceededTitle = "a".repeat(MAX_LENGTH + 1); + categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category2")); + CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); + templateService.create(templateRequest); + + UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( + "title", + "description", + List.of( + new CreateSnippetRequest(exceededTitle, "content3", 2), + new CreateSnippetRequest("filename4", "content4", 3) + ), + List.of( + new UpdateSnippetRequest(2L, "updateFilename2", "updateContent2", 1) + ), + List.of(1L), + 2L, + List.of("tag1", "tag3") + ); + + // when & then + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(updateTemplateRequest) + .when().post("/templates/1") + .then().log().all() + .statusCode(400) + .body("detail", is("파일 이름은 최대 255자까지 입력 가능합니다.")); + } + + @ParameterizedTest + @DisplayName("템플릿 생성 실패: 파일 내용 길이 초과") + @CsvSource({"a, 65536", "ㄱ, 21846"}) + void updateTemplateFailWithLongFileContent(String repeatTarget, int exceedLength) { + // given + categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category2")); + CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); + templateService.create(templateRequest); + + UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( + "title", + "description", + List.of( + new CreateSnippetRequest("filename3", repeatTarget.repeat(exceedLength), 2), + new CreateSnippetRequest("filename4", "content4", 3) + ), + List.of( + new UpdateSnippetRequest(2L, "updateFilename2", "updateContent2", 1) + ), + List.of(1L), + 2L, + List.of("tag1", "tag3") + ); + + // when & then + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(updateTemplateRequest) + .when().post("/templates/1") + .then().log().all() + .statusCode(400) + .body("detail", is("파일 내용은 최대 65,535 Byte까지 입력 가능합니다.")); + } + + @ParameterizedTest + @DisplayName("템플릿 생성 실패: 템플릿 설명 길이 초과") + @CsvSource({"a, 65536", "ㄱ, 21846"}) + void updateTemplateFailWithLongContent(String repeatTarget, int exceedLength) { + // given + categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category2")); + CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); + templateService.create(templateRequest); + + UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( + "title", + repeatTarget.repeat(exceedLength), + List.of( + new CreateSnippetRequest("filename3", "content3", 2), + new CreateSnippetRequest("filename4", "content4", 3) + ), + List.of( + new UpdateSnippetRequest(2L, "updateFilename2", "updateContent2", 1) + ), + List.of(1L), + 2L, + List.of("tag1", "tag3") + ); + + // when & then + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(updateTemplateRequest) + .when().post("/templates/1") + .then().log().all() + .statusCode(400) + .body("detail", is("템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.")); + } + // 정상 요청: 2, 3, 1 @ParameterizedTest @DisplayName("템플릿 수정 실패: 잘못된 스니펫 순서 입력") From 901e45016986f4c835cf980f89f75406133704df Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Fri, 2 Aug 2024 14:10:39 +0900 Subject: [PATCH 45/69] =?UTF-8?q?style(codezap):=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/category/controller/CategoryController.java | 5 +++-- .../template/repository/SnippetRepositoryTest.java | 2 +- .../codezap/template/service/TemplateServiceTest.java | 8 +++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index ee6179d34..9f7bb20be 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -21,7 +21,7 @@ @RestController @RequestMapping("/categories") -public class CategoryController implements SpringDocCategoryController{ +public class CategoryController implements SpringDocCategoryController { private final CategoryService categoryService; @@ -42,7 +42,8 @@ public ResponseEntity getCategories() { @PutMapping("/{id}") public ResponseEntity updateCategory(@PathVariable Long id, - @Valid @RequestBody UpdateCategoryRequest updateCategoryRequest) { + @Valid @RequestBody UpdateCategoryRequest updateCategoryRequest + ) { categoryService.update(id, updateCategoryRequest); return ResponseEntity.ok().build(); } diff --git a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java index 5ce655dad..33dea21f2 100644 --- a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java +++ b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java @@ -53,7 +53,7 @@ void findOneSnippetSuccessWithTemplateAndOrdinal() { @DisplayName("스니펫 리스트 찾기 성공: 템플릿과 순서") void findSnippetsSuccessWithTemplateAndOrdinal() { Category category = categoryRepository.save(new Category("category")); - Template template = templateRepository.save(new Template("title","description", category)); + Template template = templateRepository.save(new Template("title", "description", category)); Snippet snippet1 = snippetRepository.save(new Snippet(template, "filename1", "content1", 1)); Snippet snippet2 = snippetRepository.save(new Snippet(template, "filename2", "content2", 2)); Snippet snippet3 = snippetRepository.save(new Snippet(template, "filename3", "content3", 2)); diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index 1e5bd7b18..af3669a35 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -200,7 +200,13 @@ private UpdateTemplateRequest makeUpdateTemplateRequest(String title) { private Template saveTemplate(CreateTemplateRequest createTemplateRequest) { Category category = categoryRepository.save(new Category("category")); - Template savedTemplate = templateRepository.save(new Template(createTemplateRequest.title(), createTemplateRequest.description(), category)); + Template savedTemplate = templateRepository.save( + new Template( + createTemplateRequest.title(), + createTemplateRequest.description(), + category + ) + ); Snippet savedFirstSnippet = snippetRepository.save(new Snippet(savedTemplate, "filename1", "content1", 1)); snippetRepository.save(new Snippet(savedTemplate, "filename2", "content2", 2)); thumbnailSnippetRepository.save(new ThumbnailSnippet(savedTemplate, savedFirstSnippet)); From cc8db54c9fd342c0fec87290b73d5d3ab588228f Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Fri, 2 Aug 2024 15:40:30 +0900 Subject: [PATCH 46/69] =?UTF-8?q?refactor(codezap):=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=A0=84=EB=B0=98=EC=A0=81=EC=9D=B8=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 3 +- .../template/service/TemplateService.java | 30 +++++++------ .../controller/CategoryControllerTest.java | 8 ++-- .../category/service/CategoryServiceTest.java | 12 +++--- .../controller/TemplateControllerTest.java | 43 ++++++------------- 5 files changed, 41 insertions(+), 55 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 9f7bb20be..8a1869652 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -41,7 +41,8 @@ public ResponseEntity getCategories() { } @PutMapping("/{id}") - public ResponseEntity updateCategory(@PathVariable Long id, + public ResponseEntity updateCategory( + @PathVariable Long id, @Valid @RequestBody UpdateCategoryRequest updateCategoryRequest ) { categoryService.update(id, updateCategoryRequest); diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index d8f2e6fd3..11534317b 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -53,22 +53,10 @@ public TemplateService(ThumbnailSnippetRepository thumbnailSnippetRepository, @Transactional public Long create(CreateTemplateRequest createTemplateRequest) { Category category = categoryRepository.fetchById(createTemplateRequest.categoryId()); - Template template = templateRepository.save( - new Template( - createTemplateRequest.title(), - createTemplateRequest.description(), - category - ) + new Template(createTemplateRequest.title(), createTemplateRequest.description(), category) ); - - List tags = createTemplateRequest.tags().stream() - .map(Tag::new) - .map(tagRepository::save) - .toList(); - - tags.forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); - + createTags(createTemplateRequest, template); createTemplateRequest.snippets() .forEach(createSnippetRequest -> createSnippet(createSnippetRequest, template)); @@ -77,6 +65,14 @@ public Long create(CreateTemplateRequest createTemplateRequest) { return template.getId(); } + private void createTags(CreateTemplateRequest createTemplateRequest, Template template) { + templateTagRepository.saveAll(createTemplateRequest.tags().stream() + .map(tag -> tagRepository.save(new Tag(tag))) + .map(tag -> new TemplateTag(template, tag)) + .toList() + ); + } + public FindAllTemplatesResponse findAll() { return FindAllTemplatesResponse.from(thumbnailSnippetRepository.findAll()); } @@ -95,7 +91,11 @@ public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) Category category = categoryRepository.fetchById(updateTemplateRequest.categoryId()); Template template = templateRepository.fetchById(templateId); template.updateTemplate(updateTemplateRequest.title(), updateTemplateRequest.description(), category); + updateSnippets(updateTemplateRequest, template); + updateTags(updateTemplateRequest, template); + } + private void updateSnippets(UpdateTemplateRequest updateTemplateRequest, Template template) { updateTemplateRequest.updateSnippets().forEach(this::updateSnippet); updateTemplateRequest.createSnippets() .forEach(createSnippetRequest -> createSnippet(createSnippetRequest, template)); @@ -107,7 +107,9 @@ public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) } updateTemplateRequest.deleteSnippetIds().forEach(snippetRepository::deleteById); + } + private void updateTags(UpdateTemplateRequest updateTemplateRequest, Template template) { templateTagRepository.deleteAllByTemplate(template); updateTemplateRequest.tags().stream() .map(Tag::new) diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index 0f79d6da4..50424a1d1 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -133,13 +133,13 @@ void updateCategoryFailWithLongName() { @Test @DisplayName("카테고리 수정 실패: 중복된 이름의 카테고리 존재") void updateCategoryFailWithDuplicatedName() { - //given + // given String duplicatedName = "duplicatedName"; categoryService.create(new CreateCategoryRequest(duplicatedName)); UpdateCategoryRequest createCategoryRequest = new UpdateCategoryRequest(duplicatedName); - //when & then + // when & then RestAssured.given().log().all() .contentType(ContentType.JSON) .body(createCategoryRequest) @@ -183,7 +183,7 @@ void updateCategoryFailWithDuplicatedName() { @Test @DisplayName("카테고리 삭제 실패: 템플릿이 존재하는 카테고리는 삭제 불가능") void updateCategoryFailWithLongName() { - //given + // given templateService.create(new CreateTemplateRequest( "title", "description", @@ -192,7 +192,7 @@ void updateCategoryFailWithLongName() { List.of("tag1", "tag2") )); - //when & then + // when & then RestAssured.given().log().all() .when().delete("/categories/" + savedCategoryId) .then().log().all() diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 11a811216..9d7bd91af 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -80,26 +80,26 @@ void findAllCategoriesSuccess() { @Test @DisplayName("카테고리 수정 성공") void updateCategorySuccess() { - //given + // given Category savedCategory = categoryRepository.save(new Category("category1")); - //when + // when categoryService.update(savedCategory.getId(), new UpdateCategoryRequest("updateName")); - //then + // then assertThat(categoryRepository.fetchById(savedCategory.getId()).getName()).isEqualTo("updateName"); } @Test @DisplayName("카테고리 삭제 성공") void deleteCategorySuccess() { - //given + // given Category savedCategory = categoryRepository.save(new Category("category1")); - //when + // when categoryService.deleteById(savedCategory.getId()); - //then + // then assertThat(categoryRepository.findById(savedCategory.getId())).isEmpty(); } } diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index ee5abca0e..9641ff454 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -249,11 +249,7 @@ class updateTemplateTest { @DisplayName("템플릿 수정 성공") void updateTemplateSuccess() { // given - categoryService.create(new CreateCategoryRequest("category1")); - categoryService.create(new CreateCategoryRequest("category2")); - CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.create(templateRequest); - + createTemplateAndTwoCategories(); UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( "updateTitle", "description", @@ -283,11 +279,7 @@ void updateTemplateSuccess() { void updateTemplateFailWithLongName() { // given String exceededTitle = "a".repeat(MAX_LENGTH + 1); - categoryService.create(new CreateCategoryRequest("category1")); - categoryService.create(new CreateCategoryRequest("category2")); - CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.create(templateRequest); - + createTemplateAndTwoCategories(); UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( exceededTitle, "description", @@ -318,11 +310,7 @@ void updateTemplateFailWithLongName() { void updateTemplateFailWithLongFileName() { // given String exceededTitle = "a".repeat(MAX_LENGTH + 1); - categoryService.create(new CreateCategoryRequest("category1")); - categoryService.create(new CreateCategoryRequest("category2")); - CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.create(templateRequest); - + createTemplateAndTwoCategories(); UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( "title", "description", @@ -353,11 +341,7 @@ void updateTemplateFailWithLongFileName() { @CsvSource({"a, 65536", "ㄱ, 21846"}) void updateTemplateFailWithLongFileContent(String repeatTarget, int exceedLength) { // given - categoryService.create(new CreateCategoryRequest("category1")); - categoryService.create(new CreateCategoryRequest("category2")); - CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.create(templateRequest); - + createTemplateAndTwoCategories(); UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( "title", "description", @@ -388,11 +372,7 @@ void updateTemplateFailWithLongFileContent(String repeatTarget, int exceedLength @CsvSource({"a, 65536", "ㄱ, 21846"}) void updateTemplateFailWithLongContent(String repeatTarget, int exceedLength) { // given - categoryService.create(new CreateCategoryRequest("category1")); - categoryService.create(new CreateCategoryRequest("category2")); - CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.create(templateRequest); - + createTemplateAndTwoCategories(); UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( "title", repeatTarget.repeat(exceedLength), @@ -424,11 +404,7 @@ void updateTemplateFailWithLongContent(String repeatTarget, int exceedLength) { @CsvSource({"1, 2, 1", "3, 2, 1", "0, 2, 1"}) void updateTemplateFailWithWrongSnippetOrdinal(int createOrdinal1, int createOrdinal2, int updateOrdinal) { // given - categoryService.create(new CreateCategoryRequest("category1")); - categoryService.create(new CreateCategoryRequest("category2")); - CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.create(templateRequest); - + createTemplateAndTwoCategories(); UpdateTemplateRequest updateTemplateRequest = new UpdateTemplateRequest( "updateTitle", "description", @@ -453,6 +429,13 @@ void updateTemplateFailWithWrongSnippetOrdinal(int createOrdinal1, int createOrd .statusCode(400) .body("detail", is("스니펫 순서가 잘못되었습니다.")); } + + private void createTemplateAndTwoCategories() { + categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category2")); + CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); + templateService.create(templateRequest); + } } @Nested From 24781a90dd4b1a560fafd73b1ac89d45773c0532 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Fri, 2 Aug 2024 17:03:50 +0900 Subject: [PATCH 47/69] =?UTF-8?q?feat(codezap):=20Valid=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C=20=EC=84=A4=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CreateCategoryRequest.java | 6 ++++-- .../dto/request/UpdateCategoryRequest.java | 6 ++++-- .../global/validation/ValidationGroups.java | 7 +++++++ .../global/validation/ValidationSequence.java | 11 +++++++++++ .../template/controller/TemplateController.java | 8 ++++---- .../dto/request/CreateSnippetRequest.java | 11 +++++++---- .../dto/request/CreateTemplateRequest.java | 13 ++++++++----- .../dto/request/UpdateSnippetRequest.java | 11 +++++++---- .../dto/request/UpdateTemplateRequest.java | 15 +++++++++------ 9 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 backend/src/main/java/codezap/global/validation/ValidationGroups.java create mode 100644 backend/src/main/java/codezap/global/validation/ValidationSequence.java diff --git a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java index 86946b8a8..5fefb1e3c 100644 --- a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java @@ -3,12 +3,14 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import codezap.global.validation.ValidationGroups.NotNullGroup; +import codezap.global.validation.ValidationGroups.SizeCheckGroup; import io.swagger.v3.oas.annotations.media.Schema; public record CreateCategoryRequest( @Schema(description = "카테고리 이름", example = "Spring") - @NotNull(message = "카테고리 이름이 null 입니다.") - @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") + @NotNull(message = "카테고리 이름이 null 입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String name ) { } diff --git a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java index 4ed48bf0c..11149ba5c 100644 --- a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java @@ -3,12 +3,14 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import codezap.global.validation.ValidationGroups.NotNullGroup; +import codezap.global.validation.ValidationGroups.SizeCheckGroup; import io.swagger.v3.oas.annotations.media.Schema; public record UpdateCategoryRequest( @Schema(description = "카테고리 이름", example = "Spring") - @NotNull(message = "카테고리 이름이 null 입니다.") - @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.") + @NotNull(message = "카테고리 이름이 null 입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String name ) { } diff --git a/backend/src/main/java/codezap/global/validation/ValidationGroups.java b/backend/src/main/java/codezap/global/validation/ValidationGroups.java new file mode 100644 index 000000000..ac49b4443 --- /dev/null +++ b/backend/src/main/java/codezap/global/validation/ValidationGroups.java @@ -0,0 +1,7 @@ +package codezap.global.validation; + +public class ValidationGroups { + public interface NotNullGroup {} + public interface ByteLengthGroup {} + public interface SizeCheckGroup {} +} diff --git a/backend/src/main/java/codezap/global/validation/ValidationSequence.java b/backend/src/main/java/codezap/global/validation/ValidationSequence.java new file mode 100644 index 000000000..611530bfc --- /dev/null +++ b/backend/src/main/java/codezap/global/validation/ValidationSequence.java @@ -0,0 +1,11 @@ +package codezap.global.validation; + +import jakarta.validation.GroupSequence; + +import codezap.global.validation.ValidationGroups.ByteLengthGroup; +import codezap.global.validation.ValidationGroups.NotNullGroup; +import codezap.global.validation.ValidationGroups.SizeCheckGroup; + +@GroupSequence({NotNullGroup.class, SizeCheckGroup.class, ByteLengthGroup.class}) +public interface ValidationSequence { +} \ No newline at end of file diff --git a/backend/src/main/java/codezap/template/controller/TemplateController.java b/backend/src/main/java/codezap/template/controller/TemplateController.java index 89c93323a..dbd5ceec2 100644 --- a/backend/src/main/java/codezap/template/controller/TemplateController.java +++ b/backend/src/main/java/codezap/template/controller/TemplateController.java @@ -2,9 +2,8 @@ import java.net.URI; -import jakarta.validation.Valid; - import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -13,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import codezap.global.validation.ValidationSequence; import codezap.template.dto.request.CreateTemplateRequest; import codezap.template.dto.request.UpdateTemplateRequest; import codezap.template.dto.response.FindAllTemplatesResponse; @@ -30,7 +30,7 @@ public TemplateController(TemplateService templateService) { } @PostMapping - public ResponseEntity create(@Valid @RequestBody CreateTemplateRequest createTemplateRequest) { + public ResponseEntity create(@Validated(ValidationSequence.class) @RequestBody CreateTemplateRequest createTemplateRequest) { return ResponseEntity.created(URI.create("/templates/" + templateService.create(createTemplateRequest))) .build(); } @@ -48,7 +48,7 @@ public ResponseEntity getTemplateById(@PathVariable Lo @PostMapping("/{id}") public ResponseEntity updateTemplate( @PathVariable Long id, - @Valid @RequestBody UpdateTemplateRequest updateTemplateRequest + @Validated(ValidationSequence.class) @RequestBody UpdateTemplateRequest updateTemplateRequest ) { templateService.update(id, updateTemplateRequest); return ResponseEntity.ok().build(); diff --git a/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java index 34397eda3..d90baa5db 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java @@ -4,17 +4,20 @@ import jakarta.validation.constraints.Size; import codezap.global.validation.ByteLength; +import codezap.global.validation.ValidationGroups.ByteLengthGroup; +import codezap.global.validation.ValidationGroups.NotNullGroup; +import codezap.global.validation.ValidationGroups.SizeCheckGroup; import io.swagger.v3.oas.annotations.media.Schema; public record CreateSnippetRequest( @Schema(description = "파일 이름", example = "Main.java") - @NotNull(message = "파일 이름이 null 입니다.") - @Size(max = 255, message = "파일 이름은 최대 255자까지 입력 가능합니다.") + @NotNull(message = "파일 이름이 null 입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "파일 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String filename, @Schema(description = "소스 코드", example = "public class Main { // ...") - @NotNull(message = "파일 내용이 null 입니다.") - @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.") + @NotNull(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) + @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) String content, @Schema(description = "스니펫 순서", example = "1") diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index b7e217fb0..515f79185 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -7,22 +7,25 @@ import jakarta.validation.constraints.Size; import codezap.global.validation.ByteLength; +import codezap.global.validation.ValidationGroups.ByteLengthGroup; +import codezap.global.validation.ValidationGroups.NotNullGroup; +import codezap.global.validation.ValidationGroups.SizeCheckGroup; import codezap.template.dto.request.validation.ValidatedSnippetsOrdinalRequest; import io.swagger.v3.oas.annotations.media.Schema; public record CreateTemplateRequest( @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") - @NotNull(message = "템플릿 이름이 null 입니다.") - @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.") + @NotNull(message = "템플릿 이름이 null 입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String title, @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") - @NotNull(message = "템플릿 설명이 null 입니다.") - @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.") + @NotNull(message = "템플릿 설명이 null 입니다.", groups = NotNullGroup.class) + @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) String description, @Schema(description = "템플릿의 스니펫 내역") - @NotNull(message = "스니펫 리스트가 null 입니다.") + @NotNull(message = "스니펫 리스트가 null 입니다.", groups = NotNullGroup.class) @Valid List snippets, diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java index bf669729e..137d6ebec 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java @@ -4,6 +4,9 @@ import jakarta.validation.constraints.Size; import codezap.global.validation.ByteLength; +import codezap.global.validation.ValidationGroups.ByteLengthGroup; +import codezap.global.validation.ValidationGroups.NotNullGroup; +import codezap.global.validation.ValidationGroups.SizeCheckGroup; import io.swagger.v3.oas.annotations.media.Schema; public record UpdateSnippetRequest( @@ -12,13 +15,13 @@ public record UpdateSnippetRequest( Long id, @Schema(description = "파일 이름", example = "Main.java") - @NotNull(message = "파일 이름이 null 입니다.") - @Size(max = 255, message = "파일 이름은 최대 255자까지 입력 가능합니다.") + @NotNull(message = "파일 이름이 null 입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "파일 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String filename, @Schema(description = "소스 코드", example = "public class Main { // ...") - @NotNull(message = "파일 내용이 null 입니다.") - @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.") + @NotNull(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) + @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) String content, @Schema(description = "스니펫 순서", example = "1") diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java index 52ebfbcaf..b87d3da5a 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java @@ -8,27 +8,30 @@ import jakarta.validation.constraints.Size; import codezap.global.validation.ByteLength; +import codezap.global.validation.ValidationGroups.ByteLengthGroup; +import codezap.global.validation.ValidationGroups.NotNullGroup; +import codezap.global.validation.ValidationGroups.SizeCheckGroup; import codezap.template.dto.request.validation.ValidatedSnippetsOrdinalRequest; import io.swagger.v3.oas.annotations.media.Schema; public record UpdateTemplateRequest( @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") - @NotNull(message = "템플릿 이름이 null 입니다.") - @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.") + @NotNull(message = "템플릿 이름이 null 입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String title, @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") - @NotNull(message = "템플릿 설명이 null 입니다.") - @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.") + @NotNull(message = "템플릿 설명이 null 입니다.", groups = NotNullGroup.class) + @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) String description, @Schema(description = "새로 추가한 스니펫 내역") - @NotNull(message = "createSnippets 리스트가 null 입니다.") + @NotNull(message = "createSnippets 리스트가 null 입니다.", groups = NotNullGroup.class) @Valid List createSnippets, @Schema(description = "삭제, 생성 스니펫을 제외한 모든 스니펫 내역") - @NotNull(message = "updateSnippets 리스트가 null 입니다.") + @NotNull(message = "updateSnippets 리스트가 null 입니다.", groups = NotNullGroup.class) @Valid List updateSnippets, From c6b77e7117642119477e7acfc5ae88a2b50a5c3f Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Fri, 2 Aug 2024 17:06:06 +0900 Subject: [PATCH 48/69] =?UTF-8?q?feat(service):=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=8B=9C=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/codezap/category/service/CategoryService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 48a9f5191..1b5b9cdc1 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -46,6 +46,9 @@ public void deleteById(Long id) { if (templateRepository.existsByCategoryId(id)) { throw new CodeZapException(HttpStatus.BAD_REQUEST, "템플릿이 존재하는 카테고리는 삭제할 수 없습니다."); } + if (id == 1) { + throw new CodeZapException(HttpStatus.BAD_REQUEST, "1번 카테고리는 삭제할 수 없습니다."); + } categoryRepository.deleteById(id); } From 145f9a880d4f9285605ac54d6881169640770f74 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Sun, 4 Aug 2024 11:45:54 +0900 Subject: [PATCH 49/69] =?UTF-8?q?feat(service):=20=EC=8A=A4=EB=8B=88?= =?UTF-8?q?=ED=8E=AB=20=EC=88=9C=EC=84=9C=20=EA=B2=80=EC=A6=9D=20groups=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/category/controller/CategoryController.java | 8 ++++---- .../java/codezap/global/validation/ValidationGroups.java | 1 + .../codezap/global/validation/ValidationSequence.java | 3 ++- .../validation/ValidatedSnippetsOrdinalRequest.java | 4 +++- .../category/controller/CategoryControllerTest.java | 3 ++- .../codezap/category/service/CategoryServiceTest.java | 1 + 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 8a1869652..9017cd667 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -2,9 +2,8 @@ import java.net.URI; -import jakarta.validation.Valid; - import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -18,6 +17,7 @@ import codezap.category.dto.request.UpdateCategoryRequest; import codezap.category.dto.response.FindAllCategoriesResponse; import codezap.category.service.CategoryService; +import codezap.global.validation.ValidationSequence; @RestController @RequestMapping("/categories") @@ -30,7 +30,7 @@ public CategoryController(CategoryService categoryService) { } @PostMapping - public ResponseEntity createCategory(@Valid @RequestBody CreateCategoryRequest createCategoryRequest) { + public ResponseEntity createCategory(@Validated(ValidationSequence.class) @RequestBody CreateCategoryRequest createCategoryRequest) { return ResponseEntity.created(URI.create("/categories/" + categoryService.create(createCategoryRequest))) .build(); } @@ -43,7 +43,7 @@ public ResponseEntity getCategories() { @PutMapping("/{id}") public ResponseEntity updateCategory( @PathVariable Long id, - @Valid @RequestBody UpdateCategoryRequest updateCategoryRequest + @Validated(ValidationSequence.class) @RequestBody UpdateCategoryRequest updateCategoryRequest ) { categoryService.update(id, updateCategoryRequest); return ResponseEntity.ok().build(); diff --git a/backend/src/main/java/codezap/global/validation/ValidationGroups.java b/backend/src/main/java/codezap/global/validation/ValidationGroups.java index ac49b4443..18fe8a044 100644 --- a/backend/src/main/java/codezap/global/validation/ValidationGroups.java +++ b/backend/src/main/java/codezap/global/validation/ValidationGroups.java @@ -2,6 +2,7 @@ public class ValidationGroups { public interface NotNullGroup {} + public interface SnippetOrdinalGroup {} public interface ByteLengthGroup {} public interface SizeCheckGroup {} } diff --git a/backend/src/main/java/codezap/global/validation/ValidationSequence.java b/backend/src/main/java/codezap/global/validation/ValidationSequence.java index 611530bfc..db331d574 100644 --- a/backend/src/main/java/codezap/global/validation/ValidationSequence.java +++ b/backend/src/main/java/codezap/global/validation/ValidationSequence.java @@ -5,7 +5,8 @@ import codezap.global.validation.ValidationGroups.ByteLengthGroup; import codezap.global.validation.ValidationGroups.NotNullGroup; import codezap.global.validation.ValidationGroups.SizeCheckGroup; +import codezap.global.validation.ValidationGroups.SnippetOrdinalGroup; -@GroupSequence({NotNullGroup.class, SizeCheckGroup.class, ByteLengthGroup.class}) +@GroupSequence({NotNullGroup.class, SnippetOrdinalGroup.class, ByteLengthGroup.class, SizeCheckGroup.class}) public interface ValidationSequence { } \ No newline at end of file diff --git a/backend/src/main/java/codezap/template/dto/request/validation/ValidatedSnippetsOrdinalRequest.java b/backend/src/main/java/codezap/template/dto/request/validation/ValidatedSnippetsOrdinalRequest.java index 5adb86fc6..7a09a5f06 100644 --- a/backend/src/main/java/codezap/template/dto/request/validation/ValidatedSnippetsOrdinalRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/validation/ValidatedSnippetsOrdinalRequest.java @@ -2,7 +2,9 @@ import java.util.List; -@SnippetsOrdinal(message = "스니펫 순서가 잘못되었습니다.") +import codezap.global.validation.ValidationGroups.SnippetOrdinalGroup; + +@SnippetsOrdinal(message = "스니펫 순서가 잘못되었습니다.", groups = SnippetOrdinalGroup.class) public interface ValidatedSnippetsOrdinalRequest { List extractSnippetsOrdinal(); diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index 50424a1d1..e99ed71c3 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -159,7 +159,8 @@ class deleteCategoryTest { @BeforeEach void saveCategory() { - savedCategoryId = categoryService.create(new CreateCategoryRequest("category1")); + categoryService.create(new CreateCategoryRequest("category1")); + savedCategoryId = categoryService.create(new CreateCategoryRequest("category2")); } @Test diff --git a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java index 9d7bd91af..5dd77c66a 100644 --- a/backend/src/test/java/codezap/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/codezap/category/service/CategoryServiceTest.java @@ -94,6 +94,7 @@ void updateCategorySuccess() { @DisplayName("카테고리 삭제 성공") void deleteCategorySuccess() { // given + categoryRepository.save(new Category("category1")); Category savedCategory = categoryRepository.save(new Category("category1")); // when From e59816e9c842869f6da810798a1d5a32387168a8 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Sun, 4 Aug 2024 12:04:36 +0900 Subject: [PATCH 50/69] =?UTF-8?q?refactor(validation):=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/codezap/global/validation/ValidationSequence.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/codezap/global/validation/ValidationSequence.java b/backend/src/main/java/codezap/global/validation/ValidationSequence.java index db331d574..6c92a010d 100644 --- a/backend/src/main/java/codezap/global/validation/ValidationSequence.java +++ b/backend/src/main/java/codezap/global/validation/ValidationSequence.java @@ -7,6 +7,6 @@ import codezap.global.validation.ValidationGroups.SizeCheckGroup; import codezap.global.validation.ValidationGroups.SnippetOrdinalGroup; -@GroupSequence({NotNullGroup.class, SnippetOrdinalGroup.class, ByteLengthGroup.class, SizeCheckGroup.class}) +@GroupSequence({NotNullGroup.class, SizeCheckGroup.class, ByteLengthGroup.class, SnippetOrdinalGroup.class}) public interface ValidationSequence { } \ No newline at end of file From dbd59004085196da62b70e32a4b5cb3e129f2610 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Sun, 4 Aug 2024 14:51:08 +0900 Subject: [PATCH 51/69] =?UTF-8?q?feat(codezap):=20=EB=B9=88=20=EA=B0=92?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20=EA=B2=80=EC=A6=9D=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/dto/request/CreateCategoryRequest.java | 4 ++-- .../category/dto/request/UpdateCategoryRequest.java | 4 ++-- .../template/dto/request/CreateSnippetRequest.java | 5 +++-- .../template/dto/request/CreateTemplateRequest.java | 3 ++- .../template/dto/request/UpdateSnippetRequest.java | 5 +++-- .../java/codezap/template/service/TemplateService.java | 10 ++++++++++ 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java index 5fefb1e3c..9587c9d04 100644 --- a/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/CreateCategoryRequest.java @@ -1,6 +1,6 @@ package codezap.category.dto.request; -import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import codezap.global.validation.ValidationGroups.NotNullGroup; @@ -9,7 +9,7 @@ public record CreateCategoryRequest( @Schema(description = "카테고리 이름", example = "Spring") - @NotNull(message = "카테고리 이름이 null 입니다.", groups = NotNullGroup.class) + @NotBlank(message = "카테고리 이름이 null 입니다.", groups = NotNullGroup.class) @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String name ) { diff --git a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java index 11149ba5c..52362219b 100644 --- a/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java +++ b/backend/src/main/java/codezap/category/dto/request/UpdateCategoryRequest.java @@ -1,6 +1,6 @@ package codezap.category.dto.request; -import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import codezap.global.validation.ValidationGroups.NotNullGroup; @@ -9,7 +9,7 @@ public record UpdateCategoryRequest( @Schema(description = "카테고리 이름", example = "Spring") - @NotNull(message = "카테고리 이름이 null 입니다.", groups = NotNullGroup.class) + @NotBlank(message = "카테고리 이름이 null 입니다.", groups = NotNullGroup.class) @Size(max = 255, message = "카테고리 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String name ) { diff --git a/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java index d90baa5db..24666c7c4 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java @@ -1,5 +1,6 @@ package codezap.template.dto.request; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -11,12 +12,12 @@ public record CreateSnippetRequest( @Schema(description = "파일 이름", example = "Main.java") - @NotNull(message = "파일 이름이 null 입니다.", groups = NotNullGroup.class) + @NotBlank(message = "파일 이름이 null 입니다.", groups = NotNullGroup.class) @Size(max = 255, message = "파일 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String filename, @Schema(description = "소스 코드", example = "public class Main { // ...") - @NotNull(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) + @NotBlank(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) String content, diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index 515f79185..1d78b0da5 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -3,6 +3,7 @@ import java.util.List; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -15,7 +16,7 @@ public record CreateTemplateRequest( @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") - @NotNull(message = "템플릿 이름이 null 입니다.", groups = NotNullGroup.class) + @NotBlank(message = "템플릿 이름이 null 입니다.", groups = NotNullGroup.class) @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String title, diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java index 137d6ebec..923073424 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java @@ -1,5 +1,6 @@ package codezap.template.dto.request; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -15,12 +16,12 @@ public record UpdateSnippetRequest( Long id, @Schema(description = "파일 이름", example = "Main.java") - @NotNull(message = "파일 이름이 null 입니다.", groups = NotNullGroup.class) + @NotBlank(message = "파일 이름이 null 입니다.", groups = NotNullGroup.class) @Size(max = 255, message = "파일 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String filename, @Schema(description = "소스 코드", example = "public class Main { // ...") - @NotNull(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) + @NotBlank(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) String content, diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 11534317b..89c4a8f2e 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -3,11 +3,13 @@ import java.util.List; import java.util.Objects; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import codezap.category.domain.Category; import codezap.category.repository.CategoryRepository; +import codezap.global.exception.CodeZapException; import codezap.template.domain.Snippet; import codezap.template.domain.Tag; import codezap.template.domain.Template; @@ -93,6 +95,7 @@ public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest) template.updateTemplate(updateTemplateRequest.title(), updateTemplateRequest.description(), category); updateSnippets(updateTemplateRequest, template); updateTags(updateTemplateRequest, template); + validateSnippetsCount(updateTemplateRequest, template); } private void updateSnippets(UpdateTemplateRequest updateTemplateRequest, Template template) { @@ -122,6 +125,13 @@ private void updateTags(UpdateTemplateRequest updateTemplateRequest, Template te tags.forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); } + private void validateSnippetsCount(UpdateTemplateRequest updateTemplateRequest, Template template) { + if (updateTemplateRequest.updateSnippets().size() + updateTemplateRequest.createSnippets().size() + != snippetRepository.findAllByTemplate(template).size()) { + throw new CodeZapException(HttpStatus.BAD_REQUEST, "스니펫의 정보가 정확하지 않습니다."); + } + } + @Transactional public void deleteById(Long id) { thumbnailSnippetRepository.deleteByTemplateId(id); From b267eea4cd8fa25b799a876a95f1a1eb98f4b36c Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Sun, 4 Aug 2024 18:22:32 +0900 Subject: [PATCH 52/69] =?UTF-8?q?feat(codezap):=20=EC=8A=A4=EB=8B=88?= =?UTF-8?q?=ED=8E=AB=EC=9D=B4=20=EB=B9=84=EC=96=B4=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=EC=97=90=20=EB=8C=80=ED=95=9C=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/validation/ValidationGroups.java | 1 + .../global/validation/ValidationSequence.java | 3 ++- .../dto/request/CreateTemplateRequest.java | 1 + .../dto/request/UpdateTemplateRequest.java | 11 ++++++++-- .../dto/request/validation/SnippetsCount.java | 21 +++++++++++++++++++ .../validation/SnippetsCountValidator.java | 15 +++++++++++++ .../ValidatedSnippetsCountRequest.java | 9 ++++++++ 7 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 backend/src/main/java/codezap/template/dto/request/validation/SnippetsCount.java create mode 100644 backend/src/main/java/codezap/template/dto/request/validation/SnippetsCountValidator.java create mode 100644 backend/src/main/java/codezap/template/dto/request/validation/ValidatedSnippetsCountRequest.java diff --git a/backend/src/main/java/codezap/global/validation/ValidationGroups.java b/backend/src/main/java/codezap/global/validation/ValidationGroups.java index 18fe8a044..f78603d88 100644 --- a/backend/src/main/java/codezap/global/validation/ValidationGroups.java +++ b/backend/src/main/java/codezap/global/validation/ValidationGroups.java @@ -3,6 +3,7 @@ public class ValidationGroups { public interface NotNullGroup {} public interface SnippetOrdinalGroup {} + public interface SnippetCountGroup {} public interface ByteLengthGroup {} public interface SizeCheckGroup {} } diff --git a/backend/src/main/java/codezap/global/validation/ValidationSequence.java b/backend/src/main/java/codezap/global/validation/ValidationSequence.java index 6c92a010d..25cb9ef26 100644 --- a/backend/src/main/java/codezap/global/validation/ValidationSequence.java +++ b/backend/src/main/java/codezap/global/validation/ValidationSequence.java @@ -5,8 +5,9 @@ import codezap.global.validation.ValidationGroups.ByteLengthGroup; import codezap.global.validation.ValidationGroups.NotNullGroup; import codezap.global.validation.ValidationGroups.SizeCheckGroup; +import codezap.global.validation.ValidationGroups.SnippetCountGroup; import codezap.global.validation.ValidationGroups.SnippetOrdinalGroup; -@GroupSequence({NotNullGroup.class, SizeCheckGroup.class, ByteLengthGroup.class, SnippetOrdinalGroup.class}) +@GroupSequence({NotNullGroup.class, SizeCheckGroup.class, ByteLengthGroup.class, SnippetCountGroup.class, SnippetOrdinalGroup.class}) public interface ValidationSequence { } \ No newline at end of file diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index 1d78b0da5..a5c273e34 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -27,6 +27,7 @@ public record CreateTemplateRequest( @Schema(description = "템플릿의 스니펫 내역") @NotNull(message = "스니펫 리스트가 null 입니다.", groups = NotNullGroup.class) + @Size(min = 1, message = "스니펫은 최소 1개 입력해야 합니다.", groups = SizeCheckGroup.class) @Valid List snippets, diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java index b87d3da5a..e8df2bed1 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java @@ -4,6 +4,7 @@ import java.util.stream.Stream; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -11,12 +12,13 @@ import codezap.global.validation.ValidationGroups.ByteLengthGroup; import codezap.global.validation.ValidationGroups.NotNullGroup; import codezap.global.validation.ValidationGroups.SizeCheckGroup; +import codezap.template.dto.request.validation.ValidatedSnippetsCountRequest; import codezap.template.dto.request.validation.ValidatedSnippetsOrdinalRequest; import io.swagger.v3.oas.annotations.media.Schema; public record UpdateTemplateRequest( @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") - @NotNull(message = "템플릿 이름이 null 입니다.", groups = NotNullGroup.class) + @NotBlank(message = "템플릿 이름이 null 입니다.", groups = NotNullGroup.class) @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String title, @@ -46,7 +48,7 @@ public record UpdateTemplateRequest( @Schema(description = "태그 리스트") @NotNull(message = "태그 리스트가 null 입니다.") List tags -) implements ValidatedSnippetsOrdinalRequest { +) implements ValidatedSnippetsOrdinalRequest, ValidatedSnippetsCountRequest { @Override public List extractSnippetsOrdinal() { return Stream.concat( @@ -54,4 +56,9 @@ public List extractSnippetsOrdinal() { createSnippets.stream().map(CreateSnippetRequest::ordinal) ).toList(); } + + @Override + public Integer countSnippets() { + return updateSnippets.size() + createSnippets.size(); + } } diff --git a/backend/src/main/java/codezap/template/dto/request/validation/SnippetsCount.java b/backend/src/main/java/codezap/template/dto/request/validation/SnippetsCount.java new file mode 100644 index 000000000..86f41a911 --- /dev/null +++ b/backend/src/main/java/codezap/template/dto/request/validation/SnippetsCount.java @@ -0,0 +1,21 @@ +package codezap.template.dto.request.validation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = SnippetsCountValidator.class) +public @interface SnippetsCount { + + String message(); + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/backend/src/main/java/codezap/template/dto/request/validation/SnippetsCountValidator.java b/backend/src/main/java/codezap/template/dto/request/validation/SnippetsCountValidator.java new file mode 100644 index 000000000..a68729f6d --- /dev/null +++ b/backend/src/main/java/codezap/template/dto/request/validation/SnippetsCountValidator.java @@ -0,0 +1,15 @@ +package codezap.template.dto.request.validation; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +public class SnippetsCountValidator implements ConstraintValidator { + + @Override + public boolean isValid(ValidatedSnippetsCountRequest validatedSnippetsCountRequest, + ConstraintValidatorContext constraintValidatorContext + ) { + Integer snippetsCount = validatedSnippetsCountRequest.countSnippets(); + return snippetsCount > 0; + } +} diff --git a/backend/src/main/java/codezap/template/dto/request/validation/ValidatedSnippetsCountRequest.java b/backend/src/main/java/codezap/template/dto/request/validation/ValidatedSnippetsCountRequest.java new file mode 100644 index 000000000..2de66a8b7 --- /dev/null +++ b/backend/src/main/java/codezap/template/dto/request/validation/ValidatedSnippetsCountRequest.java @@ -0,0 +1,9 @@ +package codezap.template.dto.request.validation; + +import codezap.global.validation.ValidationGroups.SnippetCountGroup; + +@SnippetsCount(message = "스니펫은 최소 1개 입력해야 합니다.", groups = SnippetCountGroup.class) +public interface ValidatedSnippetsCountRequest { + + Integer countSnippets(); +} From 094b5b780da9c656f4bd8b0ef48caa737a558d9d Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 15:41:49 +0900 Subject: [PATCH 53/69] =?UTF-8?q?refactor(category):=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EA=B0=80=EB=8F=85=EC=84=B1=20=ED=96=A5=EC=83=81=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/controller/CategoryController.java | 7 +++++-- .../controller/SpringDocCategoryController.java | 3 +-- .../codezap/category/service/CategoryService.java | 12 ++++++------ .../codezap/global/validation/ValidationGroups.java | 4 ++++ .../global/validation/ValidationSequence.java | 7 ++++++- .../template/controller/TemplateController.java | 8 +++++--- 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 9017cd667..0ca9511bc 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -30,8 +30,11 @@ public CategoryController(CategoryService categoryService) { } @PostMapping - public ResponseEntity createCategory(@Validated(ValidationSequence.class) @RequestBody CreateCategoryRequest createCategoryRequest) { - return ResponseEntity.created(URI.create("/categories/" + categoryService.create(createCategoryRequest))) + public ResponseEntity createCategory( + @Validated(ValidationSequence.class) @RequestBody CreateCategoryRequest createCategoryRequest + ) { + Long createdCategoryId = categoryService.create(createCategoryRequest); + return ResponseEntity.created(URI.create("/categories/" + createdCategoryId)) .build(); } diff --git a/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java index f3ad05bdf..c288e47d1 100644 --- a/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java +++ b/backend/src/main/java/codezap/category/controller/SpringDocCategoryController.java @@ -27,8 +27,7 @@ public interface SpringDocCategoryController { @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/categories", errorCases = { @ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "카테고리 이름이 null 입니다."), @ErrorCase(description = "카테고리 이름이 255자를 초과한 경우", exampleMessage = "카테고리 이름은 최대 255자까지 입력 가능합니다."), - @ErrorCase(description = "동일한 이름의 카테고리가 존재하는 경우", - exampleMessage = "이름이 Spring 인 카테고리가 이미 존재합니다.") + @ErrorCase(description = "동일한 이름의 카테고리가 존재하는 경우", exampleMessage = "이름이 Spring 인 카테고리가 이미 존재합니다.") }) ResponseEntity createCategory(CreateCategoryRequest createCategoryRequest); diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 1b5b9cdc1..4b5a59da4 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -42,6 +42,12 @@ public void update(Long id, UpdateCategoryRequest updateCategoryRequest) { category.updateName(updateCategoryRequest.name()); } + private void validateDuplicatedCategory(String categoryName) { + if (categoryRepository.existsByName(categoryName)) { + throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재합니다."); + } + } + public void deleteById(Long id) { if (templateRepository.existsByCategoryId(id)) { throw new CodeZapException(HttpStatus.BAD_REQUEST, "템플릿이 존재하는 카테고리는 삭제할 수 없습니다."); @@ -51,10 +57,4 @@ public void deleteById(Long id) { } categoryRepository.deleteById(id); } - - private void validateDuplicatedCategory(String categoryName) { - if (categoryRepository.existsByName(categoryName)) { - throw new CodeZapException(HttpStatus.CONFLICT, "이름이 " + categoryName + "인 카테고리가 이미 존재합니다."); - } - } } diff --git a/backend/src/main/java/codezap/global/validation/ValidationGroups.java b/backend/src/main/java/codezap/global/validation/ValidationGroups.java index f78603d88..ba25f3892 100644 --- a/backend/src/main/java/codezap/global/validation/ValidationGroups.java +++ b/backend/src/main/java/codezap/global/validation/ValidationGroups.java @@ -2,8 +2,12 @@ public class ValidationGroups { public interface NotNullGroup {} + public interface SnippetOrdinalGroup {} + public interface SnippetCountGroup {} + public interface ByteLengthGroup {} + public interface SizeCheckGroup {} } diff --git a/backend/src/main/java/codezap/global/validation/ValidationSequence.java b/backend/src/main/java/codezap/global/validation/ValidationSequence.java index 25cb9ef26..8c8fb97ff 100644 --- a/backend/src/main/java/codezap/global/validation/ValidationSequence.java +++ b/backend/src/main/java/codezap/global/validation/ValidationSequence.java @@ -8,6 +8,11 @@ import codezap.global.validation.ValidationGroups.SnippetCountGroup; import codezap.global.validation.ValidationGroups.SnippetOrdinalGroup; -@GroupSequence({NotNullGroup.class, SizeCheckGroup.class, ByteLengthGroup.class, SnippetCountGroup.class, SnippetOrdinalGroup.class}) +@GroupSequence({ + NotNullGroup.class, + SizeCheckGroup.class, + ByteLengthGroup.class, + SnippetCountGroup.class, + SnippetOrdinalGroup.class}) public interface ValidationSequence { } \ No newline at end of file diff --git a/backend/src/main/java/codezap/template/controller/TemplateController.java b/backend/src/main/java/codezap/template/controller/TemplateController.java index dbd5ceec2..5a4cec80a 100644 --- a/backend/src/main/java/codezap/template/controller/TemplateController.java +++ b/backend/src/main/java/codezap/template/controller/TemplateController.java @@ -16,7 +16,7 @@ import codezap.template.dto.request.CreateTemplateRequest; import codezap.template.dto.request.UpdateTemplateRequest; import codezap.template.dto.response.FindAllTemplatesResponse; -import codezap.template.dto.response.FindTemplateByIdResponse; +import codezap.template.dto.response.FindTemplateResponse; import codezap.template.service.TemplateService; @RestController @@ -30,7 +30,9 @@ public TemplateController(TemplateService templateService) { } @PostMapping - public ResponseEntity create(@Validated(ValidationSequence.class) @RequestBody CreateTemplateRequest createTemplateRequest) { + public ResponseEntity create( + @Validated(ValidationSequence.class) @RequestBody CreateTemplateRequest createTemplateRequest + ) { return ResponseEntity.created(URI.create("/templates/" + templateService.create(createTemplateRequest))) .build(); } @@ -41,7 +43,7 @@ public ResponseEntity getTemplates() { } @GetMapping("/{id}") - public ResponseEntity getTemplateById(@PathVariable Long id) { + public ResponseEntity getTemplateById(@PathVariable Long id) { return ResponseEntity.ok(templateService.findById(id)); } From b8e19952989b1c50ef98d16f572c7aebb74d16ae Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 15:50:00 +0900 Subject: [PATCH 54/69] =?UTF-8?q?refactor(codezap):=20responseDto=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/FindAllCategoriesResponse.java | 4 ++-- ...Response.java => FindCategoryResponse.java} | 6 +++--- .../SpringDocTemplateController.java | 4 ++-- ...gByIdResponse.java => FindTagResponse.java} | 6 +++--- ...Response.java => FindTemplateResponse.java} | 18 +++++++++--------- .../template/service/TemplateService.java | 6 +++--- .../template/service/TemplateServiceTest.java | 4 ++-- 7 files changed, 24 insertions(+), 24 deletions(-) rename backend/src/main/java/codezap/category/dto/response/{FindCategoryByIdResponse.java => FindCategoryResponse.java} (62%) rename backend/src/main/java/codezap/template/dto/response/{FindTagByIdResponse.java => FindTagResponse.java} (66%) rename backend/src/main/java/codezap/template/dto/response/{FindTemplateByIdResponse.java => FindTemplateResponse.java} (75%) diff --git a/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java index b17e2168f..bac6dccf6 100644 --- a/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java +++ b/backend/src/main/java/codezap/category/dto/response/FindAllCategoriesResponse.java @@ -7,12 +7,12 @@ public record FindAllCategoriesResponse( @Schema(description = "카테고리 목록") - List categories + List categories ) { public static FindAllCategoriesResponse from(List categories) { return new FindAllCategoriesResponse( categories.stream() - .map(FindCategoryByIdResponse::from) + .map(FindCategoryResponse::from) .toList() ); } diff --git a/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java b/backend/src/main/java/codezap/category/dto/response/FindCategoryResponse.java similarity index 62% rename from backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java rename to backend/src/main/java/codezap/category/dto/response/FindCategoryResponse.java index 999a7a46a..699fda8d6 100644 --- a/backend/src/main/java/codezap/category/dto/response/FindCategoryByIdResponse.java +++ b/backend/src/main/java/codezap/category/dto/response/FindCategoryResponse.java @@ -3,13 +3,13 @@ import codezap.category.domain.Category; import io.swagger.v3.oas.annotations.media.Schema; -public record FindCategoryByIdResponse( +public record FindCategoryResponse( @Schema(description = "카테고리 식별자", example = "1") Long id, @Schema(description = "카테고리 이름", example = "Spring") String name ) { - public static FindCategoryByIdResponse from(Category category) { - return new FindCategoryByIdResponse(category.getId(), category.getName()); + public static FindCategoryResponse from(Category category) { + return new FindCategoryResponse(category.getId(), category.getName()); } } diff --git a/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java b/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java index f125a25f6..cd7493a51 100644 --- a/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java +++ b/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java @@ -8,7 +8,7 @@ import codezap.template.dto.request.CreateTemplateRequest; import codezap.template.dto.request.UpdateTemplateRequest; import codezap.template.dto.response.FindAllTemplatesResponse; -import codezap.template.dto.response.FindTemplateByIdResponse; +import codezap.template.dto.response.FindTemplateResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; @@ -49,7 +49,7 @@ public interface SpringDocTemplateController { @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/templates/1", errorCases = { @ErrorCase(description = "해당하는 id 값인 템플릿이 없는 경우", exampleMessage = "식별자 1에 해당하는 템플릿이 존재하지 않습니다."), }) - ResponseEntity getTemplateById(Long id); + ResponseEntity getTemplateById(Long id); @Operation(summary = "템플릿 수정", description = "해당하는 식별자의 템플릿을 수정합니다.") @ApiResponse(responseCode = "200", description = "템플릿 수정 성공") diff --git a/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTagResponse.java similarity index 66% rename from backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java rename to backend/src/main/java/codezap/template/dto/response/FindTagResponse.java index 60d6a9173..08a167a37 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindTagByIdResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindTagResponse.java @@ -3,14 +3,14 @@ import codezap.template.domain.Tag; import io.swagger.v3.oas.annotations.media.Schema; -public record FindTagByIdResponse( +public record FindTagResponse( @Schema(description = "태그 식별자", example = "1") Long id, @Schema(description = "태그 이름", example = "스프링") String name ) { - public static FindTagByIdResponse from(Tag tag) { - return new FindTagByIdResponse(tag.getId(), tag.getName()); + public static FindTagResponse from(Tag tag) { + return new FindTagResponse(tag.getId(), tag.getName()); } } diff --git a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTemplateResponse.java similarity index 75% rename from backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java rename to backend/src/main/java/codezap/template/dto/response/FindTemplateResponse.java index b9451b777..3920cfdd8 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindTemplateByIdResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindTemplateResponse.java @@ -3,13 +3,13 @@ import java.time.LocalDateTime; import java.util.List; -import codezap.category.dto.response.FindCategoryByIdResponse; +import codezap.category.dto.response.FindCategoryResponse; import codezap.template.domain.Snippet; import codezap.template.domain.Tag; import codezap.template.domain.Template; import io.swagger.v3.oas.annotations.media.Schema; -public record FindTemplateByIdResponse( +public record FindTemplateResponse( @Schema(description = "템플릿 식별자", example = "0") Long id, @@ -23,21 +23,21 @@ public record FindTemplateByIdResponse( List snippets, @Schema(description = "카테고리 정보") - FindCategoryByIdResponse category, + FindCategoryResponse category, @Schema(description = "태그 목록") - List tags, + List tags, @Schema(description = "템플릿 수정 시간", example = "2024-11-11 12:00", type = "string") LocalDateTime modifiedAt ) { - public static FindTemplateByIdResponse of(Template template, List snippets, List tags) { - return new FindTemplateByIdResponse( + public static FindTemplateResponse of(Template template, List snippets, List tags) { + return new FindTemplateResponse( template.getId(), template.getTitle(), template.getDescription(), mapToFindAllSnippetByTemplateResponse(snippets), - FindCategoryByIdResponse.from(template.getCategory()), + FindCategoryResponse.from(template.getCategory()), mapToFindTagByTemplateResponse(tags), template.getModifiedAt() ); @@ -51,11 +51,11 @@ private static List mapToFindAllSnippetByTempl .toList(); } - private static List mapToFindTagByTemplateResponse( + private static List mapToFindTagByTemplateResponse( List tags ) { return tags.stream() - .map(FindTagByIdResponse::from) + .map(FindTagResponse::from) .toList(); } } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 89c4a8f2e..19c23dd79 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -20,7 +20,7 @@ import codezap.template.dto.request.UpdateSnippetRequest; import codezap.template.dto.request.UpdateTemplateRequest; import codezap.template.dto.response.FindAllTemplatesResponse; -import codezap.template.dto.response.FindTemplateByIdResponse; +import codezap.template.dto.response.FindTemplateResponse; import codezap.template.repository.SnippetRepository; import codezap.template.repository.TagRepository; import codezap.template.repository.TemplateRepository; @@ -79,13 +79,13 @@ public FindAllTemplatesResponse findAll() { return FindAllTemplatesResponse.from(thumbnailSnippetRepository.findAll()); } - public FindTemplateByIdResponse findById(Long id) { + public FindTemplateResponse findById(Long id) { Template template = templateRepository.fetchById(id); List snippets = snippetRepository.findAllByTemplate(template); List tags = templateTagRepository.findAllByTemplate(template).stream() .map(TemplateTag::getTag) .toList(); - return FindTemplateByIdResponse.of(template, snippets, tags); + return FindTemplateResponse.of(template, snippets, tags); } @Transactional diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index af3669a35..728e046b5 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -27,7 +27,7 @@ import codezap.template.dto.request.UpdateSnippetRequest; import codezap.template.dto.request.UpdateTemplateRequest; import codezap.template.dto.response.FindAllTemplatesResponse; -import codezap.template.dto.response.FindTemplateByIdResponse; +import codezap.template.dto.response.FindTemplateResponse; import codezap.template.repository.SnippetRepository; import codezap.template.repository.TagRepository; import codezap.template.repository.TemplateRepository; @@ -109,7 +109,7 @@ void findOneTemplateSuccess() { Template template = saveTemplate(createdTemplate); // when - FindTemplateByIdResponse foundTemplate = templateService.findById(template.getId()); + FindTemplateResponse foundTemplate = templateService.findById(template.getId()); // then assertAll( From 04356e038b8eb6968b5442ce570c22f2d267c44c Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 15:53:03 +0900 Subject: [PATCH 55/69] =?UTF-8?q?refactor(codezap):=20NoArgsConstructor=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=20=EC=A0=9C=EC=96=B4=EC=9E=90=20protected?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/codezap/category/domain/Category.java | 3 ++- backend/src/main/java/codezap/member/domain/Member.java | 3 ++- backend/src/main/java/codezap/template/domain/Snippet.java | 3 ++- backend/src/main/java/codezap/template/domain/Tag.java | 3 ++- backend/src/main/java/codezap/template/domain/Template.java | 3 ++- .../src/main/java/codezap/template/domain/TemplateTag.java | 5 +++-- .../main/java/codezap/template/domain/ThumbnailSnippet.java | 3 ++- 7 files changed, 15 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/codezap/category/domain/Category.java b/backend/src/main/java/codezap/category/domain/Category.java index b44c07ddb..df1818d5e 100644 --- a/backend/src/main/java/codezap/category/domain/Category.java +++ b/backend/src/main/java/codezap/category/domain/Category.java @@ -7,11 +7,12 @@ import jakarta.persistence.Id; import codezap.global.auditing.BaseTimeEntity; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class Category extends BaseTimeEntity { @Id diff --git a/backend/src/main/java/codezap/member/domain/Member.java b/backend/src/main/java/codezap/member/domain/Member.java index 711092d33..8023e2991 100644 --- a/backend/src/main/java/codezap/member/domain/Member.java +++ b/backend/src/main/java/codezap/member/domain/Member.java @@ -6,12 +6,13 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Member { @Id diff --git a/backend/src/main/java/codezap/template/domain/Snippet.java b/backend/src/main/java/codezap/template/domain/Snippet.java index c85b0ee36..a70a74e77 100644 --- a/backend/src/main/java/codezap/template/domain/Snippet.java +++ b/backend/src/main/java/codezap/template/domain/Snippet.java @@ -12,11 +12,12 @@ import jakarta.persistence.ManyToOne; import codezap.global.auditing.BaseTimeEntity; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class Snippet extends BaseTimeEntity { diff --git a/backend/src/main/java/codezap/template/domain/Tag.java b/backend/src/main/java/codezap/template/domain/Tag.java index eb7bf6fd7..7d6eb3d51 100644 --- a/backend/src/main/java/codezap/template/domain/Tag.java +++ b/backend/src/main/java/codezap/template/domain/Tag.java @@ -7,11 +7,12 @@ import jakarta.persistence.Id; import codezap.global.auditing.BaseTimeEntity; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class Tag extends BaseTimeEntity { diff --git a/backend/src/main/java/codezap/template/domain/Template.java b/backend/src/main/java/codezap/template/domain/Template.java index 75aff1906..d4f17316c 100644 --- a/backend/src/main/java/codezap/template/domain/Template.java +++ b/backend/src/main/java/codezap/template/domain/Template.java @@ -9,11 +9,12 @@ import codezap.category.domain.Category; import codezap.global.auditing.BaseTimeEntity; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class Template extends BaseTimeEntity { diff --git a/backend/src/main/java/codezap/template/domain/TemplateTag.java b/backend/src/main/java/codezap/template/domain/TemplateTag.java index eefb2ca0b..02a94916d 100644 --- a/backend/src/main/java/codezap/template/domain/TemplateTag.java +++ b/backend/src/main/java/codezap/template/domain/TemplateTag.java @@ -10,18 +10,19 @@ import jakarta.persistence.MapsId; import codezap.global.auditing.BaseTimeEntity; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; @Entity -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class TemplateTag extends BaseTimeEntity { @Embeddable - @NoArgsConstructor + @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @Getter @EqualsAndHashCode diff --git a/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java b/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java index 920dc18d2..47664f625 100644 --- a/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java +++ b/backend/src/main/java/codezap/template/domain/ThumbnailSnippet.java @@ -7,11 +7,12 @@ import jakarta.persistence.OneToOne; import codezap.global.auditing.BaseTimeEntity; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class ThumbnailSnippet extends BaseTimeEntity { From 30717163cb7329dfa931b802db536268eff8527f Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 15:54:39 +0900 Subject: [PATCH 56/69] =?UTF-8?q?refactor(category):=20=EC=9D=98=EB=AF=B8?= =?UTF-8?q?=20=EC=9E=88=EB=8A=94=20=EC=88=AB=EC=9E=90=20=EC=83=81=EC=88=98?= =?UTF-8?q?=ED=99=94=20(=EA=B8=B0=EB=B3=B8=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EC=9D=98=20id)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/codezap/category/service/CategoryService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 4b5a59da4..309d5e17b 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -15,6 +15,7 @@ @Service public class CategoryService { + public static final int DEFAULT_CATEGORY = 1; private final CategoryRepository categoryRepository; private final TemplateRepository templateRepository; @@ -52,8 +53,8 @@ public void deleteById(Long id) { if (templateRepository.existsByCategoryId(id)) { throw new CodeZapException(HttpStatus.BAD_REQUEST, "템플릿이 존재하는 카테고리는 삭제할 수 없습니다."); } - if (id == 1) { - throw new CodeZapException(HttpStatus.BAD_REQUEST, "1번 카테고리는 삭제할 수 없습니다."); + if (id == DEFAULT_CATEGORY) { + throw new CodeZapException(HttpStatus.BAD_REQUEST, "기본 카테고리는 삭제할 수 없습니다."); } categoryRepository.deleteById(id); } From 96408edeb3fbf44a45708b5c40b19bc1d91e6d6b Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 16:00:35 +0900 Subject: [PATCH 57/69] =?UTF-8?q?refactor(template):=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/codezap/template/repository/TemplateTagRepository.java | 2 -- .../src/main/java/codezap/template/service/TemplateService.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java index 41fe972d3..a0de2a4fb 100644 --- a/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TemplateTagRepository.java @@ -11,7 +11,5 @@ public interface TemplateTagRepository extends JpaRepository List findAllByTemplate(Template template); - void deleteAllByTemplate(Template template); - void deleteAllByTemplateId(Long id); } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 19c23dd79..c26a3346a 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -113,7 +113,7 @@ private void updateSnippets(UpdateTemplateRequest updateTemplateRequest, Templat } private void updateTags(UpdateTemplateRequest updateTemplateRequest, Template template) { - templateTagRepository.deleteAllByTemplate(template); + templateTagRepository.deleteAllByTemplateId(template.getId()); updateTemplateRequest.tags().stream() .map(Tag::new) .filter(tag -> !tagRepository.existsByName(tag.getName())) From 4165b92f5a70336ea1b53e055ea1481da5d38443 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 16:08:12 +0900 Subject: [PATCH 58/69] =?UTF-8?q?refactor(codezap):=20ValidationGroups?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B2=B9=EC=B9=98=EB=8A=94=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/codezap/global/validation/ValidationGroups.java | 2 -- .../java/codezap/global/validation/ValidationSequence.java | 4 +--- .../codezap/template/dto/request/CreateSnippetRequest.java | 3 +-- .../codezap/template/dto/request/CreateTemplateRequest.java | 3 +-- .../codezap/template/dto/request/UpdateSnippetRequest.java | 3 +-- .../codezap/template/dto/request/UpdateTemplateRequest.java | 3 +-- 6 files changed, 5 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/codezap/global/validation/ValidationGroups.java b/backend/src/main/java/codezap/global/validation/ValidationGroups.java index ba25f3892..327e62fbb 100644 --- a/backend/src/main/java/codezap/global/validation/ValidationGroups.java +++ b/backend/src/main/java/codezap/global/validation/ValidationGroups.java @@ -7,7 +7,5 @@ public interface SnippetOrdinalGroup {} public interface SnippetCountGroup {} - public interface ByteLengthGroup {} - public interface SizeCheckGroup {} } diff --git a/backend/src/main/java/codezap/global/validation/ValidationSequence.java b/backend/src/main/java/codezap/global/validation/ValidationSequence.java index 8c8fb97ff..c2da469db 100644 --- a/backend/src/main/java/codezap/global/validation/ValidationSequence.java +++ b/backend/src/main/java/codezap/global/validation/ValidationSequence.java @@ -2,7 +2,6 @@ import jakarta.validation.GroupSequence; -import codezap.global.validation.ValidationGroups.ByteLengthGroup; import codezap.global.validation.ValidationGroups.NotNullGroup; import codezap.global.validation.ValidationGroups.SizeCheckGroup; import codezap.global.validation.ValidationGroups.SnippetCountGroup; @@ -11,8 +10,7 @@ @GroupSequence({ NotNullGroup.class, SizeCheckGroup.class, - ByteLengthGroup.class, SnippetCountGroup.class, SnippetOrdinalGroup.class}) public interface ValidationSequence { -} \ No newline at end of file +} diff --git a/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java index 24666c7c4..cc527635d 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java @@ -5,7 +5,6 @@ import jakarta.validation.constraints.Size; import codezap.global.validation.ByteLength; -import codezap.global.validation.ValidationGroups.ByteLengthGroup; import codezap.global.validation.ValidationGroups.NotNullGroup; import codezap.global.validation.ValidationGroups.SizeCheckGroup; import io.swagger.v3.oas.annotations.media.Schema; @@ -18,7 +17,7 @@ public record CreateSnippetRequest( @Schema(description = "소스 코드", example = "public class Main { // ...") @NotBlank(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) - @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) + @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) String content, @Schema(description = "스니펫 순서", example = "1") diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index a5c273e34..dac09639e 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -8,7 +8,6 @@ import jakarta.validation.constraints.Size; import codezap.global.validation.ByteLength; -import codezap.global.validation.ValidationGroups.ByteLengthGroup; import codezap.global.validation.ValidationGroups.NotNullGroup; import codezap.global.validation.ValidationGroups.SizeCheckGroup; import codezap.template.dto.request.validation.ValidatedSnippetsOrdinalRequest; @@ -22,7 +21,7 @@ public record CreateTemplateRequest( @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") @NotNull(message = "템플릿 설명이 null 입니다.", groups = NotNullGroup.class) - @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) + @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) String description, @Schema(description = "템플릿의 스니펫 내역") diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java index 923073424..bce59a6d8 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java @@ -5,7 +5,6 @@ import jakarta.validation.constraints.Size; import codezap.global.validation.ByteLength; -import codezap.global.validation.ValidationGroups.ByteLengthGroup; import codezap.global.validation.ValidationGroups.NotNullGroup; import codezap.global.validation.ValidationGroups.SizeCheckGroup; import io.swagger.v3.oas.annotations.media.Schema; @@ -22,7 +21,7 @@ public record UpdateSnippetRequest( @Schema(description = "소스 코드", example = "public class Main { // ...") @NotBlank(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) - @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) + @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) String content, @Schema(description = "스니펫 순서", example = "1") diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java index e8df2bed1..e99232cfe 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java @@ -9,7 +9,6 @@ import jakarta.validation.constraints.Size; import codezap.global.validation.ByteLength; -import codezap.global.validation.ValidationGroups.ByteLengthGroup; import codezap.global.validation.ValidationGroups.NotNullGroup; import codezap.global.validation.ValidationGroups.SizeCheckGroup; import codezap.template.dto.request.validation.ValidatedSnippetsCountRequest; @@ -24,7 +23,7 @@ public record UpdateTemplateRequest( @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") @NotNull(message = "템플릿 설명이 null 입니다.", groups = NotNullGroup.class) - @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.", groups = ByteLengthGroup.class) + @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) String description, @Schema(description = "새로 추가한 스니펫 내역") From 8d6afea3e397cc0bacf06f6bc215f8c576f10b00 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 16:27:38 +0900 Subject: [PATCH 59/69] =?UTF-8?q?refactor(service):=20tag=EC=99=80=20tagNa?= =?UTF-8?q?me=20=EB=B3=80=EC=88=98=EB=AA=85=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/codezap/template/service/TemplateService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index c26a3346a..74b05b777 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -69,7 +69,7 @@ public Long create(CreateTemplateRequest createTemplateRequest) { private void createTags(CreateTemplateRequest createTemplateRequest, Template template) { templateTagRepository.saveAll(createTemplateRequest.tags().stream() - .map(tag -> tagRepository.save(new Tag(tag))) + .map(tagName -> tagRepository.save(new Tag(tagName))) .map(tag -> new TemplateTag(template, tag)) .toList() ); From c117e0e96312f23fc706246407d91e0bf7c27fa1 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 16:34:38 +0900 Subject: [PATCH 60/69] =?UTF-8?q?refactor(service):=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/service/TemplateService.java | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 74b05b777..f83819a24 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -75,6 +75,16 @@ private void createTags(CreateTemplateRequest createTemplateRequest, Template te ); } + private void createSnippet(CreateSnippetRequest createSnippetRequest, Template template) { + snippetRepository.save( + new Snippet( + template, createSnippetRequest.filename(), + createSnippetRequest.content(), + createSnippetRequest.ordinal() + ) + ); + } + public FindAllTemplatesResponse findAll() { return FindAllTemplatesResponse.from(thumbnailSnippetRepository.findAll()); } @@ -112,11 +122,32 @@ private void updateSnippets(UpdateTemplateRequest updateTemplateRequest, Templat updateTemplateRequest.deleteSnippetIds().forEach(snippetRepository::deleteById); } + private void updateSnippet(UpdateSnippetRequest updateSnippetRequest) { + Snippet snippet = snippetRepository.fetchById(updateSnippetRequest.id()); + snippet.updateSnippet(updateSnippetRequest.filename(), updateSnippetRequest.content(), + updateSnippetRequest.ordinal()); + } + + private static boolean isThumbnailSnippetDeleted( + UpdateTemplateRequest updateTemplateRequest, + ThumbnailSnippet thumbnailSnippet + ) { + return updateTemplateRequest.deleteSnippetIds().contains(thumbnailSnippet.getId()); + } + + private void updateThumbnailSnippet(Template template, ThumbnailSnippet thumbnailSnippet) { + List snippets = snippetRepository.findAllByTemplateAndOrdinal(template, FIRST_ORDINAL); + snippets.stream() + .filter(snippet -> !Objects.equals(thumbnailSnippet.getSnippet().getId(), snippet.getId())) + .findFirst() + .ifPresent(thumbnailSnippet::updateThumbnailSnippet); + } + private void updateTags(UpdateTemplateRequest updateTemplateRequest, Template template) { templateTagRepository.deleteAllByTemplateId(template.getId()); updateTemplateRequest.tags().stream() + .filter(tagName -> !tagRepository.existsByName(tagName)) .map(Tag::new) - .filter(tag -> !tagRepository.existsByName(tag.getName())) .forEach(tagRepository::save); List tags = updateTemplateRequest.tags().stream() @@ -139,35 +170,4 @@ public void deleteById(Long id) { templateTagRepository.deleteAllByTemplateId(id); templateRepository.deleteById(id); } - - private static boolean isThumbnailSnippetDeleted( - UpdateTemplateRequest updateTemplateRequest, - ThumbnailSnippet thumbnailSnippet - ) { - return updateTemplateRequest.deleteSnippetIds().contains(thumbnailSnippet.getId()); - } - - private void updateThumbnailSnippet(Template template, ThumbnailSnippet thumbnailSnippet) { - List snippets = snippetRepository.findAllByTemplateAndOrdinal(template, FIRST_ORDINAL); - snippets.stream() - .filter(snippet -> !Objects.equals(thumbnailSnippet.getSnippet().getId(), snippet.getId())) - .findFirst() - .ifPresent(thumbnailSnippet::updateThumbnailSnippet); - } - - private void createSnippet(CreateSnippetRequest createSnippetRequest, Template template) { - snippetRepository.save( - new Snippet( - template, createSnippetRequest.filename(), - createSnippetRequest.content(), - createSnippetRequest.ordinal() - ) - ); - } - - private void updateSnippet(UpdateSnippetRequest updateSnippetRequest) { - Snippet snippet = snippetRepository.fetchById(updateSnippetRequest.id()); - snippet.updateSnippet(updateSnippetRequest.filename(), updateSnippetRequest.content(), - updateSnippetRequest.ordinal()); - } } From 99b6fd931cb84fcf70aa65b97b9dc9d33794fcaf Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 16:37:24 +0900 Subject: [PATCH 61/69] =?UTF-8?q?refactor(service):=20=ED=83=9C=EA=B7=B8?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/service/TemplateService.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index f83819a24..f9afeff94 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -68,11 +68,14 @@ public Long create(CreateTemplateRequest createTemplateRequest) { } private void createTags(CreateTemplateRequest createTemplateRequest, Template template) { - templateTagRepository.saveAll(createTemplateRequest.tags().stream() - .map(tagName -> tagRepository.save(new Tag(tagName))) - .map(tag -> new TemplateTag(template, tag)) - .toList() - ); + createTemplateRequest.tags().stream() + .filter(tagName -> !tagRepository.existsByName(tagName)) + .map(Tag::new) + .forEach(tagRepository::save); + + createTemplateRequest.tags().stream() + .map(tagRepository::findByName) + .forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); } private void createSnippet(CreateSnippetRequest createSnippetRequest, Template template) { @@ -150,10 +153,9 @@ private void updateTags(UpdateTemplateRequest updateTemplateRequest, Template te .map(Tag::new) .forEach(tagRepository::save); - List tags = updateTemplateRequest.tags().stream() + updateTemplateRequest.tags().stream() .map(tagRepository::findByName) - .toList(); - tags.forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); + .forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); } private void validateSnippetsCount(UpdateTemplateRequest updateTemplateRequest, Template template) { From d7c748132d357df2ff4fb6faa60caa4a6fe07beb Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 21:20:37 +0900 Subject: [PATCH 62/69] =?UTF-8?q?refactor(controller):=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EA=B0=9C=ED=96=89=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/codezap/category/controller/CategoryController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/main/java/codezap/category/controller/CategoryController.java b/backend/src/main/java/codezap/category/controller/CategoryController.java index 0ca9511bc..7ef8c0788 100644 --- a/backend/src/main/java/codezap/category/controller/CategoryController.java +++ b/backend/src/main/java/codezap/category/controller/CategoryController.java @@ -55,7 +55,6 @@ public ResponseEntity updateCategory( @DeleteMapping("/{id}") public ResponseEntity deleteCategory(@PathVariable Long id) { categoryService.deleteById(id); - return ResponseEntity.noContent() - .build(); + return ResponseEntity.noContent().build(); } } From eac479c44f9ad4eec5acddf1ddf273266645a000 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 21:27:35 +0900 Subject: [PATCH 63/69] =?UTF-8?q?refactor(service):=20=EC=83=81=EC=88=98?= =?UTF-8?q?=20=EC=A0=91=EA=B7=BC=20=EC=A0=9C=EC=96=B4=EC=9E=90=20private?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/codezap/category/service/CategoryService.java | 2 +- .../src/main/java/codezap/template/service/TemplateService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index 309d5e17b..ce934712b 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -15,7 +15,7 @@ @Service public class CategoryService { - public static final int DEFAULT_CATEGORY = 1; + private static final long DEFAULT_CATEGORY = 1L; private final CategoryRepository categoryRepository; private final TemplateRepository templateRepository; diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index f9afeff94..601f7e690 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -30,7 +30,7 @@ @Service public class TemplateService { - public static final int FIRST_ORDINAL = 1; + private static final int FIRST_ORDINAL = 1; private final ThumbnailSnippetRepository thumbnailSnippetRepository; private final TemplateRepository templateRepository; From 34c94c852f89042553bd5a0de4fa310214773c1d Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 21:28:59 +0900 Subject: [PATCH 64/69] =?UTF-8?q?refactor(domain):=20=EC=83=81=EC=88=98?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/codezap/template/domain/Snippet.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/codezap/template/domain/Snippet.java b/backend/src/main/java/codezap/template/domain/Snippet.java index a70a74e77..31974afe8 100644 --- a/backend/src/main/java/codezap/template/domain/Snippet.java +++ b/backend/src/main/java/codezap/template/domain/Snippet.java @@ -21,7 +21,7 @@ @Getter public class Snippet extends BaseTimeEntity { - private static final String CODE_LINE_BREAK = "\n"; + private static final String LINE_BREAK = "\n"; private static final int THUMBNAIL_SNIPPET_LINE_HEIGHT = 10; @Id @@ -48,9 +48,9 @@ public Snippet(Template template, String filename, String content, Integer ordin } public String getThumbnailContent() { - return Arrays.stream(content.split(CODE_LINE_BREAK)) + return Arrays.stream(content.split(LINE_BREAK)) .limit(THUMBNAIL_SNIPPET_LINE_HEIGHT) - .collect(Collectors.joining(CODE_LINE_BREAK)); + .collect(Collectors.joining(LINE_BREAK)); } public void updateSnippet(String filename, String content, Integer ordinal) { From 4cbfb71bec47718dca8122ac3b97b6afeb6bafbc Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 21:30:32 +0900 Subject: [PATCH 65/69] =?UTF-8?q?refactor(response):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EA=B0=9C=ED=96=89=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codezap/template/dto/response/FindTemplateResponse.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/main/java/codezap/template/dto/response/FindTemplateResponse.java b/backend/src/main/java/codezap/template/dto/response/FindTemplateResponse.java index 3920cfdd8..42fe1f294 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindTemplateResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindTemplateResponse.java @@ -51,9 +51,7 @@ private static List mapToFindAllSnippetByTempl .toList(); } - private static List mapToFindTagByTemplateResponse( - List tags - ) { + private static List mapToFindTagByTemplateResponse(List tags) { return tags.stream() .map(FindTagResponse::from) .toList(); From 8b1b520fa47e07f757b431bca37a95ceb9f3db74 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 21:36:46 +0900 Subject: [PATCH 66/69] =?UTF-8?q?refactor(codezap):=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=83=9D=EC=84=B1=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/controller/TemplateController.java | 3 ++- .../java/codezap/template/service/TemplateService.java | 2 +- .../category/controller/CategoryControllerTest.java | 2 +- .../template/controller/TemplateControllerTest.java | 10 +++++----- .../codezap/template/service/TemplateServiceTest.java | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/codezap/template/controller/TemplateController.java b/backend/src/main/java/codezap/template/controller/TemplateController.java index 5a4cec80a..15298c9b5 100644 --- a/backend/src/main/java/codezap/template/controller/TemplateController.java +++ b/backend/src/main/java/codezap/template/controller/TemplateController.java @@ -33,7 +33,8 @@ public TemplateController(TemplateService templateService) { public ResponseEntity create( @Validated(ValidationSequence.class) @RequestBody CreateTemplateRequest createTemplateRequest ) { - return ResponseEntity.created(URI.create("/templates/" + templateService.create(createTemplateRequest))) + Long createdTemplateId = templateService.createTemplate(createTemplateRequest); + return ResponseEntity.created(URI.create("/templates/" + createdTemplateId)) .build(); } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 601f7e690..e29370d45 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -53,7 +53,7 @@ public TemplateService(ThumbnailSnippetRepository thumbnailSnippetRepository, } @Transactional - public Long create(CreateTemplateRequest createTemplateRequest) { + public Long createTemplate(CreateTemplateRequest createTemplateRequest) { Category category = categoryRepository.fetchById(createTemplateRequest.categoryId()); Template template = templateRepository.save( new Template(createTemplateRequest.title(), createTemplateRequest.description(), category) diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index e99ed71c3..224efb7bc 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -185,7 +185,7 @@ void updateCategoryFailWithDuplicatedName() { @DisplayName("카테고리 삭제 실패: 템플릿이 존재하는 카테고리는 삭제 불가능") void updateCategoryFailWithLongName() { // given - templateService.create(new CreateTemplateRequest( + templateService.createTemplate(new CreateTemplateRequest( "title", "description", List.of(new CreateSnippetRequest("filename", "content", 1)), diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index 9641ff454..217695f9d 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -194,8 +194,8 @@ void findAllTemplatesSuccess() { categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest1 = createTemplateRequestWithTwoSnippets("title1"); CreateTemplateRequest templateRequest2 = createTemplateRequestWithTwoSnippets("title2"); - templateService.create(templateRequest1); - templateService.create(templateRequest2); + templateService.createTemplate(templateRequest1); + templateService.createTemplate(templateRequest2); // when & then RestAssured.given().log().all() @@ -215,7 +215,7 @@ void findOneTemplateSuccess() { // given categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.create(templateRequest); + templateService.createTemplate(templateRequest); // when & then RestAssured.given().log().all() @@ -434,7 +434,7 @@ private void createTemplateAndTwoCategories() { categoryService.create(new CreateCategoryRequest("category1")); categoryService.create(new CreateCategoryRequest("category2")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.create(templateRequest); + templateService.createTemplate(templateRequest); } } @@ -448,7 +448,7 @@ void deleteTemplateSuccess() { // given categoryService.create(new CreateCategoryRequest("category")); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.create(templateRequest); + templateService.createTemplate(templateRequest); // when & then RestAssured.given().log().all() diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index 728e046b5..801ca13b4 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -76,7 +76,7 @@ void createTemplateSuccess() { CreateTemplateRequest createTemplateRequest = makeTemplateRequest("title"); // when - Long id = templateService.create(createTemplateRequest); + Long id = templateService.createTemplate(createTemplateRequest); Template template = templateRepository.fetchById(id); // then From 39336abd30a5de74675a4174aa611be8df67b2fc Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 21:46:17 +0900 Subject: [PATCH 67/69] =?UTF-8?q?refactor(service):=20stream=EC=9D=84=20?= =?UTF-8?q?=ED=86=B5=ED=95=9C=20save=20=EB=8C=80=EC=8B=A0=20saveAll?= =?UTF-8?q?=EC=9D=84=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/service/TemplateService.java | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index e29370d45..154cd76fb 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -59,8 +59,11 @@ public Long createTemplate(CreateTemplateRequest createTemplateRequest) { new Template(createTemplateRequest.title(), createTemplateRequest.description(), category) ); createTags(createTemplateRequest, template); - createTemplateRequest.snippets() - .forEach(createSnippetRequest -> createSnippet(createSnippetRequest, template)); + snippetRepository.saveAll( + createTemplateRequest.snippets().stream() + .map(createSnippetRequest -> createSnippet(createSnippetRequest, template)) + .toList() + ); Snippet thumbnailSnippet = snippetRepository.findByTemplateAndOrdinal(template, FIRST_ORDINAL); thumbnailSnippetRepository.save(new ThumbnailSnippet(template, thumbnailSnippet)); @@ -68,23 +71,26 @@ public Long createTemplate(CreateTemplateRequest createTemplateRequest) { } private void createTags(CreateTemplateRequest createTemplateRequest, Template template) { - createTemplateRequest.tags().stream() + tagRepository.saveAll( + createTemplateRequest.tags().stream() .filter(tagName -> !tagRepository.existsByName(tagName)) .map(Tag::new) - .forEach(tagRepository::save); + .toList() + ); - createTemplateRequest.tags().stream() + templateTagRepository.saveAll( + createTemplateRequest.tags().stream() .map(tagRepository::findByName) - .forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); + .map(tag -> new TemplateTag(template, tag)) + .toList() + ); } - private void createSnippet(CreateSnippetRequest createSnippetRequest, Template template) { - snippetRepository.save( - new Snippet( - template, createSnippetRequest.filename(), - createSnippetRequest.content(), - createSnippetRequest.ordinal() - ) + private Snippet createSnippet(CreateSnippetRequest createSnippetRequest, Template template) { + return new Snippet( + template, createSnippetRequest.filename(), + createSnippetRequest.content(), + createSnippetRequest.ordinal() ); } @@ -148,14 +154,19 @@ private void updateThumbnailSnippet(Template template, ThumbnailSnippet thumbnai private void updateTags(UpdateTemplateRequest updateTemplateRequest, Template template) { templateTagRepository.deleteAllByTemplateId(template.getId()); - updateTemplateRequest.tags().stream() + tagRepository.saveAll( + updateTemplateRequest.tags().stream() .filter(tagName -> !tagRepository.existsByName(tagName)) .map(Tag::new) - .forEach(tagRepository::save); + .toList() + ); - updateTemplateRequest.tags().stream() + templateTagRepository.saveAll( + updateTemplateRequest.tags().stream() .map(tagRepository::findByName) - .forEach(tag -> templateTagRepository.save(new TemplateTag(template, tag))); + .map(tag -> new TemplateTag(template, tag)) + .toList() + ); } private void validateSnippetsCount(UpdateTemplateRequest updateTemplateRequest, Template template) { From d12503532b6434fde80d7037a21fe679004aaa72 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Mon, 5 Aug 2024 21:50:55 +0900 Subject: [PATCH 68/69] =?UTF-8?q?refactor(service):=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=EB=AC=B8=EC=9D=84=20=EB=B3=84=EB=8F=84=EC=9D=98=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/codezap/category/service/CategoryService.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/codezap/category/service/CategoryService.java b/backend/src/main/java/codezap/category/service/CategoryService.java index ce934712b..908ce5189 100644 --- a/backend/src/main/java/codezap/category/service/CategoryService.java +++ b/backend/src/main/java/codezap/category/service/CategoryService.java @@ -50,12 +50,20 @@ private void validateDuplicatedCategory(String categoryName) { } public void deleteById(Long id) { + assertNoTemplates(id); + assertDefaultCategory(id); + categoryRepository.deleteById(id); + } + + private void assertNoTemplates(Long id) { if (templateRepository.existsByCategoryId(id)) { throw new CodeZapException(HttpStatus.BAD_REQUEST, "템플릿이 존재하는 카테고리는 삭제할 수 없습니다."); } + } + + private static void assertDefaultCategory(Long id) { if (id == DEFAULT_CATEGORY) { throw new CodeZapException(HttpStatus.BAD_REQUEST, "기본 카테고리는 삭제할 수 없습니다."); } - categoryRepository.deleteById(id); } } From 05cd1b46821ac5e66ecf696786ade0cc08edcab2 Mon Sep 17 00:00:00 2001 From: HoeSeong123 Date: Tue, 6 Aug 2024 13:36:16 +0900 Subject: [PATCH 69/69] =?UTF-8?q?refactor(template):=20findBy=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EC=9D=98=20=EB=A6=AC=ED=84=B4=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20Optional=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/SnippetRepository.java | 3 +- .../template/repository/TagRepository.java | 4 +- .../ThumbnailSnippetRepository.java | 4 +- .../template/service/TemplateService.java | 48 ++++++++++++------- .../repository/SnippetRepositoryTest.java | 5 +- 5 files changed, 43 insertions(+), 21 deletions(-) diff --git a/backend/src/main/java/codezap/template/repository/SnippetRepository.java b/backend/src/main/java/codezap/template/repository/SnippetRepository.java index 898b5d8ba..bfe6da88e 100644 --- a/backend/src/main/java/codezap/template/repository/SnippetRepository.java +++ b/backend/src/main/java/codezap/template/repository/SnippetRepository.java @@ -1,6 +1,7 @@ package codezap.template.repository; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.http.HttpStatus; @@ -17,7 +18,7 @@ default Snippet fetchById(Long id) { List findAllByTemplate(Template template); - Snippet findByTemplateAndOrdinal(Template template, int ordinal); + Optional findByTemplateAndOrdinal(Template template, int ordinal); List findAllByTemplateAndOrdinal(Template template, int ordinal); diff --git a/backend/src/main/java/codezap/template/repository/TagRepository.java b/backend/src/main/java/codezap/template/repository/TagRepository.java index 612dee2b3..8eb30c93e 100644 --- a/backend/src/main/java/codezap/template/repository/TagRepository.java +++ b/backend/src/main/java/codezap/template/repository/TagRepository.java @@ -1,5 +1,7 @@ package codezap.template.repository; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import codezap.template.domain.Tag; @@ -7,5 +9,5 @@ public interface TagRepository extends JpaRepository { boolean existsByName(String name); - Tag findByName(String name); + Optional findByName(String name); } diff --git a/backend/src/main/java/codezap/template/repository/ThumbnailSnippetRepository.java b/backend/src/main/java/codezap/template/repository/ThumbnailSnippetRepository.java index bb836ed6e..a8fa02650 100644 --- a/backend/src/main/java/codezap/template/repository/ThumbnailSnippetRepository.java +++ b/backend/src/main/java/codezap/template/repository/ThumbnailSnippetRepository.java @@ -1,12 +1,14 @@ package codezap.template.repository; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import codezap.template.domain.Template; import codezap.template.domain.ThumbnailSnippet; public interface ThumbnailSnippetRepository extends JpaRepository { - ThumbnailSnippet findByTemplate(Template template); + Optional findByTemplate(Template template); void deleteByTemplateId(Long id); } diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 154cd76fb..281c9a08f 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -65,7 +65,8 @@ public Long createTemplate(CreateTemplateRequest createTemplateRequest) { .toList() ); - Snippet thumbnailSnippet = snippetRepository.findByTemplateAndOrdinal(template, FIRST_ORDINAL); + Snippet thumbnailSnippet = snippetRepository.findByTemplateAndOrdinal(template, FIRST_ORDINAL) + .orElseThrow(this::throwNotFoundSnippet); thumbnailSnippetRepository.save(new ThumbnailSnippet(template, thumbnailSnippet)); return template.getId(); } @@ -73,17 +74,17 @@ public Long createTemplate(CreateTemplateRequest createTemplateRequest) { private void createTags(CreateTemplateRequest createTemplateRequest, Template template) { tagRepository.saveAll( createTemplateRequest.tags().stream() - .filter(tagName -> !tagRepository.existsByName(tagName)) - .map(Tag::new) - .toList() + .filter(tagName -> !tagRepository.existsByName(tagName)) + .map(Tag::new) + .toList() ); - templateTagRepository.saveAll( - createTemplateRequest.tags().stream() - .map(tagRepository::findByName) - .map(tag -> new TemplateTag(template, tag)) - .toList() - ); + templateTagRepository.saveAll( + createTemplateRequest.tags().stream() + .map(tag -> tagRepository.findByName(tag).orElseThrow(this::throwNotFoundTag)) + .map(tag -> new TemplateTag(template, tag)) + .toList() + ); } private Snippet createSnippet(CreateSnippetRequest createSnippetRequest, Template template) { @@ -122,7 +123,8 @@ private void updateSnippets(UpdateTemplateRequest updateTemplateRequest, Templat updateTemplateRequest.createSnippets() .forEach(createSnippetRequest -> createSnippet(createSnippetRequest, template)); - ThumbnailSnippet thumbnailSnippet = thumbnailSnippetRepository.findByTemplate(template); + ThumbnailSnippet thumbnailSnippet = thumbnailSnippetRepository.findByTemplate(template) + .orElseThrow(this::throwNotFoundThumbnailSnippet); if (isThumbnailSnippetDeleted(updateTemplateRequest, thumbnailSnippet)) { updateThumbnailSnippet(template, thumbnailSnippet); @@ -156,16 +158,16 @@ private void updateTags(UpdateTemplateRequest updateTemplateRequest, Template te templateTagRepository.deleteAllByTemplateId(template.getId()); tagRepository.saveAll( updateTemplateRequest.tags().stream() - .filter(tagName -> !tagRepository.existsByName(tagName)) - .map(Tag::new) - .toList() + .filter(tagName -> !tagRepository.existsByName(tagName)) + .map(Tag::new) + .toList() ); templateTagRepository.saveAll( updateTemplateRequest.tags().stream() - .map(tagRepository::findByName) - .map(tag -> new TemplateTag(template, tag)) - .toList() + .map(tag -> tagRepository.findByName(tag).orElseThrow(this::throwNotFoundTag)) + .map(tag -> new TemplateTag(template, tag)) + .toList() ); } @@ -183,4 +185,16 @@ public void deleteById(Long id) { templateTagRepository.deleteAllByTemplateId(id); templateRepository.deleteById(id); } + + private CodeZapException throwNotFoundSnippet() { + throw new CodeZapException(HttpStatus.NOT_FOUND, "해당하는 스니펫이 존재하지 않습니다."); + } + + private CodeZapException throwNotFoundTag() { + throw new CodeZapException(HttpStatus.BAD_REQUEST, "해당하는 태그가 존재하지 않습니다."); + } + + private CodeZapException throwNotFoundThumbnailSnippet() { + throw new CodeZapException(HttpStatus.BAD_REQUEST, "해당하는 썸네일 스니펫이 존재하지 않습니다."); + } } diff --git a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java index 33dea21f2..d4c3dc05c 100644 --- a/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java +++ b/backend/src/test/java/codezap/template/repository/SnippetRepositoryTest.java @@ -9,12 +9,14 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql.ExecutionPhase; import org.springframework.transaction.annotation.Transactional; import codezap.category.domain.Category; import codezap.category.repository.CategoryRepository; +import codezap.global.exception.CodeZapException; import codezap.template.domain.Snippet; import codezap.template.domain.Template; @@ -39,7 +41,8 @@ void findOneSnippetSuccessWithTemplateAndOrdinal() { Snippet snippet1 = snippetRepository.save(new Snippet(template, "filename1", "content1", 1)); Snippet snippet2 = snippetRepository.save(new Snippet(template, "filename2", "content2", 2)); - Snippet foundSnippet = snippetRepository.findByTemplateAndOrdinal(template, 2); + Snippet foundSnippet = snippetRepository.findByTemplateAndOrdinal(template, 2) + .orElseThrow(() -> new CodeZapException(HttpStatus.NOT_FOUND, "해당하는 스니펫이 존재하지 않습니다.")); assertAll( () -> assertThat(foundSnippet.getTemplate().getTitle()).isEqualTo(template.getTitle()),