From ddef45ef55eb6055cae2e12929af30a4682ee14c Mon Sep 17 00:00:00 2001 From: SeongHoon Park Date: Fri, 22 Mar 2024 21:03:40 +0900 Subject: [PATCH 001/103] chore: Add PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..d49815a7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,14 @@ +## ๐Ÿ“ ์ž‘์—… ๋‚ด์šฉ +> ์ด๋ฒˆ PR์—์„œ ์ž‘์—…ํ•œ ๋‚ด์šฉ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. +> +> ๊ณ ๋ฏผํ•œ ๋‚ด์šฉ์ด๋‚˜, ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ–ˆ๋Š”์ง€ ์ž‘์„ฑํ•ด์ฃผ๋Š”๊ฒƒ๋„ ์ข‹์Šต๋‹ˆ๋‹ค. +> +> ![์ด๋ฏธ์ง€ ์ฒจ๋ถ€ํ•˜๋ฉด ๋” ์ข‹์•„์š”]() + +## ๐Ÿฆพ ์—ฐ๊ด€๋œ ์ด์Šˆ +> ex) JIRA๋งํฌ, #์ด์Šˆ๋ฒˆํ˜ธ + +## ๐Ÿ’ฌ๋ฆฌ๋ทฐ ์š”๊ตฌ์‚ฌํ•ญ +> ๋ฆฌ๋ทฐ์–ด๊ฐ€ ํŠน๋ณ„ํžˆ ๋ด์ฃผ์—ˆ์œผ๋ฉด ํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ์ž‘์„ฑํ•ด์ฃผ์„ธ์š” +> +> ex) ๋ฉ”์„œ๋“œ XXX์˜ ์ด๋ฆ„์„ ๋” ์ž˜ ์ง“๊ณ  ์‹ถ์€๋ฐ ํ˜น์‹œ ์ข‹์€ ๋ช…์นญ์ด ์žˆ์„๊นŒ์š”? From ff091e1c34391e5371d3bfa599792e41b48aefc6 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 04:44:17 +0900 Subject: [PATCH 002/103] =?UTF-8?q?feat:=20sql=EB=AC=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20-=20MagazineCategory=EC=97=90=20slug,=20parent=5Fid?= =?UTF-8?q?=20=EC=B9=BC=EB=9F=BC=20=EC=B6=94=EA=B0=80=20-=20MagazineCatego?= =?UTF-8?q?ry=EC=97=90=20=EA=B0=99=EC=9D=80=20parent=5Fid=EB=A5=BC=20?= =?UTF-8?q?=EB=91=94=20=EC=9A=94=EC=86=8C=EB=8A=94=20=EA=B0=99=EC=9D=80=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9D=84=20=EC=93=B8=20=EC=88=98=20=EC=97=86?= =?UTF-8?q?=EA=B2=8C=20=EC=A1=B0=EA=B1=B4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/migration/V202403051153__application.sql | 12 ++++++++++++ .../db/migration/V202403051154__application.sql | 8 ++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/main/resources/db/migration/V202403051153__application.sql create mode 100644 src/main/resources/db/migration/V202403051154__application.sql diff --git a/src/main/resources/db/migration/V202403051153__application.sql b/src/main/resources/db/migration/V202403051153__application.sql new file mode 100644 index 00000000..84d56a1d --- /dev/null +++ b/src/main/resources/db/migration/V202403051153__application.sql @@ -0,0 +1,12 @@ +# +# ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ๊ณ„์ธตํ™” ํ•ฉ๋‹ˆ๋‹ค +# ์ž‘์„ฑ์ž : gimdonghyeon (haroya01@naver.com) +# ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-22 +# ํ˜„์žฌ ๋ฒ„์ „ : V202403051153 (์ด์ „ ๋ฒ„์ „ : V202403051152__application.sql) + +ALTER TABLE `magazine_category` + ADD `parent_id` BIGINT NULL, + ADD `slug` VARCHAR(30) NULL UNIQUE, + ADD CONSTRAINT `fk_magazine_category_parent` FOREIGN KEY (`parent_id`) REFERENCES `magazine_category` (`category_id`); + + diff --git a/src/main/resources/db/migration/V202403051154__application.sql b/src/main/resources/db/migration/V202403051154__application.sql new file mode 100644 index 00000000..b3a5206c --- /dev/null +++ b/src/main/resources/db/migration/V202403051154__application.sql @@ -0,0 +1,8 @@ + +# ์นดํ…Œ๊ณ ๋ฆฌ ์ด๋ฆ„๊ณผ ๋ถ€๋ชจ์— ์œ ๋‹ˆํฌ ๋ถ€์—ฌ +# ์ž‘์„ฑ์ž : gimdonghyeon (haroya01@naver.com) +# ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-23 +# ํ˜„์žฌ ๋ฒ„์ „ : V202403051154 (์ด์ „ ๋ฒ„์ „ : V202403051153__application.sql) + +ALTER TABLE magazine_category ADD CONSTRAINT unique_name_parent_id UNIQUE (name, parent_id); + From 83422a796055396b4f543249d7230c1107845006 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 04:54:35 +0900 Subject: [PATCH 003/103] =?UTF-8?q?feat:=20=EB=B3=80=EA=B2=BD=EB=90=9C=20M?= =?UTF-8?q?agazineCategory=20entity=20jpa=20=EB=A7=A4=ED=95=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../magazine/entity/MagazineCategory.java | 78 ++++++++++++++++++- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java index 696cc307..54ae73f7 100644 --- a/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java +++ b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java @@ -1,20 +1,27 @@ package com.example.codebase.domain.magazine.entity; +import com.example.codebase.domain.magazine.dto.MagazineCategoryRequest; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.Where; - import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static lombok.AccessLevel.PROTECTED; @Entity -@Table(name = "magazine_category") +@Table(name = "magazine_category", uniqueConstraints = { + @UniqueConstraint(columnNames = {"name", "parent_id"}) +}) @Builder @Getter @AllArgsConstructor -@NoArgsConstructor +@NoArgsConstructor(access = PROTECTED) @Where(clause = "is_deleted = false") public class MagazineCategory { @@ -31,14 +38,77 @@ public class MagazineCategory { private Boolean isDeleted = false; @Builder.Default + @Column(name = "created_time", updatable = false) private LocalDateTime createdTime = LocalDateTime.now(); @Builder.Default private LocalDateTime updatedTime = LocalDateTime.now(); - public static MagazineCategory toEntity(String name) { + @Column(name = "slug", nullable = false, unique = true) + private String slug; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parent_id") + private MagazineCategory parent; + + @Builder.Default + @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + private List children = new ArrayList<>(); + + @OneToOne(mappedBy = "category", fetch = FetchType.LAZY) + private Magazine magazine; + + public static MagazineCategory toEntity(String name, String slug, MagazineCategory parent) { return MagazineCategory.builder() .name(name) + .slug(slug) + .parent(parent) .build(); } + + public void delete() { + if (!this.getChildren().isEmpty()) { + throw new RuntimeException("ํ•˜์œ„ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค."); + } + + if (this.magazine != null) { + this.magazine.delete(); + } + this.isDeleted = true; + this.updatedTime = LocalDateTime.now(); + } + + public void checkDepth() { + int depth = 0; + MagazineCategory parent = this.parent; + while (parent != null) { + depth++; + parent = parent.getParent(); + } + + if (depth >= 2) { + throw new RuntimeException("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ตœ๋Œ€ 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."); + } + } + + public void update(MagazineCategoryRequest.Update request) { + Optional.ofNullable(request.getName()) + .ifPresent(name -> this.name = name); + + Optional.ofNullable(request.getSlug()) + .ifPresent(slug -> this.slug = slug); + + this.updatedTime = LocalDateTime.now(); + } + + public void update(MagazineCategory newParent) { + if (this.parent != null) { + this.parent.getChildren().remove(this); + } + + this.parent = newParent; + if (newParent != null) { + newParent.getChildren().add(this); + } + } } From efce981fa3f7b65fec5b6225021f4c48955650cb Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 04:56:21 +0900 Subject: [PATCH 004/103] =?UTF-8?q?feat:=20magazineRequest,response=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EC=82=AC=ED=95=AD=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EA=B2=8C=20dto=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../magazine/dto/MagazineCategoryRequest.java | 34 +++++++++++++++++++ .../dto/MagazineCategoryResponse.java | 15 +++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryRequest.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryRequest.java index ee86798f..3fe7e486 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryRequest.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryRequest.java @@ -1,18 +1,52 @@ package com.example.codebase.domain.magazine.dto; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; +import static lombok.AccessLevel.PROTECTED; + public class MagazineCategoryRequest { @Getter @Setter + @NoArgsConstructor(access = PROTECTED) + @AllArgsConstructor @Schema(name = "MagazineCategoryRequest", description = "์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ ์š”์ฒญ") public static class Create { @Schema(description = "์นดํ…Œ๊ณ ๋ฆฌ ์ด๋ฆ„", example = "Post") + @NotNull(message = "์นดํ…Œ๊ณ ๋ฆฌ ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") private String name; + + @Schema(description = "์นดํ…Œ๊ณ ๋ฆฌ ์Šฌ๋Ÿฌ๊ทธ(์˜์–ด)", example = "post-category") + @NotNull(message = "์Šฌ๋Ÿฌ๊ทธ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") + @Pattern(regexp = "^[a-zA-Z]+(-[a-zA-Z]+)?$", message = "์Šฌ๋Ÿฌ๊ทธ๋Š” ์˜์–ด, - ๋กœ๋งŒ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.") + private String slug; + + @Schema(description = "๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ID", example = "1") + private Long parentId; } + @Getter + @Setter + @NoArgsConstructor(access = PROTECTED) + @AllArgsConstructor + @Schema(name = "MagazineCategoryRequest.Update", description = "์นดํ…Œ๊ณ ๋ฆฌ ์ˆ˜์ • ์š”์ฒญ") + public static class Update { + + @Schema(description = "์นดํ…Œ๊ณ ๋ฆฌ ์ด๋ฆ„", example = "Post") + private String name; + + @Schema(description = "์นดํ…Œ๊ณ ๋ฆฌ ์Šฌ๋Ÿฌ๊ทธ(์˜์–ด)", example = "post-category") + @Pattern(regexp = "^[a-zA-Z]+(-[a-zA-Z]+)?$", message = "์Šฌ๋Ÿฌ๊ทธ๋Š” ์˜์–ด, - ๋กœ๋งŒ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.") + private String slug; + + @Schema(description = "๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ID", example = "1") + private Long parentId; + } } diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryResponse.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryResponse.java index b1bdd07e..8cae2804 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryResponse.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryResponse.java @@ -7,7 +7,9 @@ import lombok.Setter; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; public class MagazineCategoryResponse { @@ -20,18 +22,30 @@ public static class Get { private String name; + private String slug; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime createdTime; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime updatedTime; + private List childrenCategories = new ArrayList<>(); + public static MagazineCategoryResponse.Get from(MagazineCategory category) { Get get = new Get(); get.setId(category.getId()); get.setName(category.getName()); + get.setSlug(category.getSlug()); get.setCreatedTime(category.getCreatedTime()); get.setUpdatedTime(category.getUpdatedTime()); + + if (category.getChildren() != null) { + get.setChildrenCategories(category.getChildren().stream().map(Get::from).collect(Collectors.toList())); + } else { + get.setChildrenCategories(new ArrayList<>()); + } + return get; } } @@ -43,7 +57,6 @@ public static class GetAll { private List categories; - public static GetAll from(List all) { GetAll getAll = new GetAll(); getAll.setCategories(all.stream() From 04f84ff6def57da84b4947f9a8591665a14d3a78 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 04:58:40 +0900 Subject: [PATCH 005/103] =?UTF-8?q?feat:=20magazineCategory=20api=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20get=20("/slug")=20=EC=8A=AC=EB=9F=AC?= =?UTF-8?q?=EA=B7=B8=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=ED=95=98=EC=9C=84=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=EB=A5=BC=20=EC=B0=BE?= =?UTF-8?q?=EC=9D=84=20=EC=88=98=20=EC=9E=88=EC=8A=B5=EB=8B=88=EB=8B=A4=20?= =?UTF-8?q?-=20delete=20("id")=20=ED=95=B4=EB=8B=B9=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=EB=A5=BC=20=EC=82=AD=EC=A0=9C=ED=95=A0?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EC=8A=B5=EB=8B=88=EB=8B=A4=20-=20put=20("?= =?UTF-8?q?id")=20=ED=95=B4=EB=8B=B9=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=A0=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MagazineCategoryController.java | 33 +++++++--- .../domain/magazine/entity/Magazine.java | 5 ++ .../MagazineCategoryRepository.java | 16 +++++ .../service/MagazineCategoryService.java | 63 ++++++++++++++++++- 4 files changed, 107 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/MagazineCategoryController.java b/src/main/java/com/example/codebase/controller/MagazineCategoryController.java index cdc3d02f..dd5208bb 100644 --- a/src/main/java/com/example/codebase/controller/MagazineCategoryController.java +++ b/src/main/java/com/example/codebase/controller/MagazineCategoryController.java @@ -2,15 +2,14 @@ import com.example.codebase.annotation.AdminOnly; +import com.example.codebase.domain.magazine.dto.MagazineCategoryRequest; import com.example.codebase.domain.magazine.dto.MagazineCategoryResponse; import com.example.codebase.domain.magazine.service.MagazineCategoryService; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import org.springframework.http.HttpStatus; 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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @Tag(name = "๋งค๊ฑฐ์ง„ ์นดํ…Œ๊ณ ๋ฆฌ API", description = "๋งค๊ฑฐ์ง„ ์นดํ…Œ๊ณ ๋ฆฌ ๊ด€๋ จ API") @RestController @@ -25,15 +24,35 @@ public MagazineCategoryController(MagazineCategoryService magazineCategoryServic @PostMapping @AdminOnly - public ResponseEntity createCategory(String name) { - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(name); + public ResponseEntity createCategory(@RequestBody @Valid MagazineCategoryRequest.Create request) { + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); return new ResponseEntity(category, HttpStatus.CREATED); } @GetMapping - public ResponseEntity getCategories() { + public ResponseEntity getAllCategories() { MagazineCategoryResponse.GetAll allCategory = magazineCategoryService.getAllCategory(); return new ResponseEntity(allCategory, HttpStatus.OK); } + @GetMapping("/{slug}") + public ResponseEntity getSubCategories(@PathVariable String slug) { + MagazineCategoryResponse.GetAll subCategory = magazineCategoryService.getSubCategories(slug); + return new ResponseEntity(subCategory, HttpStatus.OK); + } + + @DeleteMapping("/{categoryId}") + @AdminOnly + public ResponseEntity deleteCategory(@PathVariable Long categoryId) { + magazineCategoryService.deleteCategory(categoryId); + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + + @PutMapping("/{categoryId}") + @AdminOnly + public ResponseEntity updateCategory(@PathVariable Long categoryId, @RequestBody @Valid MagazineCategoryRequest.Update request) { + MagazineCategoryResponse.Get category = magazineCategoryService.updateCategory(categoryId, request); + return new ResponseEntity(category, HttpStatus.OK); + } + } diff --git a/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java b/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java index 16977339..c7ac4303 100644 --- a/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java +++ b/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java @@ -140,4 +140,9 @@ public void removeLike(MagazineLike magazineLike) { public void addMedia(MagazineMedia magazineMedia) { this.magazineMedias.add(magazineMedia); } + + public void setCategory(MagazineCategory category) { + this.category = category; + this.updatedTime = LocalDateTime.now(); + } } diff --git a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java index 797fcba4..eaa9ff7a 100644 --- a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java +++ b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java @@ -1,7 +1,23 @@ package com.example.codebase.domain.magazine.repository; import com.example.codebase.domain.magazine.entity.MagazineCategory; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; public interface MagazineCategoryRepository extends JpaRepository { + + @Override + @EntityGraph(attributePaths = {"children"}) + @Query("SELECT mc FROM MagazineCategory mc WHERE mc.parent IS NULL") + List findAll(); + + @EntityGraph(attributePaths = {"children"}) + @Query("SELECT mc FROM MagazineCategory mc WHERE mc.slug = :slug") + List findBySlug(String slug); + + boolean existsByNameAndParent(String name, MagazineCategory parentCategory); + } diff --git a/src/main/java/com/example/codebase/domain/magazine/service/MagazineCategoryService.java b/src/main/java/com/example/codebase/domain/magazine/service/MagazineCategoryService.java index b62e2c19..be40e67c 100644 --- a/src/main/java/com/example/codebase/domain/magazine/service/MagazineCategoryService.java +++ b/src/main/java/com/example/codebase/domain/magazine/service/MagazineCategoryService.java @@ -1,14 +1,17 @@ package com.example.codebase.domain.magazine.service; +import com.example.codebase.domain.magazine.dto.MagazineCategoryRequest; import com.example.codebase.domain.magazine.dto.MagazineCategoryResponse; import com.example.codebase.domain.magazine.entity.MagazineCategory; import com.example.codebase.domain.magazine.repository.MagazineCategoryRepository; import com.example.codebase.exception.NotFoundException; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import java.util.List; +import java.util.*; @Service +@Transactional public class MagazineCategoryService { private final MagazineCategoryRepository magazineCategoryRepository; @@ -17,19 +20,73 @@ public MagazineCategoryService(MagazineCategoryRepository magazineCategoryReposi this.magazineCategoryRepository = magazineCategoryRepository; } - public MagazineCategoryResponse.Get createCategory(String name) { - MagazineCategory category = MagazineCategory.toEntity(name); + public MagazineCategoryResponse.Get createCategory(MagazineCategoryRequest.Create request) { + MagazineCategory parentCategory = findParentCategory(request.getParentId()); + + if (parentCategory != null) { + parentCategory.checkDepth(); + } + + checkCategoryExists(request, parentCategory); + + MagazineCategory category = MagazineCategory.toEntity(request.getName(), request.getSlug(), parentCategory); magazineCategoryRepository.save(category); return MagazineCategoryResponse.Get.from(category); } + private MagazineCategory findParentCategory(Long parentId) throws NotFoundException { + if (parentId == null) { + return null; + } + return magazineCategoryRepository.findById(parentId) + .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + } + + private void checkCategoryExists(MagazineCategoryRequest.Create request, MagazineCategory parentCategory) { + boolean exists = magazineCategoryRepository.existsByNameAndParent(request.getName(), parentCategory); + if (exists) { + throw new RuntimeException("์ด๋ฏธ ์ด๋ฆ„์ด๋‚˜ ์Šฌ๋Ÿฌ๊ทธ๊ฐ€ ์ค‘๋ณต๋˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค."); + } + } + + @Transactional(readOnly = true) public MagazineCategoryResponse.GetAll getAllCategory() { List all = magazineCategoryRepository.findAll(); return MagazineCategoryResponse.GetAll.from(all); } + @Transactional(readOnly = true) + public MagazineCategoryResponse.GetAll getSubCategories(String slug) { + List subCategories = magazineCategoryRepository.findBySlug(slug); + return MagazineCategoryResponse.GetAll.from(subCategories); + } + + @Transactional(readOnly = true) public MagazineCategory getEntity(Long categoryId) { return magazineCategoryRepository.findById(categoryId) .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); } + + public void deleteCategory(Long categoryId) { + MagazineCategory category = magazineCategoryRepository.findById(categoryId) + .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + + category.delete(); + } + + public MagazineCategoryResponse.Get updateCategory(Long categoryId, MagazineCategoryRequest.Update request) { + MagazineCategory category = magazineCategoryRepository.findById(categoryId) + .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + + Optional.ofNullable(request.getParentId()).ifPresent(parentId -> { + MagazineCategory parentCategory = magazineCategoryRepository.findById(parentId) + .orElseThrow(() -> new NotFoundException("๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + parentCategory.checkDepth(); + category.update(parentCategory); + }); + + category.update(request); + magazineCategoryRepository.save(category); + return MagazineCategoryResponse.Get.from(category); + } } From 3a5bfbd835fd2345313a207a18481c687e93f557 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 04:59:58 +0900 Subject: [PATCH 006/103] =?UTF-8?q?test:=20=EB=B3=80=EA=B2=BD=EB=90=9C=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=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 --- .../controller/CurationControllerTest.java | 41 ++++++++++++++++++- .../controller/MagazineControllerTest.java | 32 ++++++++++----- .../MagazineLikeControllerTest.java | 22 ++++++++-- 3 files changed, 78 insertions(+), 17 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index be82dbfa..e98f1666 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -5,11 +5,13 @@ import com.example.codebase.domain.curation.dto.CurationResponse; import com.example.codebase.domain.curation.repository.CurationRepository; import com.example.codebase.domain.curation.service.CurationService; +import com.example.codebase.domain.magazine.dto.MagazineCategoryRequest; import com.example.codebase.domain.magazine.dto.MagazineCategoryResponse; import com.example.codebase.domain.magazine.dto.MagazineRequest; import com.example.codebase.domain.magazine.dto.MagazineResponse; import com.example.codebase.domain.magazine.entity.Magazine; import com.example.codebase.domain.magazine.entity.MagazineCategory; +import com.example.codebase.domain.magazine.repository.MagazineCategoryRepository; import com.example.codebase.domain.magazine.repository.MagazineRepository; import com.example.codebase.domain.magazine.service.MagazineCategoryService; import com.example.codebase.domain.magazine.service.MagazineService; @@ -91,6 +93,9 @@ public class CurationControllerTest { @Autowired private CurationRepository curationRepository; + @Autowired + private MagazineCategoryRepository magazineCategoryRepository; + private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach @@ -102,6 +107,8 @@ public void setUp() { objectMapper.registerModule(new JavaTimeModule()); } + private static int categoryCount = 0; + public Member createOrLoadMember() { return createOrLoadMember("testid", "ROLE_ADMIN"); } @@ -131,10 +138,29 @@ public Member createOrLoadMember(String username, String... authorities) { } public MagazineCategory createCategory() { - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory("์นดํ…Œ๊ณ ๋ฆฌ"); + categoryCount++; + + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + categoryCount; + String categorySlug = String.valueOf((char)('a' + categoryCount - 1)); + + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); + + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); return magazineCategoryService.getEntity(category.getId()); } + public MagazineCategory createCategory(String name, String slug) { + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(name, slug, null); + List categories = magazineCategoryRepository.findBySlug(request.getSlug()); + + if (!categories.isEmpty()) { + return categories.get(0); + } else { + MagazineCategoryResponse.Get categoryResponse = magazineCategoryService.createCategory(request); + return magazineCategoryService.getEntity(categoryResponse.getId()); + } + } + public MagazineResponse.Get createMagazine(Member member) { MagazineCategory category = createCategory(); @@ -146,6 +172,16 @@ public MagazineResponse.Get createMagazine(Member member) { return magazineService.create(magazineRequest, member, category); } + MagazineResponse.Get createMagazine(Member member, MagazineCategory category) { + MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); + magazineRequest.setTitle("์ œ๋ชฉ"); + magazineRequest.setContent("๋‚ด์šฉ"); + magazineRequest.setCategoryId(category.getId()); + + return magazineService.create(magazineRequest, member, category); + } + + public CurationResponse.GetAll createCuration(Magazine magazine) { CurationRequest.Create curationRequest = new CurationRequest.Create(); List magazineIds = new ArrayList<>(); @@ -166,7 +202,6 @@ public CurationResponse.GetAll createCuration(Long megazineId) { return curationService.createCuration(curationRequest); } - @Transactional public MagazineResponse.Get createMagazineAndCuration(Member member) { MagazineResponse.Get magazineResponse = createMagazine(member); createCuration(magazineResponse.getId()); @@ -223,9 +258,11 @@ public MagazineResponse.Get createMagazineAndCuration(Member member) { @WithMockCustomUser(username = "testid", role = "ADMIN") @DisplayName("ํ๋ ˆ์ด์…˜ ์ˆ˜์ •") + @BeforeEach @Test void ํ๋ ˆ์ด์…˜_์ˆ˜์ •() throws Exception { Member member = createOrLoadMember(); + MagazineResponse.Get magazineBefore = createMagazine(member); MagazineResponse.Get magazineAfter = createMagazine(member); diff --git a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java index 2a684288..a8b89139 100644 --- a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java @@ -2,10 +2,7 @@ import com.example.codebase.domain.auth.WithMockCustomUser; import com.example.codebase.domain.follow.service.FollowService; -import com.example.codebase.domain.magazine.dto.MagazineCategoryResponse; -import com.example.codebase.domain.magazine.dto.MagazineCommentRequest; -import com.example.codebase.domain.magazine.dto.MagazineRequest; -import com.example.codebase.domain.magazine.dto.MagazineResponse; +import com.example.codebase.domain.magazine.dto.*; import com.example.codebase.domain.magazine.entity.MagazineCategory; import com.example.codebase.domain.magazine.service.MagazineCategoryService; import com.example.codebase.domain.magazine.service.MagazineService; @@ -14,7 +11,6 @@ import com.example.codebase.domain.member.service.MemberService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import io.swagger.v3.oas.annotations.Operation; import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; @@ -75,6 +71,8 @@ public void setUp() { objectMapper.registerModule(new JavaTimeModule()); } + private static int categoryCount = 0; + public Member createMember(String username) { CreateMemberDTO createMemberDTO = new CreateMemberDTO(); createMemberDTO.setUsername(username); @@ -107,7 +105,14 @@ public MagazineResponse.Get createMagaizne(Member member) { } public MagazineCategory createCategory() { - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory("์นดํ…Œ๊ณ ๋ฆฌ"); + categoryCount++; + + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + categoryCount; + String categorySlug = String.valueOf((char)('a' + categoryCount - 1)); // slug์˜ ์˜์–ด ์กฐ๊ฑด + + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); + + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); return magazineCategoryService.getEntity(category.getId()); } @@ -133,7 +138,8 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ() throws Exception { // given createMember("testid"); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory("๊ธ€"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); @@ -471,7 +477,8 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, void ๋งค๊ฑฐ์ง„_๋ฏธ๋””์–ด_์ƒ์„ฑ() throws Exception { // given createMember("testid"); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory("๊ธ€"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); @@ -506,7 +513,8 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, void ๋งค๊ฑฐ์ง„_๋ฏธ๋””์–ด_์ž˜๋ชป๋œ_์ƒ์„ฑ() throws Exception { // given createMember("testid"); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory("๊ธ€"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); @@ -534,7 +542,8 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, void ๋งค๊ฑฐ์ง„_๋ฏธ๋””์–ด_์ž˜๋ชป๋œ_์ƒ์„ฑ2() throws Exception { // given createMember("testid"); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory("๊ธ€"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); @@ -571,7 +580,8 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, void ๋งค๊ฑฐ์ง„_๋ฉ”ํƒ€๋ฐ์ดํ„ฐ_์ƒ์„ฑ () throws Exception { // given createMember("testid"); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory("๊ธ€"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); diff --git a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java index 5dcfb8ca..774ee44b 100644 --- a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java @@ -1,6 +1,7 @@ package com.example.codebase.controller; import com.example.codebase.domain.auth.WithMockCustomUser; +import com.example.codebase.domain.magazine.dto.MagazineCategoryRequest; import com.example.codebase.domain.magazine.dto.MagazineCategoryResponse; import com.example.codebase.domain.magazine.dto.MagazineRequest; import com.example.codebase.domain.magazine.dto.MagazineResponse; @@ -83,6 +84,8 @@ class MagazineLikeControllerTest { @Autowired private MagazineCategoryRepository magazineCategoryRepository; + @Autowired + private MagazineCategoryService magazineCategoryService; @Autowired private MemberRepository memberRepository; @@ -108,6 +111,7 @@ public void setUp() { member = createOrLoadMember("testid"); magazine = createMagaizne(member); } + private static int categoryCount = 0; @AfterEach public void tearDown() { @@ -131,10 +135,7 @@ public Member createOrLoadMember(String username) { return memberService.getEntity(username); } public Magazine createMagaizne(Member member) { - MagazineCategory category = MagazineCategory.builder() - .name("์นดํ…Œ๊ณ ๋ฆฌ") - .build(); - magazineCategoryRepository.save(category); + MagazineCategory category = createCategory(); Magazine saved = magazineRepository.saveAndFlush(Magazine.builder() .title("์ œ๋ชฉ") @@ -146,6 +147,19 @@ public Magazine createMagaizne(Member member) { return saved; } + public MagazineCategory createCategory() { + categoryCount++; + + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + categoryCount; + String categorySlug = String.valueOf((char)('a' + categoryCount - 1)); // slug์˜ ์˜์–ด ์กฐ๊ฑด + + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); + + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + return magazineCategoryService.getEntity(category.getId()); + } + + @WithMockCustomUser(username = "testid") @DisplayName("๋งค๊ฑฐ์ง„ ์ข‹์•„์š” ์‹œ") @Test From ed0e1a0d88f185b843db4bc015754a7382cd6e94 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 13:00:06 +0900 Subject: [PATCH 007/103] =?UTF-8?q?test:=20=EB=B3=80=EA=B2=BD=EB=90=9C=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=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 --- .../controller/CurationControllerTest.java | 3 +- .../MagazineCategoryControllerTest.java | 136 ++++++++++++++++-- 2 files changed, 126 insertions(+), 13 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index e98f1666..0879f979 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -315,9 +315,10 @@ public MagazineResponse.Get createMagazineAndCuration(Member member) { .andExpect(status().isNoContent()); } + @WithMockCustomUser(username = "testid", role = "ADMIN") @DisplayName("ํ๋ ˆ์ด์…˜ ์ „์ฒด ์กฐํšŒ") @Test - void ํ๋ ˆ์ด์…˜_() throws Exception { + void ํ๋ ˆ์ด์…˜_์ „์ฒด์กฐํšŒ() throws Exception { Member member = createOrLoadMember(); createMagazineAndCuration(member); createMagazineAndCuration(member); diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index 2d7126c0..bc6b0ab2 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -1,7 +1,10 @@ package com.example.codebase.controller; import com.example.codebase.domain.auth.WithMockCustomUser; +import com.example.codebase.domain.magazine.dto.MagazineCategoryRequest; import com.example.codebase.domain.magazine.dto.MagazineCategoryResponse; +import com.example.codebase.domain.magazine.dto.MagazineResponse; +import com.example.codebase.domain.magazine.entity.MagazineCategory; import com.example.codebase.domain.magazine.service.MagazineCategoryService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @@ -13,17 +16,18 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import java.nio.charset.StandardCharsets; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -54,17 +58,32 @@ public void setUp() { objectMapper.registerModule(new JavaTimeModule()); } + private static int categoryCount = 0; + + public MagazineCategory createCategoryAndLoad() { + categoryCount++; + + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + categoryCount; + String categorySlug = String.valueOf((char)('a' + categoryCount - 1)); + + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); + + MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + return magazineCategoryService.getEntity(category.getId()); + } + @WithMockCustomUser(username = "admin", role = "ADMIN") @DisplayName("๋งค๊ฑฐ์ง„ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ์ด ๋œ๋‹ค.") @Test public void createCategory() throws Exception { // given - String name = "๊ธ€"; + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "word", null); // when mockMvc.perform( post("/api/magazine-category") - .param("name", name) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) ) .andDo(print()) .andExpect(status().isCreated()); @@ -72,7 +91,8 @@ public void createCategory() throws Exception { // then MagazineCategoryResponse.GetAll allCategory = magazineCategoryService.getAllCategory(); assertTrue(allCategory.getCategories().stream() - .anyMatch(category -> category.getName().equals(name))); + .map(MagazineCategoryResponse.Get::getName) + .anyMatch(category -> category.equals(request.getName()))); } @DisplayName("๋งค๊ฑฐ์ง„ ์นดํ…Œ๊ณ ๋ฆฌ ์ „์ฒด๊ฐ€ ์กฐํšŒ ๋œ๋‹ค.") @@ -80,10 +100,15 @@ public void createCategory() throws Exception { public void getCategories() throws Exception { // given List categoryNames = List.of("๊ธ€", "IT", "์‚ฌ์ง„"); - magazineCategoryService.createCategory(categoryNames.get(0)); - magazineCategoryService.createCategory(categoryNames.get(1)); - magazineCategoryService.createCategory(categoryNames.get(2)); - magazineCategoryService.createCategory("์Œ์•…"); + List categories = List.of( + new MagazineCategoryRequest.Create("๊ธ€", "word", null), + new MagazineCategoryRequest.Create("IT", "it", null), + new MagazineCategoryRequest.Create("์‚ฌ์ง„", "photo", null) + ); + + magazineCategoryService.createCategory(categories.get(0)); + magazineCategoryService.createCategory(categories.get(1)); + magazineCategoryService.createCategory(categories.get(2)); // when String response = mockMvc.perform( @@ -92,12 +117,99 @@ public void getCategories() throws Exception { .andDo(print()) .andExpect(status().isOk()) .andReturn() - .getResponse().getContentAsString(); // json + .getResponse().getContentAsString(StandardCharsets.UTF_8); // then MagazineCategoryResponse.GetAll allCategory = objectMapper.readValue(response, MagazineCategoryResponse.GetAll.class); // json to object (์—ญ์ง๋ ฌํ™”) assertTrue(allCategory.getCategories().stream() - .anyMatch(category -> categoryNames.contains(category.getName()))); + .map(MagazineCategoryResponse.Get::getName) + .allMatch(categoryNames::contains)); + } + + @DisplayName("๋งค๊ฑฐ์ง„ slug๋ฅผ ํ†ตํ•ด ํ•˜์œ„ ์นดํ…Œ๊ณ ๋ฆฌ ์กฐํšŒ๊ฐ€ ๋œ๋‹ค. ") + @Test + public void getSubCategories() throws Exception { + // given + // ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategory parentCategory = createCategoryAndLoad(); + + // ์ž์‹ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategoryResponse.Get childCategory1 = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("์ž์‹1", "firstChild", parentCategory.getId()) + ); + + MagazineCategoryResponse.Get childCategory2 = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("์ž์‹2", "secondChild", parentCategory.getId()) + ); + // ์†์ž ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategoryResponse.Get grandsonCategory = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("์†์ž", "grandson", childCategory1.getId()) + ); + + // when + String response = mockMvc.perform( + get("/api/magazine-category/" + parentCategory.getSlug()) + ) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn() + .getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + MagazineCategoryResponse.GetAll subCategory = objectMapper.readValue(response, MagazineCategoryResponse.GetAll.class); // json to object (์—ญ์ง๋ ฌํ™”) + assertTrue(subCategory.getCategories().stream() + .map(MagazineCategoryResponse.Get::getName) + .allMatch(List.of(childCategory1.getName(), childCategory2.getName(), grandsonCategory.getName())::contains)); + } + + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("๋งค๊ฑฐ์ง„ ์นดํ…Œ๊ณ ๋ฆฌ ์‚ญ์ œ๊ฐ€ ๋œ๋‹ค.") + @Test + public void deleteCategory() throws Exception { + // given + MagazineCategory category = createCategoryAndLoad(); + + // when + mockMvc.perform( + delete("/api/magazine-category/" + category.getId()) + ) + .andDo(print()) + .andExpect(status().isNoContent()); + + + } + + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("๋งค๊ฑฐ์ง„ ์นดํ…Œ๊ณ ๋ฆฌ ์ˆ˜์ •์ด ๋œ๋‹ค.") + @Test + public void updateCategory() throws Exception { + // given + // ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategory parentCategoryBefore =createCategoryAndLoad(); + + MagazineCategory parentCategoryAfter = createCategoryAndLoad(); + + // ์ž์‹ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategoryResponse.Get childCategory = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("์ˆ˜์ •๋œ์นดํ…Œ๊ณ ๋ฆฌ", "changeCategory", parentCategoryBefore.getId()) + ); + + MagazineCategoryRequest.Update updateRequest = new MagazineCategoryRequest.Update("์ˆ˜์ •๋œ ๊ธ€", "updated-word", parentCategoryAfter.getId()); + + // when + mockMvc.perform( + put("/api/magazine-category/" + childCategory.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest)) + ) + .andDo(print()) + .andExpect(status().isOk()); + + // then + MagazineCategory updatedCategory = magazineCategoryService.getEntity(childCategory.getId()); + assertEquals(updateRequest.getName(), updatedCategory.getName()); + assertEquals(updateRequest.getSlug(), updatedCategory.getSlug()); + assertEquals(updateRequest.getParentId(), updatedCategory.getParent().getId()); } } \ No newline at end of file From 5b5a4f69d02a96a75f03910b52172e973182fb2b Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 17:30:20 +0900 Subject: [PATCH 008/103] =?UTF-8?q?feat:=20=EB=B6=80=EB=AA=A8=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=EB=8F=84=20=ED=95=A8=EA=BB=98=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=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/MagazineCategoryResponse.java | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryResponse.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryResponse.java index 8cae2804..3c1df71f 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryResponse.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineCategoryResponse.java @@ -9,13 +9,12 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; public class MagazineCategoryResponse { @Getter @Setter - @Schema(name = "MagazineCategoryResponse", description = "Category Response") + @Schema(name = "MagazineCategoryResponse.Get", description = "Category Response") public static class Get { private Long id; @@ -32,6 +31,8 @@ public static class Get { private List childrenCategories = new ArrayList<>(); + private Create parentCategory = null; + public static MagazineCategoryResponse.Get from(MagazineCategory category) { Get get = new Get(); get.setId(category.getId()); @@ -39,17 +40,52 @@ public static MagazineCategoryResponse.Get from(MagazineCategory category) { get.setSlug(category.getSlug()); get.setCreatedTime(category.getCreatedTime()); get.setUpdatedTime(category.getUpdatedTime()); - if (category.getChildren() != null) { - get.setChildrenCategories(category.getChildren().stream().map(Get::from).collect(Collectors.toList())); - } else { - get.setChildrenCategories(new ArrayList<>()); + get.setChildrenCategories(category.getChildren().stream().map(Get::from).toList()); + } + + if (category.getParent() != null) { + get.setParentCategory(Create.from(category.getParent())); } return get; } } + + @Getter + @Setter + @Schema(name = "MagazineCategoryResponse.Create", description = "Category Response Create") + public static class Create { + + private Long id; + + private String name; + + private String slug; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime createdTime; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime updatedTime; + + private Create parentCategory = null; + public static MagazineCategoryResponse.Create from(MagazineCategory category) { + Create create = new Create(); + create.setId(category.getId()); + create.setName(category.getName()); + create.setSlug(category.getSlug()); + create.setCreatedTime(category.getCreatedTime()); + create.setUpdatedTime(category.getUpdatedTime()); + + if (category.getParent() != null) { + create.setParentCategory(Create.from(category.getParent())); + } + return create; + } + } + @Getter @Setter @Schema(name = "MagazineCategoryRequest", description = "MagazineCategoryRequest") From 743375c02532e0292cd77b84c644110ccc782659 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 17:33:10 +0900 Subject: [PATCH 009/103] =?UTF-8?q?refactor:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=83=9D=EC=84=B1,=20=EC=88=98=EC=A0=95=EC=8B=9C?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20response=20=EB=B6=84?= =?UTF-8?q?=ED=95=A0=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=A1=9C=EC=A7=81=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 --- .../controller/MagazineCategoryController.java | 2 +- .../domain/magazine/entity/MagazineCategory.java | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/MagazineCategoryController.java b/src/main/java/com/example/codebase/controller/MagazineCategoryController.java index dd5208bb..abd06dfb 100644 --- a/src/main/java/com/example/codebase/controller/MagazineCategoryController.java +++ b/src/main/java/com/example/codebase/controller/MagazineCategoryController.java @@ -25,7 +25,7 @@ public MagazineCategoryController(MagazineCategoryService magazineCategoryServic @PostMapping @AdminOnly public ResponseEntity createCategory(@RequestBody @Valid MagazineCategoryRequest.Create request) { - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); return new ResponseEntity(category, HttpStatus.CREATED); } diff --git a/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java index 54ae73f7..4814353d 100644 --- a/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java +++ b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java @@ -55,15 +55,17 @@ public class MagazineCategory { @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.MERGE) private List children = new ArrayList<>(); - @OneToOne(mappedBy = "category", fetch = FetchType.LAZY) - private Magazine magazine; - public static MagazineCategory toEntity(String name, String slug, MagazineCategory parent) { - return MagazineCategory.builder() + MagazineCategory magazineCategory = MagazineCategory.builder() .name(name) .slug(slug) .parent(parent) .build(); + + if(parent != null) + parent.getChildren().add(magazineCategory); + + return magazineCategory; } public void delete() { @@ -71,9 +73,6 @@ public void delete() { throw new RuntimeException("ํ•˜์œ„ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค."); } - if (this.magazine != null) { - this.magazine.delete(); - } this.isDeleted = true; this.updatedTime = LocalDateTime.now(); } From c0c64f44f295cafda346fa6fffb66d8b9a6cdc7c Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 17:34:20 +0900 Subject: [PATCH 010/103] =?UTF-8?q?feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AD=EC=A0=9C,=20=EC=83=9D=EC=84=B1=EC=8B=9C?= =?UTF-8?q?=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80=20-=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EC=8B=9C=20=ED=95=B4=EB=8B=B9=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=A7=A4=EA=B1=B0=EC=A7=84=EC=9D=B4=20=EC=9E=88=EC=9C=BC?= =?UTF-8?q?=EB=A9=B4=20=EC=82=AD=EC=A0=9C=EB=90=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4.=20-=20=EC=83=9D=EC=84=B1=EC=8B=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=EC=9D=B4=EB=82=98=20=EC=8A=AC=EB=9F=AC?= =?UTF-8?q?=EA=B0=80=EA=B0=80=20=EC=A4=91=EB=B3=B5=EB=90=98=EB=A9=B4=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=EC=BD=94=EB=93=9C=EB=A5=BC=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=95=A9=EB=8B=88=EB=8B=A4.=20(=EA=B8=B0=EC=A1=B4=20:?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=EA=B0=80=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20?= =?UTF-8?q?=EB=85=B8=EC=B6=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/MagazineCategoryRepository.java | 2 ++ .../service/MagazineCategoryService.java | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java index eaa9ff7a..72b53958 100644 --- a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java +++ b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java @@ -20,4 +20,6 @@ public interface MagazineCategoryRepository extends JpaRepository new NotFoundException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + Long magazineCount = magazineCategoryRepository.countMagazineByCategory(category); + if (magazineCount > 0) { + throw new RuntimeException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์— ์†ํ•œ ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค."); + } + category.delete(); } From 60f0a52f3ecd7c3fd8ffde9df94977eb6f057686 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 17:36:16 +0900 Subject: [PATCH 011/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=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 --- .../controller/CurationControllerTest.java | 5 +-- .../MagazineCategoryControllerTest.java | 40 ++++++++++++++----- .../controller/MagazineControllerTest.java | 12 +++--- .../MagazineLikeControllerTest.java | 2 +- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index 0879f979..12b66b18 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -145,7 +145,7 @@ public MagazineCategory createCategory() { MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); return magazineCategoryService.getEntity(category.getId()); } @@ -156,7 +156,7 @@ public MagazineCategory createCategory(String name, String slug) { if (!categories.isEmpty()) { return categories.get(0); } else { - MagazineCategoryResponse.Get categoryResponse = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create categoryResponse = magazineCategoryService.createCategory(request); return magazineCategoryService.getEntity(categoryResponse.getId()); } } @@ -258,7 +258,6 @@ public MagazineResponse.Get createMagazineAndCuration(Member member) { @WithMockCustomUser(username = "testid", role = "ADMIN") @DisplayName("ํ๋ ˆ์ด์…˜ ์ˆ˜์ •") - @BeforeEach @Test void ํ๋ ˆ์ด์…˜_์ˆ˜์ •() throws Exception { Member member = createOrLoadMember(); diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index bc6b0ab2..cd975316 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -23,6 +23,7 @@ import org.springframework.web.context.WebApplicationContext; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -58,17 +59,17 @@ public void setUp() { objectMapper.registerModule(new JavaTimeModule()); } - private static int categoryCount = 0; + private static int categoryCount = 0; public MagazineCategory createCategoryAndLoad() { categoryCount++; String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + categoryCount; - String categorySlug = String.valueOf((char)('a' + categoryCount - 1)); + String categorySlug = String.valueOf((char) ('a' + categoryCount - 1)); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); return magazineCategoryService.getEntity(category.getId()); } @@ -134,15 +135,15 @@ public void getSubCategories() throws Exception { MagazineCategory parentCategory = createCategoryAndLoad(); // ์ž์‹ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ - MagazineCategoryResponse.Get childCategory1 = magazineCategoryService.createCategory( + MagazineCategoryResponse.Create childCategory1 = magazineCategoryService.createCategory( new MagazineCategoryRequest.Create("์ž์‹1", "firstChild", parentCategory.getId()) ); - MagazineCategoryResponse.Get childCategory2 = magazineCategoryService.createCategory( + MagazineCategoryResponse.Create childCategory2 = magazineCategoryService.createCategory( new MagazineCategoryRequest.Create("์ž์‹2", "secondChild", parentCategory.getId()) ); // ์†์ž ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ - MagazineCategoryResponse.Get grandsonCategory = magazineCategoryService.createCategory( + MagazineCategoryResponse.Create grandsonCategory = magazineCategoryService.createCategory( new MagazineCategoryRequest.Create("์†์ž", "grandson", childCategory1.getId()) ); @@ -157,9 +158,26 @@ public void getSubCategories() throws Exception { // then MagazineCategoryResponse.GetAll subCategory = objectMapper.readValue(response, MagazineCategoryResponse.GetAll.class); // json to object (์—ญ์ง๋ ฌํ™”) - assertTrue(subCategory.getCategories().stream() - .map(MagazineCategoryResponse.Get::getName) - .allMatch(List.of(childCategory1.getName(), childCategory2.getName(), grandsonCategory.getName())::contains)); + + List allNames = new ArrayList<>(); + for (MagazineCategoryResponse.Get category : subCategory.getCategories()) { + // ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ + allNames.add(category.getName()); + + // ์ž์‹ ์นดํ…Œ๊ณ ๋ฆฌ + for (MagazineCategoryResponse.Get child : category.getChildrenCategories()) { + allNames.add(child.getName()); + + // ์†์ž ์นดํ…Œ๊ณ ๋ฆฌ + for (MagazineCategoryResponse.Get grandchild : child.getChildrenCategories()) { + allNames.add(grandchild.getName()); + } + } + } + + boolean allCategoriesPresent = allNames.containsAll(List.of(childCategory1.getName(), childCategory2.getName(), grandsonCategory.getName())); + + assertTrue(allCategoriesPresent); } @WithMockCustomUser(username = "admin", role = "ADMIN") @@ -185,12 +203,12 @@ public void deleteCategory() throws Exception { public void updateCategory() throws Exception { // given // ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ - MagazineCategory parentCategoryBefore =createCategoryAndLoad(); + MagazineCategory parentCategoryBefore = createCategoryAndLoad(); MagazineCategory parentCategoryAfter = createCategoryAndLoad(); // ์ž์‹ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ - MagazineCategoryResponse.Get childCategory = magazineCategoryService.createCategory( + MagazineCategoryResponse.Create childCategory = magazineCategoryService.createCategory( new MagazineCategoryRequest.Create("์ˆ˜์ •๋œ์นดํ…Œ๊ณ ๋ฆฌ", "changeCategory", parentCategoryBefore.getId()) ); diff --git a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java index a8b89139..9de913be 100644 --- a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java @@ -112,7 +112,7 @@ public MagazineCategory createCategory() { MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); return magazineCategoryService.getEntity(category.getId()); } @@ -139,7 +139,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, // given createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); @@ -478,7 +478,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, // given createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); @@ -514,7 +514,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, // given createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); @@ -543,7 +543,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, // given createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); @@ -581,7 +581,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, // given createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); diff --git a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java index 774ee44b..992a2da1 100644 --- a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java @@ -155,7 +155,7 @@ public MagazineCategory createCategory() { MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); - MagazineCategoryResponse.Get category = magazineCategoryService.createCategory(request); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); return magazineCategoryService.getEntity(category.getId()); } From c8fdb2145c6a3280397278f8bd015f7f47f519af Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 17:51:37 +0900 Subject: [PATCH 012/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=9E=9C=EB=8D=A4=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/controller/CurationControllerTest.java | 12 +++++++----- .../controller/MagazineCategoryControllerTest.java | 10 ++++++---- .../codebase/controller/MagazineControllerTest.java | 12 +++++++----- .../controller/MagazineLikeControllerTest.java | 12 +++++++----- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index 12b66b18..53af52b2 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -46,6 +46,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.Random; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -107,8 +108,6 @@ public void setUp() { objectMapper.registerModule(new JavaTimeModule()); } - private static int categoryCount = 0; - public Member createOrLoadMember() { return createOrLoadMember("testid", "ROLE_ADMIN"); } @@ -137,11 +136,14 @@ public Member createOrLoadMember(String username, String... authorities) { return memberRepository.save(dummy); } + private Random random = new Random(); + public MagazineCategory createCategory() { - categoryCount++; + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); - String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + categoryCount; - String categorySlug = String.valueOf((char)('a' + categoryCount - 1)); + char randomChar1 = (char) ('a' + random.nextInt(26)); + char randomChar2 = (char) ('a' + random.nextInt(26)); + String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index cd975316..4fc463e9 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -25,6 +25,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Random; import static org.junit.jupiter.api.Assertions.*; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; @@ -59,13 +60,14 @@ public void setUp() { objectMapper.registerModule(new JavaTimeModule()); } - private static int categoryCount = 0; + private Random random = new Random(); public MagazineCategory createCategoryAndLoad() { - categoryCount++; + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); - String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + categoryCount; - String categorySlug = String.valueOf((char) ('a' + categoryCount - 1)); + char randomChar1 = (char) ('a' + random.nextInt(26)); + char randomChar2 = (char) ('a' + random.nextInt(26)); + String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); diff --git a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java index 9de913be..22aba5e5 100644 --- a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java @@ -28,6 +28,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; +import java.util.Random; import static org.junit.jupiter.api.Assertions.*; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; @@ -71,8 +72,6 @@ public void setUp() { objectMapper.registerModule(new JavaTimeModule()); } - private static int categoryCount = 0; - public Member createMember(String username) { CreateMemberDTO createMemberDTO = new CreateMemberDTO(); createMemberDTO.setUsername(username); @@ -104,11 +103,14 @@ public MagazineResponse.Get createMagaizne(Member member) { return magazineService.create(magazineRequest, member, category); } + private Random random = new Random(); + public MagazineCategory createCategory() { - categoryCount++; + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); - String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + categoryCount; - String categorySlug = String.valueOf((char)('a' + categoryCount - 1)); // slug์˜ ์˜์–ด ์กฐ๊ฑด + char randomChar1 = (char) ('a' + random.nextInt(26)); + char randomChar2 = (char) ('a' + random.nextInt(26)); + String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); diff --git a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java index 992a2da1..4d4c38a4 100644 --- a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java @@ -45,6 +45,7 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -111,7 +112,6 @@ public void setUp() { member = createOrLoadMember("testid"); magazine = createMagaizne(member); } - private static int categoryCount = 0; @AfterEach public void tearDown() { @@ -147,11 +147,14 @@ public Magazine createMagaizne(Member member) { return saved; } + private Random random = new Random(); + public MagazineCategory createCategory() { - categoryCount++; + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); - String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + categoryCount; - String categorySlug = String.valueOf((char)('a' + categoryCount - 1)); // slug์˜ ์˜์–ด ์กฐ๊ฑด + char randomChar1 = (char) ('a' + random.nextInt(26)); + char randomChar2 = (char) ('a' + random.nextInt(26)); + String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); @@ -159,7 +162,6 @@ public MagazineCategory createCategory() { return magazineCategoryService.getEntity(category.getId()); } - @WithMockCustomUser(username = "testid") @DisplayName("๋งค๊ฑฐ์ง„ ์ข‹์•„์š” ์‹œ") @Test From e11c83683ce28535928f941355267c18ae9573da Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 18:30:11 +0900 Subject: [PATCH 013/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=9E=9C=EB=8D=A4=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20=EB=9E=9C=EB=8D=A4=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=8B=9C=EB=93=9C=EB=A5=BC=20=EB=8F=99=EC=A0=81(Sy?= =?UTF-8?q?stem.currentTimeMillis())=EC=9D=84=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/controller/CurationControllerTest.java | 7 ++++--- .../controller/MagazineCategoryControllerTest.java | 6 +++--- .../codebase/controller/MagazineControllerTest.java | 6 +++--- .../codebase/controller/MagazineLikeControllerTest.java | 6 +++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index 53af52b2..0b6b50bf 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -136,14 +136,14 @@ public Member createOrLoadMember(String username, String... authorities) { return memberRepository.save(dummy); } - private Random random = new Random(); - public MagazineCategory createCategory() { + Random random = new Random(System.currentTimeMillis()); + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); - String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); + String categorySlug = String.valueOf(randomChar1) + randomChar2; MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); @@ -151,6 +151,7 @@ public MagazineCategory createCategory() { return magazineCategoryService.getEntity(category.getId()); } + public MagazineCategory createCategory(String name, String slug) { MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(name, slug, null); List categories = magazineCategoryRepository.findBySlug(request.getSlug()); diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index 4fc463e9..ccc84fe8 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -60,14 +60,14 @@ public void setUp() { objectMapper.registerModule(new JavaTimeModule()); } - private Random random = new Random(); - public MagazineCategory createCategoryAndLoad() { + Random random = new Random(System.currentTimeMillis()); + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); - String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); + String categorySlug = String.valueOf(randomChar1) + randomChar2; MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); diff --git a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java index 22aba5e5..7ee4386b 100644 --- a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java @@ -103,14 +103,14 @@ public MagazineResponse.Get createMagaizne(Member member) { return magazineService.create(magazineRequest, member, category); } - private Random random = new Random(); - public MagazineCategory createCategory() { + Random random = new Random(System.currentTimeMillis()); + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); - String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); + String categorySlug = String.valueOf(randomChar1) + randomChar2; MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); diff --git a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java index 4d4c38a4..98baaa90 100644 --- a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java @@ -147,14 +147,14 @@ public Magazine createMagaizne(Member member) { return saved; } - private Random random = new Random(); - public MagazineCategory createCategory() { + Random random = new Random(System.currentTimeMillis()); + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); - String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); + String categorySlug = String.valueOf(randomChar1) + randomChar2; MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); From cb09c27e2937986aa751f7223fb8dc287c6a355c Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 25 Mar 2024 19:21:57 +0900 Subject: [PATCH 014/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=9E=9C=EB=8D=A4=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=ED=95=9C=20categorySlug=20=EC=98=AC=EB=B0=94?= =?UTF-8?q?=EB=A5=B4=EA=B2=8C=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/codebase/controller/CurationControllerTest.java | 2 +- .../codebase/controller/MagazineCategoryControllerTest.java | 2 +- .../com/example/codebase/controller/MagazineControllerTest.java | 2 +- .../example/codebase/controller/MagazineLikeControllerTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index 0b6b50bf..e6a5a0a0 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -143,7 +143,7 @@ public MagazineCategory createCategory() { char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); - String categorySlug = String.valueOf(randomChar1) + randomChar2; + String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index ccc84fe8..0ff4779f 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -67,7 +67,7 @@ public MagazineCategory createCategoryAndLoad() { char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); - String categorySlug = String.valueOf(randomChar1) + randomChar2; + String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); diff --git a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java index 7ee4386b..435c65e8 100644 --- a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java @@ -110,7 +110,7 @@ public MagazineCategory createCategory() { char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); - String categorySlug = String.valueOf(randomChar1) + randomChar2; + String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); diff --git a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java index 98baaa90..218b2a98 100644 --- a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java @@ -154,7 +154,7 @@ public MagazineCategory createCategory() { char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); - String categorySlug = String.valueOf(randomChar1) + randomChar2; + String categorySlug = new StringBuilder().append(randomChar1).append(randomChar2).toString(); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); From 5c9d856b60304b33787479a2d1842565a916f9dc Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 26 Mar 2024 14:22:32 +0900 Subject: [PATCH 015/103] =?UTF-8?q?test:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EA=B9=8A=EC=9D=B4=EA=B0=80=203=EB=8B=A8=EA=B3=84?= =?UTF-8?q?=20=EC=9D=B4=EC=83=81=20=EC=83=9D=EC=84=B1=EC=8B=9C=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EA=B2=80=EC=A6=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MagazineCategoryControllerTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index 0ff4779f..d68e6a2d 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -232,4 +232,40 @@ public void updateCategory() throws Exception { assertEquals(updateRequest.getParentId(), updatedCategory.getParent().getId()); } + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ตœ๋Œ€ 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•˜๋‹ค") + @Test + public void ์นดํ…Œ๊ณ ๋ฆฌ_์ตœ๋Œ€_2๋‹จ๊ณ„_๊ฒ€์ฆ() throws Exception { + // given + // ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategory parentCategory = createCategoryAndLoad(); + + // ์ž์‹ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategoryResponse.Create childDepth1 = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("๊นŠ์ด1", "depthOne", parentCategory.getId()) + ); + + MagazineCategoryResponse.Create childDepth2 = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("๊นŠ์ด2", "depthTwo", childDepth1.getId()) + ); + // ๊นŠ์ด 3 ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊นŠ์ด3", "depthThree", childDepth2.getId()); + + // when + mockMvc.perform( + post("/api/magazine-category") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()); + + // then + Throwable exception = assertThrows(RuntimeException.class, () -> { + magazineCategoryService.createCategory(request); + }); + + assertEquals("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ตœ๋Œ€ 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.", exception.getMessage()); + } + } \ No newline at end of file From 2b24cb8b8274517178fbb161ca384cda1192936d Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 26 Mar 2024 14:23:46 +0900 Subject: [PATCH 016/103] =?UTF-8?q?refactor:=20magazineCategory=20?= =?UTF-8?q?=EB=B6=80=EB=AA=A8=20=EB=B3=80=EA=B2=BD=20=EB=A7=A4=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20update(Ma?= =?UTF-8?q?gazineCategory)=20->=20changeParentCategory(MagazineCategory)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/domain/magazine/entity/MagazineCategory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java index 4814353d..f9c4e90d 100644 --- a/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java +++ b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java @@ -100,7 +100,7 @@ public void update(MagazineCategoryRequest.Update request) { this.updatedTime = LocalDateTime.now(); } - public void update(MagazineCategory newParent) { + public void changeParentCategory(MagazineCategory newParent) { if (this.parent != null) { this.parent.getChildren().remove(this); } From 32715e66f376336299e72f8fe3a31c076cd91e57 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 26 Mar 2024 16:44:29 +0900 Subject: [PATCH 017/103] =?UTF-8?q?refactor:=20=EC=8A=AC=EB=9F=AC=EA=B7=B8?= =?UTF-8?q?=20=EC=A4=91=EB=B3=B5=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?-=20=EC=8A=AC=EB=9F=AC=EA=B7=B8=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=B3=80=EA=B2=BD=ED=96=88?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4=20-=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=EB=93=A4=EC=9D=84=20=EA=B5=AC?= =?UTF-8?q?=EC=B2=B4=ED=99=94=20=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4=20-?= =?UTF-8?q?=20update=20=EC=8B=9C=20=EC=98=AC=EB=B0=94=EB=A5=B4=EA=B2=8C=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=EB=93=A4=EC=9D=B4=20=EC=9C=A0=ED=9A=A8=ED=95=9C=EC=A7=80=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=96=88?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../magazine/entity/MagazineCategory.java | 2 +- .../MagazineCategoryRepository.java | 2 ++ .../service/MagazineCategoryService.java | 36 ++++++++++++------- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java index f9c4e90d..a9f00f34 100644 --- a/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java +++ b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineCategory.java @@ -86,7 +86,7 @@ public void checkDepth() { } if (depth >= 2) { - throw new RuntimeException("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ตœ๋Œ€ 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."); + throw new RuntimeException("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ตœ๋Œ€ 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ ๋ฐ ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."); } } diff --git a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java index 72b53958..970a5d52 100644 --- a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java +++ b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java @@ -22,4 +22,6 @@ public interface MagazineCategoryRepository extends JpaRepository new NotFoundException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); - Optional.ofNullable(request.getParentId()).ifPresent(parentId -> { - MagazineCategory parentCategory = magazineCategoryRepository.findById(parentId) - .orElseThrow(() -> new NotFoundException("๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + MagazineCategory parentCategory = findParentCategory(request.getParentId()); + + if (parentCategory != null) { parentCategory.checkDepth(); - category.update(parentCategory); - }); + } + + checkCategoryExists(request, parentCategory); + category.changeParentCategory(parentCategory); category.update(request); magazineCategoryRepository.save(category); return MagazineCategoryResponse.Get.from(category); } + + private void checkCategoryExists(MagazineCategoryRequest.Update request, MagazineCategory parentCategory) { + boolean exists = magazineCategoryRepository.existsByNameAndParent(request.getName(), parentCategory); + if (exists) { + throw new RuntimeException("ํ•ด๋‹น ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์‚ฐํ•˜ ์ด๋ฆ„์ด ๊ฐ™์€ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค."); + } + boolean existsSlug = magazineCategoryRepository.existsBySlug(request.getSlug()); + if (existsSlug) { + throw new RuntimeException("์Šฌ๋Ÿฌ๊ทธ๊ฐ€ ์ค‘๋ณต๋˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค."); + } + } } From 1064dcd0b3a5ebd7d6e13f99721f7289040eca6b Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 26 Mar 2024 16:46:09 +0900 Subject: [PATCH 018/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=B5=9C=EB=8C=80=202=EB=8B=A8=EA=B3=84=20=EA=B9=8C=EC=A7=80?= =?UTF-8?q?=EB=A7=8C=20=EC=83=9D=EC=84=B1=20=EA=B0=80=EB=8A=A5=ED=95=98?= =?UTF-8?q?=EB=8B=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9D=98=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=EB=B6=80=EB=B6=84=EC=9D=84=20e2e=EC=97=90=20=EB=A7=9E=EA=B2=8C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4.=20-?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EC=98=80=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MagazineCategoryControllerTest.java | 151 +++++++++++++++++- 1 file changed, 143 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index d68e6a2d..3a86aed5 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -3,9 +3,14 @@ import com.example.codebase.domain.auth.WithMockCustomUser; import com.example.codebase.domain.magazine.dto.MagazineCategoryRequest; import com.example.codebase.domain.magazine.dto.MagazineCategoryResponse; +import com.example.codebase.domain.magazine.dto.MagazineRequest; import com.example.codebase.domain.magazine.dto.MagazineResponse; import com.example.codebase.domain.magazine.entity.MagazineCategory; import com.example.codebase.domain.magazine.service.MagazineCategoryService; +import com.example.codebase.domain.magazine.service.MagazineService; +import com.example.codebase.domain.member.dto.CreateMemberDTO; +import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.member.service.MemberService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import jakarta.transaction.Transactional; @@ -25,6 +30,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Random; import static org.junit.jupiter.api.Assertions.*; @@ -49,6 +55,12 @@ class MagazineCategoryControllerTest { @Autowired private MagazineCategoryService magazineCategoryService; + @Autowired + private MagazineService magazineService; + + @Autowired + private MemberService memberService; + private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach @@ -75,6 +87,35 @@ public MagazineCategory createCategoryAndLoad() { return magazineCategoryService.getEntity(category.getId()); } + public MagazineResponse.Get createMagaizne(Member member, MagazineCategory category) { + MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); + magazineRequest.setTitle("์ œ๋ชฉ"); + magazineRequest.setContent("๋‚ด์šฉ"); + magazineRequest.setCategoryId(category.getId()); + magazineRequest.setMetadata(Map.of( + "color", "blue", + "font", "godic" + )); + magazineRequest.setMediaUrls(List.of( + "https://cdn.artscope.kr/local/1.jpg", + "https://cdn.artscope.kr/local/2.jpg" + )); + + return magazineService.create(magazineRequest, member, category); + } + + public Member createMember(String username) { + CreateMemberDTO createMemberDTO = new CreateMemberDTO(); + createMemberDTO.setUsername(username); + createMemberDTO.setPassword("password"); + createMemberDTO.setName("name"); + createMemberDTO.setEmail("email" + "@" + username + ".com"); + createMemberDTO.setAllowEmailReceive(true); + + memberService.createMember(createMemberDTO); + return memberService.getEntity(username); + } + @WithMockCustomUser(username = "admin", role = "ADMIN") @DisplayName("๋งค๊ฑฐ์ง„ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ์ด ๋œ๋‹ค.") @Test @@ -233,9 +274,9 @@ public void updateCategory() throws Exception { } @WithMockCustomUser(username = "admin", role = "ADMIN") - @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ตœ๋Œ€ 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•˜๋‹ค") + @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ƒ์„ฑ์‹œ ๊นŠ์ด 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค") @Test - public void ์นดํ…Œ๊ณ ๋ฆฌ_์ตœ๋Œ€_2๋‹จ๊ณ„_๊ฒ€์ฆ() throws Exception { + public void ์นดํ…Œ๊ณ ๋ฆฌ_์ƒ์„ฑ์‹œ_๊นŠ์ด_2๋‹จ๊ณ„_์ดˆ๊ณผ์‹œ_์—๋Ÿฌ() throws Exception { // given // ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ MagazineCategory parentCategory = createCategoryAndLoad(); @@ -258,14 +299,108 @@ public void updateCategory() throws Exception { .content(objectMapper.writeValueAsString(request)) ) .andDo(print()) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ตœ๋Œ€ 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ ๋ฐ ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } - // then - Throwable exception = assertThrows(RuntimeException.class, () -> { - magazineCategoryService.createCategory(request); - }); + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ƒ์„ฑ์‹œ ๊นŠ์ด 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค 404") + @Test + public void ์นดํ…Œ๊ณ ๋ฆฌ_์ˆ˜์ •์‹œ_๊นŠ์ด_2๋‹จ๊ณ„_์ดˆ๊ณผ์‹œ_์—๋Ÿฌ() throws Exception { + // given + // ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategory parentCategoryBefore = createCategoryAndLoad(); + + MagazineCategoryResponse.Create childDepth1 = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("๊นŠ์ด1", "depthOne", parentCategoryBefore.getId()) + ); + + MagazineCategoryResponse.Create childDepth2 = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("๊นŠ์ด2", "depthTwo", childDepth1.getId()) + ); + + // ์ž์‹ ์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ + MagazineCategoryResponse.Create updateCategoryBefore = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("์ˆ˜์ •๋ ์นดํ…Œ๊ณ ๋ฆฌ", "changeCategory", parentCategoryBefore.getId()) + ); + + MagazineCategoryRequest.Update updateRequest = new MagazineCategoryRequest.Update("์ˆ˜์ •๋œ์นดํ…Œ๊ณ ๋ฆฌ", "updated-word", childDepth2.getId()); + + // when + mockMvc.perform( + put("/api/magazine-category/" + updateCategoryBefore.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ตœ๋Œ€ 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ ๋ฐ ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ์‹œ slug๊ฐ€ ์ค‘๋ณต๋˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•  ๊ฒฝ์šฐ ์—๋Ÿฌ 404") + @Test + public void ์Šฌ๋Ÿฌ๊ทธ๊ฐ€_๊ฐ™์€_์นดํ…Œ๊ณ ๋ฆฌ_์ƒ์„ฑ์‹œ_์ค‘๋ณต() throws Exception { + // given + MagazineCategoryResponse.Create existingCategory = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("๊ธฐ์กด์นดํ…Œ๊ณ ๋ฆฌ", "category", null) + ); + + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ฒ€์ฆํ• ์นดํ…Œ๊ณ ๋ฆฌ", "category", null); + + // when + mockMvc.perform( + post("/api/magazine-category") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("์Šฌ๋Ÿฌ๊ทธ๊ฐ€ ์ค‘๋ณต๋˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ ์ˆ˜์ •์‹œ slug๊ฐ€ ์ค‘๋ณต๋˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•  ๊ฒฝ์šฐ ์—๋Ÿฌ 404") + @Test + public void ์Šฌ๋Ÿฌ๊ทธ๊ฐ€_๊ฐ™์€_์นดํ…Œ๊ณ ๋ฆฌ_์ˆ˜์ •์‹œ_์ค‘๋ณต() throws Exception { + // given + MagazineCategoryResponse.Create existingCategory = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("๋น„๊ต์นดํ…Œ๊ณ ๋ฆฌ", "category", null) + ); + + MagazineCategoryResponse.Create updateCategoryBefore = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("์ˆ˜์ •๋ ์นดํ…Œ๊ณ ๋ฆฌ", "changeCategory", null) + ); - assertEquals("์นดํ…Œ๊ณ ๋ฆฌ๋Š” ์ตœ๋Œ€ 2๋‹จ๊ณ„ ๊นŒ์ง€๋งŒ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.", exception.getMessage()); + MagazineCategoryRequest.Create updateRequest = new MagazineCategoryRequest.Create("๊ฒ€์ฆํ• ์นดํ…Œ๊ณ ๋ฆฌ", "category", null); + + // when + mockMvc.perform( + put("/api/magazine-category/" + updateCategoryBefore.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("์Šฌ๋Ÿฌ๊ทธ๊ฐ€ ์ค‘๋ณต๋˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ ์‚ญ์ œ์‹œ ์‚ฐํ•˜ ๋ฉ”๊ฑฐ์ง„์ด ์กด์žฌํ•  ๊ฒฝ์šฐ ์—๋Ÿฌ 404") + @Test + public void ์นดํ…Œ๊ณ ๋ฆฌ_์‚ญ์ œ์‹œ_์‚ฐํ•˜_๋งค๊ฑฐ์ง„_์กด์žฌ์‹œ_์—๋Ÿฌ() throws Exception { + // given + MagazineCategory parentCategory = createCategoryAndLoad(); + + createMagaizne(createMember("admin"),parentCategory); + + // when + mockMvc.perform( + delete("/api/magazine-category/" + parentCategory.getId()) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์— ์†ํ•œ ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); } } \ No newline at end of file From d3cbdc6eacb0f388c8b7e45fbe6d1ebc45cea547 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 26 Mar 2024 18:55:51 +0900 Subject: [PATCH 019/103] =?UTF-8?q?feat:=20=EB=A9=94=EA=B1=B0=EC=A7=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=8B=9C=20categoryId=EB=A5=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=ED=95=A0=EC=88=98=20=EC=9E=88=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 --- .../domain/magazine/dto/MagazineRequest.java | 3 +++ .../domain/magazine/dto/MagazineResponse.java | 12 +++++++++--- .../domain/magazine/service/MagazineService.java | 11 +++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java index 312f9930..4bed8737 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java @@ -47,6 +47,9 @@ public static class Update { @NotEmpty private String content; + @NotNull + private Long categoryId; + private Map metadata; private List mediaUrls; diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java index 04432477..46244bc2 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java @@ -19,7 +19,7 @@ public class MagazineResponse { @Getter @Setter - @Schema(description = "๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‘๋‹ต") + @Schema(description = "๋งค๊ฑฐ์ง„ ์กฐํšŒ ์‘๋‹ต") public static class Get { private Long id; @@ -29,7 +29,11 @@ public static class Get { private Map metadata; - private String category; + private Long categoryId; + + private String categorySlug; + + private String categoryName; private List mediaUrls; @@ -58,7 +62,9 @@ public static Get from(Magazine magazine) { response.mediaUrls = magazine.getMagazineMedias().stream() .map(MagazineMedia::getUrl) .toList(); - response.category = magazine.getCategory().getName(); + response.categoryId = magazine.getCategory().getId(); + response.categorySlug = magazine.getCategory().getSlug(); + response.categoryName = magazine.getCategory().getName(); response.views = magazine.getViews(); response.likes = magazine.getLikes(); response.comments = magazine.getComments(); diff --git a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java index 1aedaa0b..ab1eaec5 100644 --- a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java +++ b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java @@ -7,6 +7,7 @@ import com.example.codebase.domain.magazine.entity.MagazineCategory; import com.example.codebase.domain.magazine.entity.MagazineComment; import com.example.codebase.domain.magazine.entity.MagazineMedia; +import com.example.codebase.domain.magazine.repository.MagazineCategoryRepository; import com.example.codebase.domain.magazine.repository.MagazineCommentRepository; import com.example.codebase.domain.magazine.repository.MagazineMediaRepository; import com.example.codebase.domain.magazine.repository.MagazineRepository; @@ -30,12 +31,15 @@ public class MagazineService { private final MagazineMediaRepository magazineMediaRepository; + private final MagazineCategoryRepository magazineCategoryRepository; + @Autowired - public MagazineService(MagazineRepository magazineRepository, MagazineCommentRepository magazineCommentRepository, MagazineMediaRepository magazineMediaRepository) { + public MagazineService(MagazineRepository magazineRepository, MagazineCommentRepository magazineCommentRepository, MagazineMediaRepository magazineMediaRepository, MagazineCategoryRepository magazineCategoryRepository) { this.magazineRepository = magazineRepository; this.magazineCommentRepository = magazineCommentRepository; this.magazineMediaRepository = magazineMediaRepository; + this.magazineCategoryRepository = magazineCategoryRepository; } @Transactional @@ -79,9 +83,12 @@ public MagazineResponse.Get update(Long id, String loginUsername, MagazineReques Magazine magazine = magazineRepository.findById(id) .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + MagazineCategory magazineCategory = magazineCategoryRepository.findById(magazineRequest.getCategoryId()) + .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + validateOwner(loginUsername, magazine); - magazine.update(magazineRequest); + magazine.update(magazineRequest, magazineCategory); return MagazineResponse.Get.from(magazine); } From c1483a0447fe404553809040ad6a0f48bd54ac79 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 26 Mar 2024 18:56:31 +0900 Subject: [PATCH 020/103] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A7=A4=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/codebase/domain/magazine/entity/Magazine.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java b/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java index c7ac4303..624bf3d8 100644 --- a/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java +++ b/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java @@ -104,10 +104,11 @@ public boolean isOwner(String loginUsername) { return member.getUsername().equals(loginUsername); } - public void update(MagazineRequest.Update magazineRequest) { + public void update(MagazineRequest.Update magazineRequest, MagazineCategory category) { this.title = magazineRequest.getTitle(); this.content = magazineRequest.getContent(); this.metadata = magazineRequest.getMetadata(); + this.category = category; this.updatedTime = LocalDateTime.now(); } @@ -141,8 +142,4 @@ public void addMedia(MagazineMedia magazineMedia) { this.magazineMedias.add(magazineMedia); } - public void setCategory(MagazineCategory category) { - this.category = category; - this.updatedTime = LocalDateTime.now(); - } } From 33a2c4f45dc05e10e7fb429e227731a552470fc2 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 26 Mar 2024 18:57:42 +0900 Subject: [PATCH 021/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20-=20magazineCategory?= =?UTF-8?q?=EC=9D=98=20=EA=B2=80=EC=A6=9D=20=EC=BD=94=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4=20-=20ma?= =?UTF-8?q?gazineCategory=EC=9D=98=20=EC=83=9D=EC=84=B1=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=8B=9C=20category=EB=A5=BC=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=ED=97=80?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MagazineCategoryControllerTest.java | 55 +++++++++++++++++++ .../controller/MagazineControllerTest.java | 8 +++ 2 files changed, 63 insertions(+) diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index 3a86aed5..c7f3f7ec 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -403,4 +403,59 @@ public void updateCategory() throws Exception { .andExpect(result -> assertEquals("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์— ์†ํ•œ ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); } + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ฑ์‹œ ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์‚ฐํ•˜ ์ด๋ฆ„์ด ๊ฐ™์€ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•  ๊ฒฝ์šฐ ์—๋Ÿฌ 404") + @Test + public void ์นดํ…Œ๊ณ ๋ฆฌ_์ƒ์„ฑ์‹œ_๊ฐ™์€๋ถ€๋ชจ_์‚ฐํ•˜_์ด๋ฆ„_์ค‘๋ณต_์—๋Ÿฌ() throws Exception { + // given + MagazineCategoryResponse.Create parentCategory = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("๋น„๊ต์นดํ…Œ๊ณ ๋ฆฌ", "parentCategory", null) + ); + + MagazineCategoryResponse.Create existingCategory = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("์ด๋ฆ„์ด๊ฐ™์€์นดํ…Œ๊ณ ๋ฆฌ", "otherCategory", parentCategory.getId()) + ); + + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("์ด๋ฆ„์ด๊ฐ™์€์นดํ…Œ๊ณ ๋ฆฌ", "category", parentCategory.getId()); + + // when + mockMvc.perform( + post("/api/magazine-category") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("ํ•ด๋‹น ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์‚ฐํ•˜ ์ด๋ฆ„์ด ๊ฐ™์€ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ ์ˆ˜์ •์‹œ ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์‚ฐํ•˜ ์ด๋ฆ„์ด ๊ฐ™์€ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•  ๊ฒฝ์šฐ ์—๋Ÿฌ 404") + @Test + public void ์นดํ…Œ๊ณ ๋ฆฌ_์ˆ˜์ •์‹œ_๊ฐ™์€๋ถ€๋ชจ_์‚ฐํ•˜_์ด๋ฆ„_์ค‘๋ณต_์—๋Ÿฌ() throws Exception { + // given + MagazineCategoryResponse.Create parentCategory = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("๋น„๊ต์นดํ…Œ๊ณ ๋ฆฌ", "parentCategory", null) + ); + + magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("์ด๋ฆ„์ด๊ฐ™์€์นดํ…Œ๊ณ ๋ฆฌ", "childCategory", parentCategory.getId()) + ); + + MagazineCategoryResponse.Create changeCategory = magazineCategoryService.createCategory( + new MagazineCategoryRequest.Create("์ˆ˜์ •ํ• ์นดํ…Œ๊ณ ๋ฆฌ", "otherCategory", parentCategory.getId()) + ); + + MagazineCategoryRequest.Update updateRequest = new MagazineCategoryRequest.Update("์ด๋ฆ„์ด๊ฐ™์€์นดํ…Œ๊ณ ๋ฆฌ", "otherCategory", parentCategory.getId()); + + // when + mockMvc.perform( + put("/api/magazine-category/" + changeCategory.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("ํ•ด๋‹น ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์‚ฐํ•˜ ์ด๋ฆ„์ด ๊ฐ™์€ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } } \ No newline at end of file diff --git a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java index 435c65e8..1b125c6b 100644 --- a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java @@ -163,6 +163,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, assertTrue(magazine.getId() > 0); assertEquals(magazine.getTitle(), magazineRequest.getTitle()); assertEquals(magazine.getContent(), magazineRequest.getContent()); + assertEquals(magazine.getCategoryId(), category.getId()); } @WithMockCustomUser(username = "testid", role = "USER") @@ -233,6 +234,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, assertEquals(magazine.getId(), magazineResponse.getId()); assertEquals(magazine.getTitle(), magazineResponse.getTitle()); assertEquals(magazine.getContent(), magazineResponse.getContent()); + assertEquals(magazine.getCategoryId(), magazineResponse.getCategoryId()); } @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ธ ์กฐํšŒ์‹œ ์—†๋Š” ๋งค๊ฑฐ์ง„์ด๋ฉด 404.") @@ -261,6 +263,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineRequest.Update magazineRequest = new MagazineRequest.Update(); magazineRequest.setTitle("์ˆ˜์ •๋œ ์ œ๋ชฉ"); magazineRequest.setContent("์ˆ˜์ •๋œ ๋‚ด์šฉ"); + magazineRequest.setCategoryId(createCategory().getId()); // when String response = mockMvc.perform( @@ -277,6 +280,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, assertEquals(magazine.getId(), magazineResponse.getId()); assertEquals(magazineRequest.getTitle(), magazineResponse.getTitle()); assertEquals(magazineRequest.getContent(), magazineResponse.getContent()); + assertEquals(magazineRequest.getCategoryId(), magazineResponse.getCategoryId()); } @WithMockCustomUser(username = "testid", role = "USER") @@ -284,9 +288,11 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, @Test void ๋งค๊ฑฐ์ง„_์ˆ˜์ •_์—๋Ÿฌ() throws Exception { // given + MagazineCategory category = createCategory(); MagazineRequest.Update magazineRequest = new MagazineRequest.Update(); magazineRequest.setTitle("์ˆ˜์ •๋œ ์ œ๋ชฉ"); magazineRequest.setContent("์ˆ˜์ •๋œ ๋‚ด์šฉ"); + magazineRequest.setCategoryId(category.getId()); // when String content = mockMvc.perform( @@ -612,6 +618,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, // given Member member = createMember("testid"); MagazineResponse.Get magaizne = createMagaizne(member); + MagazineCategory category = createCategory(); MagazineRequest.Update magazineRequest = new MagazineRequest.Update(); magazineRequest.setTitle(magaizne.getTitle()); @@ -621,6 +628,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, "color", "๋นจ๊ฐ•์œผ๋กœ", "font", "๋‹ค๋ฅธ ํฐํŠธ" )); + magazineRequest.setCategoryId(category.getId()); // when String response = mockMvc.perform( From 067daa75ca5f1321a170ed86bbaa865a7fd865f1 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Wed, 27 Mar 2024 13:57:49 +0900 Subject: [PATCH 022/103] =?UTF-8?q?refactor:=20magazine=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1,=20=EC=88=98=EC=A0=95=EC=8B=9C=20categorySlug?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?-=20magazine=20=EC=83=9D=EC=84=B1,=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=8B=9C=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=8A=AC?= =?UTF-8?q?=EB=9F=AC=EA=B7=B8=EB=A5=BC=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/codebase/controller/MagazineController.java | 6 ++++-- .../codebase/domain/magazine/dto/MagazineRequest.java | 4 ++-- .../magazine/repository/MagazineCategoryRepository.java | 5 ++++- .../domain/magazine/service/MagazineCategoryService.java | 6 +++--- .../codebase/domain/magazine/service/MagazineService.java | 5 +---- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/MagazineController.java b/src/main/java/com/example/codebase/controller/MagazineController.java index 10ad92a1..2edbe262 100644 --- a/src/main/java/com/example/codebase/controller/MagazineController.java +++ b/src/main/java/com/example/codebase/controller/MagazineController.java @@ -51,7 +51,7 @@ public ResponseEntity createMagazine( // ์—ฐ๊ด€ ๊ฐ์ฒด ์กฐํšŒ Member member = memberService.getEntity(loginUsername); - MagazineCategory category = magazineCategoryService.getEntity(magazineRequest.getCategoryId()); + MagazineCategory category = magazineCategoryService.getEntity(magazineRequest.getCategorySlug()); // ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ๋กœ์ง ์ˆ˜ํ–‰ MagazineResponse.Get magazine = magazineService.create(magazineRequest, member, category); @@ -107,7 +107,9 @@ public ResponseEntity updateMagazine( ) { String loginUsername = SecurityUtil.getCurrentUsername() .orElseThrow(LoginRequiredException::new); - MagazineResponse.Get magazine = magazineService.update(id, loginUsername, magazineRequest); + + MagazineCategory category = magazineCategoryService.getEntity(magazineRequest.getCategorySlug()); + MagazineResponse.Get magazine = magazineService.update(id, loginUsername, magazineRequest, category); return new ResponseEntity(magazine, HttpStatus.OK); } diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java index 4bed8737..6f0d5aed 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java @@ -26,7 +26,7 @@ public static class Create { private String content; @NotNull - private Long categoryId; + private String categorySlug; // TODO: metadata ์— ๋Œ€ํ•ด ๊ฒ€์ฆ ํ•„์š” (๋ณด์•ˆ) @Schema(description = "JSON ํ˜•์‹์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ") @@ -48,7 +48,7 @@ public static class Update { private String content; @NotNull - private Long categoryId; + private String categorySlug; private Map metadata; diff --git a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java index 970a5d52..63ccb0ab 100644 --- a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java +++ b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineCategoryRepository.java @@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.Query; import java.util.List; +import java.util.Optional; public interface MagazineCategoryRepository extends JpaRepository { @@ -16,7 +17,7 @@ public interface MagazineCategoryRepository extends JpaRepository findBySlug(String slug); + List findBySlugWithChild(String slug); boolean existsByNameAndParent(String name, MagazineCategory parentCategory); @@ -24,4 +25,6 @@ public interface MagazineCategoryRepository extends JpaRepository findBySlug(String slug); } diff --git a/src/main/java/com/example/codebase/domain/magazine/service/MagazineCategoryService.java b/src/main/java/com/example/codebase/domain/magazine/service/MagazineCategoryService.java index 044377c6..affdeaf8 100644 --- a/src/main/java/com/example/codebase/domain/magazine/service/MagazineCategoryService.java +++ b/src/main/java/com/example/codebase/domain/magazine/service/MagazineCategoryService.java @@ -62,13 +62,13 @@ public MagazineCategoryResponse.GetAll getAllCategory() { @Transactional(readOnly = true) public MagazineCategoryResponse.GetAll getSubCategories(String slug) { - List subCategories = magazineCategoryRepository.findBySlug(slug); + List subCategories = magazineCategoryRepository.findBySlugWithChild(slug); return MagazineCategoryResponse.GetAll.from(subCategories); } @Transactional(readOnly = true) - public MagazineCategory getEntity(Long categoryId) { - return magazineCategoryRepository.findById(categoryId) + public MagazineCategory getEntity(String categorySlug) { + return magazineCategoryRepository.findBySlug(categorySlug) .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); } diff --git a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java index ab1eaec5..c6d24aee 100644 --- a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java +++ b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java @@ -79,13 +79,10 @@ public void delete(String loginUsername, Long id) { } @Transactional - public MagazineResponse.Get update(Long id, String loginUsername, MagazineRequest.Update magazineRequest) { + public MagazineResponse.Get update(Long id, String loginUsername, MagazineRequest.Update magazineRequest, MagazineCategory magazineCategory) { Magazine magazine = magazineRepository.findById(id) .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); - MagazineCategory magazineCategory = magazineCategoryRepository.findById(magazineRequest.getCategoryId()) - .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); - validateOwner(loginUsername, magazine); magazine.update(magazineRequest, magazineCategory); From 26188d33450bd9c8de43b2e6cdbedbd972836c33 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Wed, 27 Mar 2024 13:58:50 +0900 Subject: [PATCH 023/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20-=20magazine=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=8B=9C=20categorySlug=EB=A5=BC=20=EB=B0=9B=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD=EB=90=A8=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20=EC=88=98=EC=A0=95=ED=96=88=EC=8A=B5=EB=8B=88?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CurationControllerTest.java | 10 ++++---- .../MagazineCategoryControllerTest.java | 13 +++++----- .../controller/MagazineControllerTest.java | 24 +++++++++---------- .../MagazineLikeControllerTest.java | 2 +- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index e6a5a0a0..ed39c88f 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -148,19 +148,19 @@ public MagazineCategory createCategory() { MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); - return magazineCategoryService.getEntity(category.getId()); + return magazineCategoryService.getEntity(category.getSlug()); } public MagazineCategory createCategory(String name, String slug) { MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(name, slug, null); - List categories = magazineCategoryRepository.findBySlug(request.getSlug()); + List categories = magazineCategoryRepository.findBySlugWithChild(request.getSlug()); if (!categories.isEmpty()) { return categories.get(0); } else { MagazineCategoryResponse.Create categoryResponse = magazineCategoryService.createCategory(request); - return magazineCategoryService.getEntity(categoryResponse.getId()); + return magazineCategoryService.getEntity(categoryResponse.getSlug()); } } @@ -170,7 +170,7 @@ public MagazineResponse.Get createMagazine(Member member) { MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); return magazineService.create(magazineRequest, member, category); } @@ -179,7 +179,7 @@ MagazineResponse.Get createMagazine(Member member, MagazineCategory category) { MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); return magazineService.create(magazineRequest, member, category); } diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index c7f3f7ec..34a3c68c 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -84,14 +84,14 @@ public MagazineCategory createCategoryAndLoad() { MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); - return magazineCategoryService.getEntity(category.getId()); + return magazineCategoryService.getEntity(category.getSlug()); } public MagazineResponse.Get createMagaizne(Member member, MagazineCategory category) { MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); magazineRequest.setMetadata(Map.of( "color", "blue", "font", "godic" @@ -258,19 +258,20 @@ public void updateCategory() throws Exception { MagazineCategoryRequest.Update updateRequest = new MagazineCategoryRequest.Update("์ˆ˜์ •๋œ ๊ธ€", "updated-word", parentCategoryAfter.getId()); // when - mockMvc.perform( + String response = mockMvc.perform( put("/api/magazine-category/" + childCategory.getId()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updateRequest)) ) .andDo(print()) - .andExpect(status().isOk()); + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); // then - MagazineCategory updatedCategory = magazineCategoryService.getEntity(childCategory.getId()); + MagazineCategoryResponse.Get updatedCategory =objectMapper.readValue(response, MagazineCategoryResponse.Get.class); assertEquals(updateRequest.getName(), updatedCategory.getName()); assertEquals(updateRequest.getSlug(), updatedCategory.getSlug()); - assertEquals(updateRequest.getParentId(), updatedCategory.getParent().getId()); + assertEquals(updateRequest.getParentId(), updatedCategory.getParentCategory().getId()); } @WithMockCustomUser(username = "admin", role = "ADMIN") diff --git a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java index 1b125c6b..4606a85f 100644 --- a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java @@ -90,7 +90,7 @@ public MagazineResponse.Get createMagaizne(Member member) { MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); magazineRequest.setMetadata(Map.of( "color", "blue", "font", "godic" @@ -115,7 +115,7 @@ public MagazineCategory createCategory() { MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); - return magazineCategoryService.getEntity(category.getId()); + return magazineCategoryService.getEntity(category.getSlug()); } public MagazineResponse.Get createComment(MagazineResponse.Get magaizne, Member member, String comment) { @@ -146,7 +146,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); // when String response = mockMvc.perform( @@ -175,7 +175,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(0L); + magazineRequest.setCategorySlug("slug"); // when String content = mockMvc.perform( @@ -263,7 +263,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineRequest.Update magazineRequest = new MagazineRequest.Update(); magazineRequest.setTitle("์ˆ˜์ •๋œ ์ œ๋ชฉ"); magazineRequest.setContent("์ˆ˜์ •๋œ ๋‚ด์šฉ"); - magazineRequest.setCategoryId(createCategory().getId()); + magazineRequest.setCategorySlug(createCategory().getSlug()); // when String response = mockMvc.perform( @@ -280,7 +280,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, assertEquals(magazine.getId(), magazineResponse.getId()); assertEquals(magazineRequest.getTitle(), magazineResponse.getTitle()); assertEquals(magazineRequest.getContent(), magazineResponse.getContent()); - assertEquals(magazineRequest.getCategoryId(), magazineResponse.getCategoryId()); + assertEquals(magazineRequest.getCategorySlug(), magazineResponse.getCategorySlug()); } @WithMockCustomUser(username = "testid", role = "USER") @@ -292,7 +292,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineRequest.Update magazineRequest = new MagazineRequest.Update(); magazineRequest.setTitle("์ˆ˜์ •๋œ ์ œ๋ชฉ"); magazineRequest.setContent("์ˆ˜์ •๋œ ๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); // when String content = mockMvc.perform( @@ -491,7 +491,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); magazineRequest.setMediaUrls(List.of( "https://cdn.artscope.kr/local/1.jpg", "https://cdn.artscope.kr/local/2.jpg" @@ -527,7 +527,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); magazineRequest.setMediaUrls(List.of( "/local/1.jpg", "1.jpg" @@ -556,7 +556,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); magazineRequest.setMediaUrls(List.of( "https://cdn.artscope.kr/local/1.jpg", "https://cdn.artscope.kr/local/2.jpg", @@ -594,7 +594,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); magazineRequest.setMetadata(Map.of( "color", "blue", "font", "godic" @@ -628,7 +628,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, "color", "๋นจ๊ฐ•์œผ๋กœ", "font", "๋‹ค๋ฅธ ํฐํŠธ" )); - magazineRequest.setCategoryId(category.getId()); + magazineRequest.setCategorySlug(category.getSlug()); // when String response = mockMvc.perform( diff --git a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java index 218b2a98..e0d5e000 100644 --- a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java @@ -159,7 +159,7 @@ public MagazineCategory createCategory() { MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create(categoryName, categorySlug, null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); - return magazineCategoryService.getEntity(category.getId()); + return magazineCategoryService.getEntity(category.getSlug()); } @WithMockCustomUser(username = "testid") From 09900bfec1f9ff247f3dc12c6e797b375f442e14 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Wed, 27 Mar 2024 14:11:02 +0900 Subject: [PATCH 024/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20-=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=EB=A5=BC=20?= =?UTF-8?q?=EA=B0=80=EC=A7=84=20=EB=A7=A4=EA=B1=B0=EC=A7=84=EC=9D=B4=20?= =?UTF-8?q?=EC=9E=88=EC=9D=84=EA=B2=BD=EC=9A=B0=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EB=A5=BC=20=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MagazineCategoryControllerTest.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index 34a3c68c..99d67986 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -459,4 +459,27 @@ public void updateCategory() throws Exception { .andExpect(status().isBadRequest()) .andExpect(result -> assertEquals("ํ•ด๋‹น ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์‚ฐํ•˜ ์ด๋ฆ„์ด ๊ฐ™์€ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); } -} \ No newline at end of file + + @WithMockCustomUser(username = "admin", role = "ADMIN") + @DisplayName("์นดํ…Œ๊ณ ๋ฆฌ ์‚ญ์ œ์‹œ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ๊ฐ€์ง„ ๋ฉ”๊ฑฐ์ง„์ด ์žˆ์„๊ฒฝ์šฐ ์—๋Ÿฌ 404") + @Test + public void ์นดํ…Œ๊ณ ๋ฆฌ_์‚ญ์ œ์‹œ_๋งค๊ฑฐ์ง„์ด_์žˆ์„_๊ฒฝ์šฐ_์—๋Ÿฌ() throws Exception { + // given + MagazineCategory category = createCategoryAndLoad(); + + Member member = createMember("admin"); + + createMagaizne(member, category); + + // when + mockMvc.perform( + delete("/api/magazine-category/" + category.getId()) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์— ์†ํ•œ ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + +} + From c4868c1ebe3bd99691c4fea476fa40915838184f Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Thu, 28 Mar 2024 14:23:05 +0900 Subject: [PATCH 025/103] =?UTF-8?q?feat:=20group,group=5Fuser=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/V202403051155__application.sql | 19 +++++++++++++++++ .../migration/V202403051156__application.sql | 21 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/main/resources/db/migration/V202403051155__application.sql create mode 100644 src/main/resources/db/migration/V202403051156__application.sql diff --git a/src/main/resources/db/migration/V202403051155__application.sql b/src/main/resources/db/migration/V202403051155__application.sql new file mode 100644 index 00000000..57641d34 --- /dev/null +++ b/src/main/resources/db/migration/V202403051155__application.sql @@ -0,0 +1,19 @@ +# group ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค +# ์ž‘์„ฑ์ž : gimdonghyeon (haroya01@naver.com) +# ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-28 +# ํ˜„์žฌ ๋ฒ„์ „ : V202403051155 (์ด์ „ ๋ฒ„์ „ : V202403051154__application.sql) + +CREATE TABLE `group` +( + group_id BIGINT PRIMARY KEY AUTO_INCREMENT, + description TEXT, + address varchar(255), + profile_image varchar(512) NOT NULL, + background_image varchar(512) NOT NULL, + name varchar(50) NOT NULL, + created_time TIMESTAMP NOT NULL, + updated_time TIMESTAMP NOT NULL, + is_deleted BOOL DEFAULT FALSE NOT NULL +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4; + diff --git a/src/main/resources/db/migration/V202403051156__application.sql b/src/main/resources/db/migration/V202403051156__application.sql new file mode 100644 index 00000000..840f1fcb --- /dev/null +++ b/src/main/resources/db/migration/V202403051156__application.sql @@ -0,0 +1,21 @@ + +# group_user ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑ +# ์ž‘์„ฑ์ž : gimdonghyeon (haroya01@naver.com) +# ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-28 +# ํ˜„์žฌ ๋ฒ„์ „ : V202403051156 (์ด์ „ ๋ฒ„์ „ : V202403051155__application.sql) + + +CREATE TABLE `group_user` +( + group_user_id BIGINT PRIMARY KEY AUTO_INCREMENT, + member_id BINARY(16) NOT NULL, + group_id BIGINT NOT NULL, + position varchar(100) NOT NULL, + user_role VARCHAR(10), + created_time DATETIME NOT NULL, + updated_time DATETIME NOT NULL, + UNIQUE (member_id, group_id), + CONSTRAINT fk_group_user_member_id FOREIGN KEY (member_id) REFERENCES `member` (member_id), + CONSTRAINT fk_group_user_group_id FOREIGN KEY (group_id) REFERENCES `group` (group_id) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4; \ No newline at end of file From 61761c7e66d48dabde8eac5887eb96a03ae851c3 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Thu, 28 Mar 2024 14:26:38 +0900 Subject: [PATCH 026/103] =?UTF-8?q?feat:=20=EC=B6=94=EA=B0=80=EB=90=9C=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=EB=8C=80=ED=95=9C=20ent?= =?UTF-8?q?ity=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=9E=91=EC=84=B1=20-=20gr?= =?UTF-8?q?oup,=20group=5Fuser=20=EC=97=90=20=EB=8C=80=ED=95=9C=20entity?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A5=BC=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/domain/group/entity/Group.java | 52 +++++++++++++++++++ .../domain/group/entity/GroupUser.java | 46 ++++++++++++++++ .../domain/group/entity/GroupUserRole.java | 6 +++ .../group/repository/GroupRepository.java | 7 +++ .../group/repository/GroupUserRepository.java | 7 +++ 5 files changed, 118 insertions(+) create mode 100644 src/main/java/com/example/codebase/domain/group/entity/Group.java create mode 100644 src/main/java/com/example/codebase/domain/group/entity/GroupUser.java create mode 100644 src/main/java/com/example/codebase/domain/group/entity/GroupUserRole.java create mode 100644 src/main/java/com/example/codebase/domain/group/repository/GroupRepository.java create mode 100644 src/main/java/com/example/codebase/domain/group/repository/GroupUserRepository.java diff --git a/src/main/java/com/example/codebase/domain/group/entity/Group.java b/src/main/java/com/example/codebase/domain/group/entity/Group.java new file mode 100644 index 00000000..9ea5d796 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/group/entity/Group.java @@ -0,0 +1,52 @@ +package com.example.codebase.domain.group.entity; + +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.SQLRestriction; + +import java.time.LocalDateTime; + +import static lombok.AccessLevel.PROTECTED; + +@Entity +@Table(name = "group") +@Getter +@AllArgsConstructor +@Builder +@NoArgsConstructor(access = PROTECTED) +@SQLRestriction("is_deleted = false") +public class Group { + + @Id + @Column(name = "group_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "group_description") + private String description; + + @Column(name = "address") + private String address; + + @Column(name = "profile_image", nullable = false) + private String profileImage; + + @Column(name = "background_image", nullable = false) + private String backgroundImage; + + @Column(name = "name", nullable = false) + private String name; + + @Builder.Default + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + @Builder.Default + @Column(name = "updated_time") + private LocalDateTime updatedTime = LocalDateTime.now(); + + @Builder.Default + @Column(name = "is_deleted") + private Boolean isDeleted = false; + +} diff --git a/src/main/java/com/example/codebase/domain/group/entity/GroupUser.java b/src/main/java/com/example/codebase/domain/group/entity/GroupUser.java new file mode 100644 index 00000000..b20fed1f --- /dev/null +++ b/src/main/java/com/example/codebase/domain/group/entity/GroupUser.java @@ -0,0 +1,46 @@ +package com.example.codebase.domain.group.entity; + +import com.example.codebase.domain.member.entity.Member; +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +import static lombok.AccessLevel.PROTECTED; + +@Entity +@Table(name = "group_user") +@Getter +@NoArgsConstructor(access = PROTECTED) +@AllArgsConstructor +@Builder +public class GroupUser { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "group_user_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "group_id") + private Group group; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Column(name = "position") + private String position; + + @Enumerated(EnumType.STRING) + @Column(name = "user_role") + private GroupUserRole role; + + @Builder.Default + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + @Builder.Default + @Column(name = "updated_time") + private LocalDateTime updatedTime = LocalDateTime.now(); +} diff --git a/src/main/java/com/example/codebase/domain/group/entity/GroupUserRole.java b/src/main/java/com/example/codebase/domain/group/entity/GroupUserRole.java new file mode 100644 index 00000000..cecadd3a --- /dev/null +++ b/src/main/java/com/example/codebase/domain/group/entity/GroupUserRole.java @@ -0,0 +1,6 @@ +package com.example.codebase.domain.group.entity; + +public enum GroupUserRole { + OWNER, // ์†Œ์œ ์ž + MEMBER, // ๋ฉค๋ฒ„ +} diff --git a/src/main/java/com/example/codebase/domain/group/repository/GroupRepository.java b/src/main/java/com/example/codebase/domain/group/repository/GroupRepository.java new file mode 100644 index 00000000..69eddbdb --- /dev/null +++ b/src/main/java/com/example/codebase/domain/group/repository/GroupRepository.java @@ -0,0 +1,7 @@ +package com.example.codebase.domain.group.repository; + +import com.example.codebase.domain.group.entity.Group; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface GroupRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/codebase/domain/group/repository/GroupUserRepository.java b/src/main/java/com/example/codebase/domain/group/repository/GroupUserRepository.java new file mode 100644 index 00000000..f8968897 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/group/repository/GroupUserRepository.java @@ -0,0 +1,7 @@ +package com.example.codebase.domain.group.repository; + +import com.example.codebase.domain.group.entity.GroupUser; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface GroupUserRepository extends JpaRepository { +} From c8c6cb5ab5c343593c315775ff664c8cd33b0a70 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Thu, 28 Mar 2024 14:28:06 +0900 Subject: [PATCH 027/103] =?UTF-8?q?refactor:=20=EB=88=84=EB=9D=BD=EB=90=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=82=B4=EC=9A=A9=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20-=20group=5Fdescription=EC=97=90=EC=84=9C=20description?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=EB=90=9C=20column?= =?UTF-8?q?=EC=9D=B4=20=EB=88=84=EB=9D=BD=EB=90=9C=EA=B1=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/codebase/domain/group/entity/Group.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/group/entity/Group.java b/src/main/java/com/example/codebase/domain/group/entity/Group.java index 9ea5d796..1301c5bc 100644 --- a/src/main/java/com/example/codebase/domain/group/entity/Group.java +++ b/src/main/java/com/example/codebase/domain/group/entity/Group.java @@ -22,7 +22,7 @@ public class Group { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "group_description") + @Column(name = "description") private String description; @Column(name = "address") From 00af05547faa8ff27da8e645f8c7417f4852f3ea Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Thu, 28 Mar 2024 14:47:08 +0900 Subject: [PATCH 028/103] =?UTF-8?q?refactor:=20@WHERE=EC=9D=B4=20@Deprecat?= =?UTF-8?q?ed=EC=97=90=20=EB=94=B0=EB=9D=BC=20soft-delete=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/codebase/domain/group/entity/Group.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/group/entity/Group.java b/src/main/java/com/example/codebase/domain/group/entity/Group.java index 1301c5bc..6bd123d4 100644 --- a/src/main/java/com/example/codebase/domain/group/entity/Group.java +++ b/src/main/java/com/example/codebase/domain/group/entity/Group.java @@ -2,7 +2,8 @@ import jakarta.persistence.*; import lombok.*; -import org.hibernate.annotations.SQLRestriction; +import org.hibernate.annotations.SoftDelete; +import org.hibernate.annotations.SoftDeleteType; import java.time.LocalDateTime; @@ -14,7 +15,6 @@ @AllArgsConstructor @Builder @NoArgsConstructor(access = PROTECTED) -@SQLRestriction("is_deleted = false") public class Group { @Id @@ -46,6 +46,7 @@ public class Group { private LocalDateTime updatedTime = LocalDateTime.now(); @Builder.Default + @SoftDelete(strategy = SoftDeleteType.DELETED) @Column(name = "is_deleted") private Boolean isDeleted = false; From 5156004850ab22d2f81bef198247001550d863fd Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Thu, 28 Mar 2024 16:00:33 +0900 Subject: [PATCH 029/103] =?UTF-8?q?feat:=20Group=20->=20Team=20=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=EB=90=A8=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20SQL=20=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/V202403051155__application.sql | 20 ++++++++-------- .../migration/V202403051156__application.sql | 23 +++++++++---------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/main/resources/db/migration/V202403051155__application.sql b/src/main/resources/db/migration/V202403051155__application.sql index 57641d34..e36d8784 100644 --- a/src/main/resources/db/migration/V202403051155__application.sql +++ b/src/main/resources/db/migration/V202403051155__application.sql @@ -3,17 +3,17 @@ # ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-28 # ํ˜„์žฌ ๋ฒ„์ „ : V202403051155 (์ด์ „ ๋ฒ„์ „ : V202403051154__application.sql) -CREATE TABLE `group` +CREATE TABLE `team` ( - group_id BIGINT PRIMARY KEY AUTO_INCREMENT, - description TEXT, - address varchar(255), - profile_image varchar(512) NOT NULL, - background_image varchar(512) NOT NULL, - name varchar(50) NOT NULL, - created_time TIMESTAMP NOT NULL, - updated_time TIMESTAMP NOT NULL, - is_deleted BOOL DEFAULT FALSE NOT NULL + `team_id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `description` TEXT, + `address` varchar(255), + `profile_image` varchar(512) NOT NULL, + `background_image` varchar(512) NOT NULL, + `name` varchar(50) NOT NULL, + `created_time` TIMESTAMP NOT NULL, + `updated_time` TIMESTAMP NOT NULL, + `is_deleted` BOOL DEFAULT FALSE NOT NULL ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; diff --git a/src/main/resources/db/migration/V202403051156__application.sql b/src/main/resources/db/migration/V202403051156__application.sql index 840f1fcb..d7640a5a 100644 --- a/src/main/resources/db/migration/V202403051156__application.sql +++ b/src/main/resources/db/migration/V202403051156__application.sql @@ -1,21 +1,20 @@ - # group_user ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑ # ์ž‘์„ฑ์ž : gimdonghyeon (haroya01@naver.com) # ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-28 # ํ˜„์žฌ ๋ฒ„์ „ : V202403051156 (์ด์ „ ๋ฒ„์ „ : V202403051155__application.sql) -CREATE TABLE `group_user` +CREATE TABLE `team_user` ( - group_user_id BIGINT PRIMARY KEY AUTO_INCREMENT, - member_id BINARY(16) NOT NULL, - group_id BIGINT NOT NULL, - position varchar(100) NOT NULL, - user_role VARCHAR(10), - created_time DATETIME NOT NULL, - updated_time DATETIME NOT NULL, - UNIQUE (member_id, group_id), - CONSTRAINT fk_group_user_member_id FOREIGN KEY (member_id) REFERENCES `member` (member_id), - CONSTRAINT fk_group_user_group_id FOREIGN KEY (group_id) REFERENCES `group` (group_id) + `team_user_id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `member_id` BINARY(16) NOT NULL, + `team_id` BIGINT NOT NULL, + `position` varchar(100) NOT NULL, + `user_role` VARCHAR(10), + `created_time` DATETIME NOT NULL, + `updated_time` DATETIME NOT NULL, + UNIQUE (`member_id`, `team_id`), + CONSTRAINT `fk_team_user_member_id` FOREIGN KEY (`member_id`) REFERENCES `member` (`member_id`), + CONSTRAINT `fk_team_user_team_id` FOREIGN KEY (`team_id`) REFERENCES `team` (`team_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; \ No newline at end of file From 243c1bb5a89760d84023d4aad138bcf851199290 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Thu, 28 Mar 2024 16:02:35 +0900 Subject: [PATCH 030/103] =?UTF-8?q?feat:=20Group=20->=20Team=20=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=EB=90=A8=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JPA 6.4 ๋ถ€ํ„ฐ ์ง€์›ํ•˜๋Š” @SoftDelete ๊ธฐ๋Šฅ Team ์—”ํ‹ฐํ‹ฐ์— ์ ์šฉ - ์ด๋กœ ์ธํ•ด TeamUser์— Team ๊ด€๋ จ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ EAGER ์ „๋žต์œผ๋กœ ์กฐํšŒ (์‚ญ์ œ๋ฌ๋Š”์ง€ ์•Œ๊ธฐ์œ„ํ•ด) --- .../codebase/controller/TeamController.java | 43 +++++++++++++++++++ .../domain/group/entity/GroupUserRole.java | 6 --- .../group/repository/GroupRepository.java | 7 --- .../group/repository/GroupUserRepository.java | 7 --- .../Group.java => team/entity/Team.java} | 17 +++----- .../entity/TeamUser.java} | 14 +++--- .../domain/team/entity/TeamUserRole.java | 6 +++ .../team/repository/TeamRepository.java | 7 +++ .../team/repository/TeamUserRepository.java | 7 +++ 9 files changed, 77 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/example/codebase/controller/TeamController.java delete mode 100644 src/main/java/com/example/codebase/domain/group/entity/GroupUserRole.java delete mode 100644 src/main/java/com/example/codebase/domain/group/repository/GroupRepository.java delete mode 100644 src/main/java/com/example/codebase/domain/group/repository/GroupUserRepository.java rename src/main/java/com/example/codebase/domain/{group/entity/Group.java => team/entity/Team.java} (74%) rename src/main/java/com/example/codebase/domain/{group/entity/GroupUser.java => team/entity/TeamUser.java} (78%) create mode 100644 src/main/java/com/example/codebase/domain/team/entity/TeamUserRole.java create mode 100644 src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java create mode 100644 src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java diff --git a/src/main/java/com/example/codebase/controller/TeamController.java b/src/main/java/com/example/codebase/controller/TeamController.java new file mode 100644 index 00000000..0dd91840 --- /dev/null +++ b/src/main/java/com/example/codebase/controller/TeamController.java @@ -0,0 +1,43 @@ +package com.example.codebase.controller; + +import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.repository.TeamRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; + +@RestController +@RequestMapping("/api/teams") +public class TeamController { + + @Autowired + private TeamRepository teamRepository; + + @PostMapping + public ResponseEntity createTeam() { + Team saved = teamRepository.save(Team.builder() + .name("๊ทธ๋ฃน1") + .address("์ฃผ์†Œ") + .createdTime(LocalDateTime.now()) + .updatedTime(LocalDateTime.now()) + .description("์šฐ๋ฆฌ ๊ทธ๋ฃน์€ ์–ด์ฉŒ๊ตฌ") + .backgroundImage("http://cdn.artscope.kr/image") + .profileImage("http://cdn.artscope.kr/image") + .build()); + return new ResponseEntity(saved, HttpStatus.OK); + } + + @GetMapping + public ResponseEntity getAllTeam() { + return new ResponseEntity(teamRepository.findAll(), HttpStatus.OK); + } + + @DeleteMapping + public ResponseEntity deleteTeam(@RequestParam Long id) { + teamRepository.deleteById(id); + return new ResponseEntity("์‚ญ์ œ๋จ", HttpStatus.OK); + } +} diff --git a/src/main/java/com/example/codebase/domain/group/entity/GroupUserRole.java b/src/main/java/com/example/codebase/domain/group/entity/GroupUserRole.java deleted file mode 100644 index cecadd3a..00000000 --- a/src/main/java/com/example/codebase/domain/group/entity/GroupUserRole.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.codebase.domain.group.entity; - -public enum GroupUserRole { - OWNER, // ์†Œ์œ ์ž - MEMBER, // ๋ฉค๋ฒ„ -} diff --git a/src/main/java/com/example/codebase/domain/group/repository/GroupRepository.java b/src/main/java/com/example/codebase/domain/group/repository/GroupRepository.java deleted file mode 100644 index 69eddbdb..00000000 --- a/src/main/java/com/example/codebase/domain/group/repository/GroupRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.example.codebase.domain.group.repository; - -import com.example.codebase.domain.group.entity.Group; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface GroupRepository extends JpaRepository { -} diff --git a/src/main/java/com/example/codebase/domain/group/repository/GroupUserRepository.java b/src/main/java/com/example/codebase/domain/group/repository/GroupUserRepository.java deleted file mode 100644 index f8968897..00000000 --- a/src/main/java/com/example/codebase/domain/group/repository/GroupUserRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.example.codebase.domain.group.repository; - -import com.example.codebase.domain.group.entity.GroupUser; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface GroupUserRepository extends JpaRepository { -} diff --git a/src/main/java/com/example/codebase/domain/group/entity/Group.java b/src/main/java/com/example/codebase/domain/team/entity/Team.java similarity index 74% rename from src/main/java/com/example/codebase/domain/group/entity/Group.java rename to src/main/java/com/example/codebase/domain/team/entity/Team.java index 1301c5bc..fef60595 100644 --- a/src/main/java/com/example/codebase/domain/group/entity/Group.java +++ b/src/main/java/com/example/codebase/domain/team/entity/Team.java @@ -1,24 +1,25 @@ -package com.example.codebase.domain.group.entity; +package com.example.codebase.domain.team.entity; import jakarta.persistence.*; import lombok.*; -import org.hibernate.annotations.SQLRestriction; +import org.hibernate.annotations.SoftDelete; +import org.hibernate.annotations.SoftDeleteType; import java.time.LocalDateTime; import static lombok.AccessLevel.PROTECTED; @Entity -@Table(name = "group") +@Table(name = "team") @Getter @AllArgsConstructor @Builder @NoArgsConstructor(access = PROTECTED) -@SQLRestriction("is_deleted = false") -public class Group { +@SoftDelete(columnName = "is_deleted", strategy = SoftDeleteType.DELETED) +public class Team { @Id - @Column(name = "group_id") + @Column(name = "team_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -45,8 +46,4 @@ public class Group { @Column(name = "updated_time") private LocalDateTime updatedTime = LocalDateTime.now(); - @Builder.Default - @Column(name = "is_deleted") - private Boolean isDeleted = false; - } diff --git a/src/main/java/com/example/codebase/domain/group/entity/GroupUser.java b/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java similarity index 78% rename from src/main/java/com/example/codebase/domain/group/entity/GroupUser.java rename to src/main/java/com/example/codebase/domain/team/entity/TeamUser.java index b20fed1f..ed7c49e2 100644 --- a/src/main/java/com/example/codebase/domain/group/entity/GroupUser.java +++ b/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java @@ -1,4 +1,4 @@ -package com.example.codebase.domain.group.entity; +package com.example.codebase.domain.team.entity; import com.example.codebase.domain.member.entity.Member; import jakarta.persistence.*; @@ -9,21 +9,21 @@ import static lombok.AccessLevel.PROTECTED; @Entity -@Table(name = "group_user") +@Table(name = "team_user") @Getter @NoArgsConstructor(access = PROTECTED) @AllArgsConstructor @Builder -public class GroupUser { +public class TeamUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "group_user_id") private Long id; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "group_id") - private Group group; + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "team_id") + private Team team; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") @@ -34,7 +34,7 @@ public class GroupUser { @Enumerated(EnumType.STRING) @Column(name = "user_role") - private GroupUserRole role; + private TeamUserRole role; @Builder.Default @Column(name = "created_time") diff --git a/src/main/java/com/example/codebase/domain/team/entity/TeamUserRole.java b/src/main/java/com/example/codebase/domain/team/entity/TeamUserRole.java new file mode 100644 index 00000000..1f9795df --- /dev/null +++ b/src/main/java/com/example/codebase/domain/team/entity/TeamUserRole.java @@ -0,0 +1,6 @@ +package com.example.codebase.domain.team.entity; + +public enum TeamUserRole { + OWNER, // ์†Œ์œ ์ž + MEMBER, // ๋ฉค๋ฒ„ +} diff --git a/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java b/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java new file mode 100644 index 00000000..849c7664 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java @@ -0,0 +1,7 @@ +package com.example.codebase.domain.team.repository; + +import com.example.codebase.domain.team.entity.Team; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TeamRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java new file mode 100644 index 00000000..521f5ace --- /dev/null +++ b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java @@ -0,0 +1,7 @@ +package com.example.codebase.domain.team.repository; + +import com.example.codebase.domain.team.entity.TeamUser; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TeamUserRepository extends JpaRepository { +} From 55d84d192a90d37e859aee8e5ee9026bafccedc8 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Thu, 28 Mar 2024 16:03:01 +0900 Subject: [PATCH 031/103] =?UTF-8?q?test:=20Team=20SoftDelete=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DB ๊ณ„์ธต ์œ ๋‹› ํ…Œ์ŠคํŠธ --- .../team/repository/TeamRepositoryTest.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/test/java/com/example/codebase/domain/team/repository/TeamRepositoryTest.java diff --git a/src/test/java/com/example/codebase/domain/team/repository/TeamRepositoryTest.java b/src/test/java/com/example/codebase/domain/team/repository/TeamRepositoryTest.java new file mode 100644 index 00000000..9efd356f --- /dev/null +++ b/src/test/java/com/example/codebase/domain/team/repository/TeamRepositoryTest.java @@ -0,0 +1,54 @@ +package com.example.codebase.domain.team.repository; + +import com.example.codebase.domain.team.entity.Team; +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.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +@DataJpaTest +@ActiveProfiles("test") +class TeamRepositoryTest { + + @Autowired + private TeamRepository teamRepository; + + private Team deletedTeam; + @BeforeEach + public void setup() { + deletedTeam = Team.builder() + .name("๊ทธ๋ฃน1") + .address("์ฃผ์†Œ") + .createdTime(LocalDateTime.now()) + .updatedTime(LocalDateTime.now()) + .description("์šฐ๋ฆฌ ๊ทธ๋ฃน์€ ์–ด์ฉŒ๊ตฌ") + .backgroundImage("http://cdn.artscope.kr/image") + .profileImage("http://cdn.artscope.kr/image") + .build(); + teamRepository.save(deletedTeam); + } + + @Transactional(propagation = Propagation.NOT_SUPPORTED) + @DisplayName("์‚ญ์ œ๋œ ํŒ€์€ ์กฐํšŒ๊ฐ€ ์•ˆ๋œ๋‹ค.") + @Test + public void test01() throws Exception { + // given + teamRepository.delete(deletedTeam); + + // when + Optional optionalTeam = teamRepository.findById(deletedTeam.getId()); + + // then + assertTrue(optionalTeam.isEmpty()); + } + +} \ No newline at end of file From 916b344ac9af77451ab1e76a3548612b4ec3357b Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Sun, 31 Mar 2024 17:06:21 +0900 Subject: [PATCH 032/103] =?UTF-8?q?feat:=20=ED=8C=80=20=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9E=89=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?DDL=20=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ํ…Œ์ด๋ธ” ํ™•์žฅ์„ ์œ„ํ•ด ๋ณตํ•ฉํ‚ค์—์„œ ๋Œ€๋ฆฌํ‚ค ์‚ฌ์šฉ์œผ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. - following_id -> folliwng_member_id ๋กœ ์ปฌ๋Ÿผ๋ช…์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. - following_team_id ์ปฌ๋Ÿผ์ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์ปฌ๋Ÿผ์€ team ํ…Œ์ด๋ธ”์˜ FK ์ž…๋‹ˆ๋‹ค. - UNIQUE ์ œ์•ฝ์กฐ๊ฑด์„ ํ†ตํ•ด ์ค‘๋ณต ํŒ”๋กœ์ž‰์„ ์ œํ•œํ–ˆ์Šต๋‹ˆ๋‹ค. --- .../migration/V202403051156__application.sql | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/main/resources/db/migration/V202403051156__application.sql b/src/main/resources/db/migration/V202403051156__application.sql index d7640a5a..7b15f2bd 100644 --- a/src/main/resources/db/migration/V202403051156__application.sql +++ b/src/main/resources/db/migration/V202403051156__application.sql @@ -1,20 +1,28 @@ -# group_user ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑ -# ์ž‘์„ฑ์ž : gimdonghyeon (haroya01@naver.com) -# ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-28 + +# ํŒ€ ํŒ”๋กœ์ž‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•œ ํ…Œ์ด๋ธ” ๋ณ€๊ฒฝ ๋ฐ ์ปฌ๋Ÿผ ์ถ”๊ฐ€ +# ์ž‘์„ฑ์ž : Hoon9901 (shonn.dev@gmail.com) +# ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-31 # ํ˜„์žฌ ๋ฒ„์ „ : V202403051156 (์ด์ „ ๋ฒ„์ „ : V202403051155__application.sql) +# ๋ณตํ•ฉํ‚ค๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ ์ „ FK ๊ฒธ PK๋กœ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ PK ์‚ญ์ œ ์ „, FK ์ œ๊ฑฐ +ALTER TABLE `follow` + DROP FOREIGN KEY `fk_follow_follow_id`, + DROP FOREIGN KEY `fk_follow_follower_id`; + +# ๋Œ€๋ฆฌํ‚ค ์ƒ์„ฑ ๋ฐ ๊ธฐ์กด ๋ณตํ•ฉํ‚ค(PK) ์ œ๊ฑฐ +ALTER TABLE `follow` + ADD `follow_id` BIGINT AUTO_INCREMENT PRIMARY KEY FIRST, + DROP PRIMARY KEY; + +# ๊ธฐ์กด ํŒ”๋กœ์ž‰ ์ปฌ๋Ÿผ ๋ช…์„ ํŒ”๋กœ์ž‰ ๋ฉค๋ฒ„๋กœ ๋ณ€๊ฒฝ / ์ƒˆ๋กœ์šด TEAM ํŒ”๋กœ์ž‰ ์ปฌ๋Ÿผ ์ƒ์„ฑ +ALTER TABLE `follow` + CHANGE `following_id` `following_member_id` BINARY(16) COMMENT 'member -> member follow', + ADD `following_team_id` BIGINT COMMENT 'member -> team follow' AFTER `following_member_id`; -CREATE TABLE `team_user` -( - `team_user_id` BIGINT PRIMARY KEY AUTO_INCREMENT, - `member_id` BINARY(16) NOT NULL, - `team_id` BIGINT NOT NULL, - `position` varchar(100) NOT NULL, - `user_role` VARCHAR(10), - `created_time` DATETIME NOT NULL, - `updated_time` DATETIME NOT NULL, - UNIQUE (`member_id`, `team_id`), - CONSTRAINT `fk_team_user_member_id` FOREIGN KEY (`member_id`) REFERENCES `member` (`member_id`), - CONSTRAINT `fk_team_user_team_id` FOREIGN KEY (`team_id`) REFERENCES `team` (`team_id`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; \ No newline at end of file +# ๊ธฐ์กด ๋ณ€๊ฒฝ๋œ ์ปฌ๋Ÿผ์œผ๋กœ FK ์ƒ์„ฑ ๋ฐ ํŒ€ ์ปฌ๋Ÿผ FK ์ƒ์„ฑ ๊ทธ๋ฆฌ๊ณ  +# ๋„๋ฉ”์ธ ์ œ์•ฝ ์กฐ๊ฑด ์ƒ์„ฑ +ALTER TABLE `follow` + ADD CONSTRAINT `fk_follow_follower_id` FOREIGN KEY (`follower_id`) REFERENCES `member` (`member_id`), + ADD CONSTRAINT `fk_follow_following_member_id` FOREIGN KEY (`following_member_id`) REFERENCES `member` (`member_id`), + ADD CONSTRAINT `fk_follow_following_team_id` FOREIGN KEY (`following_team_id`) REFERENCES `team` (`team_id`), + ADD CONSTRAINT UNIQUE (`follower_id`, `following_member_id`, `following_team_id`); From b9d72c220eabc196603e490749361fddd7f2b3b4 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Sun, 31 Mar 2024 17:20:01 +0900 Subject: [PATCH 033/103] =?UTF-8?q?feat:=20=ED=8C=80=20=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9E=89=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=20=ED=8C=94=EB=A1=9C=EC=9E=89=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - followingTeam ์—ฐ๊ด€๊ฐ์ฒด๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. - Unique ์ œ์•ฝ์กฐ๊ฑด์ด ๋ช…์‹œ์ ์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (H2 ํ…Œ์ŠคํŠธ ๋ฐ ๋ช…์‹œ์  ์„ ์–ธ ํšจ๊ณผ) - ๊ธฐ์กด ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋ณ€๊ฒฝ๋จ์— ๋”ฐ๋ผ FollowService ์ฝ”๋“œ๋„ ํ•œ ์ค„ ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. - ๊ธฐ์กด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์œผ๋กœ ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์œผ๋กœ ๋™์ž‘ํ•˜๋‹ˆ ๋ถˆํ•„์š”ํ•œ Member ์ชฝ follow ๊ด€๋ จ ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์„ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค. --- .../codebase/domain/follow/entity/Follow.java | 51 ++++++++++--------- .../domain/follow/service/FollowService.java | 2 +- .../codebase/domain/member/entity/Member.java | 8 --- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/follow/entity/Follow.java b/src/main/java/com/example/codebase/domain/follow/entity/Follow.java index a6ed13ac..92e0ecdc 100644 --- a/src/main/java/com/example/codebase/domain/follow/entity/Follow.java +++ b/src/main/java/com/example/codebase/domain/follow/entity/Follow.java @@ -1,55 +1,58 @@ package com.example.codebase.domain.follow.entity; import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.entity.Team; import jakarta.persistence.*; import lombok.*; -import org.springframework.data.domain.Persistable; import java.time.LocalDateTime; @Entity -@Table(name = "follow") +@Table(name = "follow", + uniqueConstraints = { + @UniqueConstraint(name = "UniqueFollowerAndAllFollowingDomains", columnNames = { + "follower_id", "following_member_id", "following_team_id" + }) + }) @Getter @Builder -@AllArgsConstructor -@NoArgsConstructor -@IdClass(FollowIds.class) -public class Follow implements Persistable{ +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Follow { @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "follow_id") + private Long id; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "follower_id", columnDefinition = "BINARY(16)") private Member follower; - @Id @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "following_id", columnDefinition = "BINARY(16)") - private Member following; + @JoinColumn(name = "following_member_id", columnDefinition = "BINARY(16)") + private Member followingMember; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "following_team_id") + private Team followingTeam; @Column(name = "follow_time") @Builder.Default - private LocalDateTime followTime = null; + private LocalDateTime followTime = LocalDateTime.now(); public static Follow of(Member follower, Member following) { return Follow.builder() .follower(follower) - .following(following) + .followingMember(following) .build(); } - @Override - public FollowIds getId() { - return new FollowIds(this.follower.getId(), this.following.getId()); - } - - @Override - @Transient - public boolean isNew() { - if(this.followTime == null) { - this.followTime = LocalDateTime.now(); - return true; - } - return false; + public static Follow of(Member follower, Team following) { + return Follow.builder() + .follower(follower) + .followingTeam(following) + .build(); } } diff --git a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java index a42ae598..bbd60258 100644 --- a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java +++ b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java @@ -74,7 +74,7 @@ public FollowMembersResponseDTO getFollowingList(Optional loginUsername, List followingMemberResponses = followingList.getContent().stream() .map(followWithIsFollow -> - FollowMemberDetailResponseDTO.of(followWithIsFollow.getFollow().getFollowing(), followWithIsFollow.getStatus())) + FollowMemberDetailResponseDTO.of(followWithIsFollow.getFollow().getFollowingMember(), followWithIsFollow.getStatus())) .toList(); return FollowMembersResponseDTO.of(followingMemberResponses, pageInfo); diff --git a/src/main/java/com/example/codebase/domain/member/entity/Member.java b/src/main/java/com/example/codebase/domain/member/entity/Member.java index 3174194f..840f8b16 100644 --- a/src/main/java/com/example/codebase/domain/member/entity/Member.java +++ b/src/main/java/com/example/codebase/domain/member/entity/Member.java @@ -122,14 +122,6 @@ public class Member { @OneToOne(mappedBy = "member", optional=false, cascade = CascadeType.ALL, fetch = FetchType.LAZY) private NotificationSetting notificationSettings; - @Builder.Default - @OneToMany(mappedBy = "follower", cascade = CascadeType.ALL) - private List followings = new ArrayList<>(); - - @Builder.Default - @OneToMany(mappedBy = "following", cascade = CascadeType.ALL) - private List followers = new ArrayList<>(); - public static User toUser(Member member) { return new User(member.getUsername(), member.getPassword(), member.getAuthorities().stream() .map(authority -> new SimpleGrantedAuthority(authority.getAuthority().getAuthorityName())) From caba3e4dcbfb5c9962ac7c3738c409c2d54933d1 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Sun, 31 Mar 2024 17:33:29 +0900 Subject: [PATCH 034/103] =?UTF-8?q?fix:=20V202403051155=5F=5Fapplication.s?= =?UTF-8?q?ql=20=EB=88=84=EB=9D=BD=EB=90=9C=20DDL=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A6=BD=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/V202403051156__application.sql | 42 ++++++++----------- .../migration/V202403051157__application.sql | 28 +++++++++++++ 2 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 src/main/resources/db/migration/V202403051157__application.sql diff --git a/src/main/resources/db/migration/V202403051156__application.sql b/src/main/resources/db/migration/V202403051156__application.sql index 7b15f2bd..d7640a5a 100644 --- a/src/main/resources/db/migration/V202403051156__application.sql +++ b/src/main/resources/db/migration/V202403051156__application.sql @@ -1,28 +1,20 @@ - -# ํŒ€ ํŒ”๋กœ์ž‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•œ ํ…Œ์ด๋ธ” ๋ณ€๊ฒฝ ๋ฐ ์ปฌ๋Ÿผ ์ถ”๊ฐ€ -# ์ž‘์„ฑ์ž : Hoon9901 (shonn.dev@gmail.com) -# ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-31 +# group_user ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑ +# ์ž‘์„ฑ์ž : gimdonghyeon (haroya01@naver.com) +# ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-28 # ํ˜„์žฌ ๋ฒ„์ „ : V202403051156 (์ด์ „ ๋ฒ„์ „ : V202403051155__application.sql) -# ๋ณตํ•ฉํ‚ค๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ ์ „ FK ๊ฒธ PK๋กœ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ PK ์‚ญ์ œ ์ „, FK ์ œ๊ฑฐ -ALTER TABLE `follow` - DROP FOREIGN KEY `fk_follow_follow_id`, - DROP FOREIGN KEY `fk_follow_follower_id`; - -# ๋Œ€๋ฆฌํ‚ค ์ƒ์„ฑ ๋ฐ ๊ธฐ์กด ๋ณตํ•ฉํ‚ค(PK) ์ œ๊ฑฐ -ALTER TABLE `follow` - ADD `follow_id` BIGINT AUTO_INCREMENT PRIMARY KEY FIRST, - DROP PRIMARY KEY; - -# ๊ธฐ์กด ํŒ”๋กœ์ž‰ ์ปฌ๋Ÿผ ๋ช…์„ ํŒ”๋กœ์ž‰ ๋ฉค๋ฒ„๋กœ ๋ณ€๊ฒฝ / ์ƒˆ๋กœ์šด TEAM ํŒ”๋กœ์ž‰ ์ปฌ๋Ÿผ ์ƒ์„ฑ -ALTER TABLE `follow` - CHANGE `following_id` `following_member_id` BINARY(16) COMMENT 'member -> member follow', - ADD `following_team_id` BIGINT COMMENT 'member -> team follow' AFTER `following_member_id`; -# ๊ธฐ์กด ๋ณ€๊ฒฝ๋œ ์ปฌ๋Ÿผ์œผ๋กœ FK ์ƒ์„ฑ ๋ฐ ํŒ€ ์ปฌ๋Ÿผ FK ์ƒ์„ฑ ๊ทธ๋ฆฌ๊ณ  -# ๋„๋ฉ”์ธ ์ œ์•ฝ ์กฐ๊ฑด ์ƒ์„ฑ -ALTER TABLE `follow` - ADD CONSTRAINT `fk_follow_follower_id` FOREIGN KEY (`follower_id`) REFERENCES `member` (`member_id`), - ADD CONSTRAINT `fk_follow_following_member_id` FOREIGN KEY (`following_member_id`) REFERENCES `member` (`member_id`), - ADD CONSTRAINT `fk_follow_following_team_id` FOREIGN KEY (`following_team_id`) REFERENCES `team` (`team_id`), - ADD CONSTRAINT UNIQUE (`follower_id`, `following_member_id`, `following_team_id`); +CREATE TABLE `team_user` +( + `team_user_id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `member_id` BINARY(16) NOT NULL, + `team_id` BIGINT NOT NULL, + `position` varchar(100) NOT NULL, + `user_role` VARCHAR(10), + `created_time` DATETIME NOT NULL, + `updated_time` DATETIME NOT NULL, + UNIQUE (`member_id`, `team_id`), + CONSTRAINT `fk_team_user_member_id` FOREIGN KEY (`member_id`) REFERENCES `member` (`member_id`), + CONSTRAINT `fk_team_user_team_id` FOREIGN KEY (`team_id`) REFERENCES `team` (`team_id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4; \ No newline at end of file diff --git a/src/main/resources/db/migration/V202403051157__application.sql b/src/main/resources/db/migration/V202403051157__application.sql new file mode 100644 index 00000000..7b15f2bd --- /dev/null +++ b/src/main/resources/db/migration/V202403051157__application.sql @@ -0,0 +1,28 @@ + +# ํŒ€ ํŒ”๋กœ์ž‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•œ ํ…Œ์ด๋ธ” ๋ณ€๊ฒฝ ๋ฐ ์ปฌ๋Ÿผ ์ถ”๊ฐ€ +# ์ž‘์„ฑ์ž : Hoon9901 (shonn.dev@gmail.com) +# ์ž‘์„ฑ ๋‚ ์งœ : 2024-03-31 +# ํ˜„์žฌ ๋ฒ„์ „ : V202403051156 (์ด์ „ ๋ฒ„์ „ : V202403051155__application.sql) + +# ๋ณตํ•ฉํ‚ค๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ ์ „ FK ๊ฒธ PK๋กœ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ PK ์‚ญ์ œ ์ „, FK ์ œ๊ฑฐ +ALTER TABLE `follow` + DROP FOREIGN KEY `fk_follow_follow_id`, + DROP FOREIGN KEY `fk_follow_follower_id`; + +# ๋Œ€๋ฆฌํ‚ค ์ƒ์„ฑ ๋ฐ ๊ธฐ์กด ๋ณตํ•ฉํ‚ค(PK) ์ œ๊ฑฐ +ALTER TABLE `follow` + ADD `follow_id` BIGINT AUTO_INCREMENT PRIMARY KEY FIRST, + DROP PRIMARY KEY; + +# ๊ธฐ์กด ํŒ”๋กœ์ž‰ ์ปฌ๋Ÿผ ๋ช…์„ ํŒ”๋กœ์ž‰ ๋ฉค๋ฒ„๋กœ ๋ณ€๊ฒฝ / ์ƒˆ๋กœ์šด TEAM ํŒ”๋กœ์ž‰ ์ปฌ๋Ÿผ ์ƒ์„ฑ +ALTER TABLE `follow` + CHANGE `following_id` `following_member_id` BINARY(16) COMMENT 'member -> member follow', + ADD `following_team_id` BIGINT COMMENT 'member -> team follow' AFTER `following_member_id`; + +# ๊ธฐ์กด ๋ณ€๊ฒฝ๋œ ์ปฌ๋Ÿผ์œผ๋กœ FK ์ƒ์„ฑ ๋ฐ ํŒ€ ์ปฌ๋Ÿผ FK ์ƒ์„ฑ ๊ทธ๋ฆฌ๊ณ  +# ๋„๋ฉ”์ธ ์ œ์•ฝ ์กฐ๊ฑด ์ƒ์„ฑ +ALTER TABLE `follow` + ADD CONSTRAINT `fk_follow_follower_id` FOREIGN KEY (`follower_id`) REFERENCES `member` (`member_id`), + ADD CONSTRAINT `fk_follow_following_member_id` FOREIGN KEY (`following_member_id`) REFERENCES `member` (`member_id`), + ADD CONSTRAINT `fk_follow_following_team_id` FOREIGN KEY (`following_team_id`) REFERENCES `team` (`team_id`), + ADD CONSTRAINT UNIQUE (`follower_id`, `following_member_id`, `following_team_id`); From 8faf3581c11e9a243cf145e12a71e0215ae9bd81 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 1 Apr 2024 08:09:11 +0900 Subject: [PATCH 035/103] =?UTF-8?q?feat:=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=9E=91=EC=84=B1=20-=20team,=20?= =?UTF-8?q?teamUser=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/domain/team/entity/Team.java | 33 ++++++++++++++- .../codebase/domain/team/entity/TeamUser.java | 41 +++++++++++++++++-- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/team/entity/Team.java b/src/main/java/com/example/codebase/domain/team/entity/Team.java index fef60595..c1125554 100644 --- a/src/main/java/com/example/codebase/domain/team/entity/Team.java +++ b/src/main/java/com/example/codebase/domain/team/entity/Team.java @@ -1,20 +1,23 @@ package com.example.codebase.domain.team.entity; +import com.example.codebase.domain.team.dto.TeamRequest; import jakarta.persistence.*; import lombok.*; import org.hibernate.annotations.SoftDelete; import org.hibernate.annotations.SoftDeleteType; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import static lombok.AccessLevel.PROTECTED; @Entity @Table(name = "team") @Getter -@AllArgsConstructor @Builder @NoArgsConstructor(access = PROTECTED) +@AllArgsConstructor @SoftDelete(columnName = "is_deleted", strategy = SoftDeleteType.DELETED) public class Team { @@ -23,7 +26,7 @@ public class Team { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "description") + @Column(name = "description", columnDefinition = "TEXT") private String description; @Column(name = "address") @@ -46,4 +49,30 @@ public class Team { @Column(name = "updated_time") private LocalDateTime updatedTime = LocalDateTime.now(); + @Builder.Default + @OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true) + private List teamUsers = new ArrayList<>(); + + public static Team toEntity(TeamRequest.Create request) { + return Team.builder() + .name(request.getName()) + .address(request.getAddress()) + .profileImage(request.getProfileImage()) + .backgroundImage(request.getBackgroundImage()) + .description(request.getDescription()) + .build(); + } + + public void update(TeamRequest.Update request) { + this.name = request.getName(); + this.address = request.getAddress(); + this.profileImage = request.getProfileImage(); + this.backgroundImage = request.getBackgroundImage(); + this.description = request.getDescription(); + this.updatedTime = LocalDateTime.now(); + } + + public void addTeamUser(TeamUser teamUser) { + this.teamUsers.add(teamUser); + } } diff --git a/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java b/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java index ed7c49e2..aa1b4573 100644 --- a/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java +++ b/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java @@ -1,6 +1,7 @@ package com.example.codebase.domain.team.entity; import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.dto.TeamUserRequest; import jakarta.persistence.*; import lombok.*; @@ -9,7 +10,10 @@ import static lombok.AccessLevel.PROTECTED; @Entity -@Table(name = "team_user") +@Table(name = "team_user", + uniqueConstraints = { + @UniqueConstraint(columnNames = {"team_id", "member_id"}) + }) @Getter @NoArgsConstructor(access = PROTECTED) @AllArgsConstructor @@ -18,7 +22,7 @@ public class TeamUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "group_user_id") + @Column(name = "team_user_id") private Long id; @ManyToOne(fetch = FetchType.EAGER) @@ -29,7 +33,7 @@ public class TeamUser { @JoinColumn(name = "member_id") private Member member; - @Column(name = "position") + @Column(name = "position", nullable = false) private String position; @Enumerated(EnumType.STRING) @@ -43,4 +47,35 @@ public class TeamUser { @Builder.Default @Column(name = "updated_time") private LocalDateTime updatedTime = LocalDateTime.now(); + + public static TeamUser toEntity(String position, Member member, Team team, TeamUserRole role) { + return TeamUser.builder() + .member(member) + .team(team) + .role(role) + .position(position) + .build(); + } + + public void validOwner() { + if (role != TeamUserRole.OWNER) { + throw new IllegalArgumentException("ํŒ€์žฅ๋งŒ ๊ฐ€๋Šฅํ•œ ์ž‘์—…์ž…๋‹ˆ๋‹ค."); + } + } + + public boolean isOwner() { + return role == TeamUserRole.OWNER; + } + + public void transferOwner(TeamUser transferUser) { + this.role = TeamUserRole.MEMBER; + transferUser.role = TeamUserRole.OWNER; + } + + public void update(TeamUserRequest.Update request) { + this.position = request.getPosition(); + this.updatedTime = LocalDateTime.now(); + } + } + From 15145ab50bcb2926d2690e423b8c330e4954622b Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 1 Apr 2024 08:10:41 +0900 Subject: [PATCH 036/103] =?UTF-8?q?feat:=20Team=20=EA=B4=80=EB=A0=A8=20cru?= =?UTF-8?q?d=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/controller/TeamController.java | 88 ++++++++++++++----- .../codebase/domain/team/dto/TeamRequest.java | 71 +++++++++++++++ .../domain/team/dto/TeamResponse.java | 49 +++++++++++ .../team/repository/TeamRepository.java | 5 ++ .../domain/team/service/TeamService.java | 62 +++++++++++++ 5 files changed, 252 insertions(+), 23 deletions(-) create mode 100644 src/main/java/com/example/codebase/domain/team/dto/TeamRequest.java create mode 100644 src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java create mode 100644 src/main/java/com/example/codebase/domain/team/service/TeamService.java diff --git a/src/main/java/com/example/codebase/controller/TeamController.java b/src/main/java/com/example/codebase/controller/TeamController.java index 0dd91840..3afce66a 100644 --- a/src/main/java/com/example/codebase/controller/TeamController.java +++ b/src/main/java/com/example/codebase/controller/TeamController.java @@ -1,43 +1,85 @@ package com.example.codebase.controller; -import com.example.codebase.domain.team.entity.Team; -import com.example.codebase.domain.team.repository.TeamRepository; +import com.example.codebase.annotation.LoginOnly; +import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.member.service.MemberService; +import com.example.codebase.domain.team.dto.TeamRequest; +import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.service.TeamService; +import com.example.codebase.domain.team.service.TeamUserService; +import com.example.codebase.util.SecurityUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.time.LocalDateTime; - @RestController +@Tag(name = "ํŒ€ API", description = "ํŒ€๊ณผ ๊ด€๋ จ๋œ API") @RequestMapping("/api/teams") public class TeamController { + private final TeamService teamService; + + private final MemberService memberService; + + private final TeamUserService teamUserService; + @Autowired - private TeamRepository teamRepository; + public TeamController(TeamService teamService, MemberService memberService, TeamUserService teamUserService) { + this.teamService = teamService; + this.memberService = memberService; + this.teamUserService = teamUserService; + } @PostMapping - public ResponseEntity createTeam() { - Team saved = teamRepository.save(Team.builder() - .name("๊ทธ๋ฃน1") - .address("์ฃผ์†Œ") - .createdTime(LocalDateTime.now()) - .updatedTime(LocalDateTime.now()) - .description("์šฐ๋ฆฌ ๊ทธ๋ฃน์€ ์–ด์ฉŒ๊ตฌ") - .backgroundImage("http://cdn.artscope.kr/image") - .profileImage("http://cdn.artscope.kr/image") - .build()); - return new ResponseEntity(saved, HttpStatus.OK); + @Operation(summary = "ํŒ€ ์ƒ์„ฑ", description = "ํŒ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.") + @LoginOnly + public ResponseEntity createTeam(@RequestBody @Valid TeamRequest.Create request) { + String loginUsername = SecurityUtil.getCurrentUsernameValue(); + Member member = memberService.getEntity(loginUsername); + + TeamResponse.Get response = teamService.createTeam(request, member); + + return new ResponseEntity(response, HttpStatus.CREATED); + } + + @GetMapping("/{teamId}") + @Operation(summary = "ํŒ€ ์ •๋ณด ์กฐํšŒ", description = "ํŒ€์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity getTeam(@PathVariable Long teamId) { + TeamResponse.Get response = teamService.getTeam(teamId); + + return new ResponseEntity(response, HttpStatus.OK); } - @GetMapping - public ResponseEntity getAllTeam() { - return new ResponseEntity(teamRepository.findAll(), HttpStatus.OK); + @PutMapping("/{teamId}") + @Operation(summary = "ํŒ€ ์ •๋ณด ์ˆ˜์ •", description = "ํŒ€ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.") + @LoginOnly + public ResponseEntity updateTeam(@PathVariable Long teamId, @RequestBody @Valid TeamRequest.Update request) { + String loginUsername = SecurityUtil.getCurrentUsernameValue(); + TeamUser member = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); + member.validOwner(); + + TeamResponse.Get response = teamService.updateTeam(request, member); + + return new ResponseEntity(response, HttpStatus.OK); } - @DeleteMapping - public ResponseEntity deleteTeam(@RequestParam Long id) { - teamRepository.deleteById(id); - return new ResponseEntity("์‚ญ์ œ๋จ", HttpStatus.OK); + @DeleteMapping("/{teamId}") + @Operation(summary = "ํŒ€ ์‚ญ์ œ", description = "ํŒ€์„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.") + @LoginOnly + public ResponseEntity deleteTeam(@PathVariable Long teamId) { + String loginUsername = SecurityUtil.getCurrentUsernameValue(); + TeamUser member = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); + member.validOwner(); + + teamService.deleteTeam(member); + + return new ResponseEntity("ํŒ€์ด ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.NO_CONTENT); } + + } diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamRequest.java b/src/main/java/com/example/codebase/domain/team/dto/TeamRequest.java new file mode 100644 index 00000000..169f86b0 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamRequest.java @@ -0,0 +1,71 @@ +package com.example.codebase.domain.team.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.validator.constraints.URL; + +import static lombok.AccessLevel.PROTECTED; + +public class TeamRequest { + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor(access = PROTECTED) + @Schema(name = "TeamRequest.Create", description = "ํŒ€ ์ƒ์„ฑ DTO") + public static class Create { + + @NotEmpty(message = "ํŒ€ ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") + @Size(max = 50, message = "ํŒ€ ์ด๋ฆ„์€ 50์ž ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") + private String name; + + @Size(max = 255, message = "ํŒ€ ์ฃผ์†Œ๋Š” ์ตœ๋Œ€ 255์ž ๊นŒ์ง€ ์ž…๋ ฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.") + private String address; + + @NotEmpty(message = "ํŒ€ ํ”„๋กœํ•„ ์ด๋ฏธ์ง€๋Š” ํ•„์ˆ˜ ์ž…๋‹ˆ๋‹ค") + @URL(message = "ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ URL์ด ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.") + private String profileImage; + + @NotEmpty(message = "ํŒ€ ๋ฐฐ๊ฒฝํ™”๋ฉด ์ด๋ฏธ์ง€๋Š” ํ•„์ˆ˜ ์ž…๋‹ˆ๋‹ค") + @URL(message = "๋ฐฐ๊ฒฝํ™”๋ฉด ์ด๋ฏธ์ง€ URL์ด ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.") + private String backgroundImage; + + @NotEmpty(message = "ํŒ€ ์†Œ๊ฐœ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") + private String description; + + @NotEmpty(message = "๋ณธ์ธ์˜ ํฌ์ง€์…˜์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") + @Size(max = 100, message = "ํฌ์ง€์…˜์€ 100์ž ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") + private String position; + } + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor(access = PROTECTED) + @Schema(name = "TeamRequest.Update", description = "ํŒ€ ์ˆ˜์ • DTO") + public static class Update { + + @NotEmpty(message = "ํŒ€ ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") + @Size(max = 50, message = "ํŒ€ ์ด๋ฆ„์€ 50์ž ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") + private String name; + + @Size(max = 255, message = "ํŒ€ ์ฃผ์†Œ๋Š” ์ตœ๋Œ€ 255์ž ๊นŒ์ง€ ์ž…๋ ฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.") + private String address; + + @NotEmpty(message = "ํŒ€ ํ”„๋กœํ•„ ์ด๋ฏธ์ง€๋Š” ํ•„์ˆ˜ ์ž…๋‹ˆ๋‹ค") + @URL(message = "ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ URL์ด ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.") + private String profileImage; + + @NotEmpty(message = "ํŒ€ ๋ฐฐ๊ฒฝํ™”๋ฉด ์ด๋ฏธ์ง€๋Š” ํ•„์ˆ˜ ์ž…๋‹ˆ๋‹ค") + @URL(message = "๋ฐฐ๊ฒฝํ™”๋ฉด ์ด๋ฏธ์ง€ URL์ด ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.") + private String backgroundImage; + + @NotEmpty(message = "ํŒ€ ์†Œ๊ฐœ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") + private String description; + } +} diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java b/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java new file mode 100644 index 00000000..5dc15572 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java @@ -0,0 +1,49 @@ +package com.example.codebase.domain.team.dto; + +import com.example.codebase.domain.team.entity.Team; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +public class TeamResponse { + + @Getter + @Setter + @Schema(name = "TeamResponse.Get", description = "ํŒ€ ์กฐํšŒ DTO") + public static class Get{ + + private Long id; + + private String description; + + private String address; + + private String profileImage; + + private String backgroundImage; + + private String name; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime createdTime; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime updatedTime; + + public static TeamResponse.Get from(Team team) { + Get get = new Get(); + get.setId(team.getId()); + get.setDescription(team.getDescription()); + get.setAddress(team.getAddress()); + get.setProfileImage(team.getProfileImage()); + get.setBackgroundImage(team.getBackgroundImage()); + get.setName(team.getName()); + get.setCreatedTime(team.getCreatedTime()); + get.setUpdatedTime(team.getUpdatedTime()); + return get; + } + } +} diff --git a/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java b/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java index 849c7664..e09481a3 100644 --- a/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java +++ b/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java @@ -4,4 +4,9 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface TeamRepository extends JpaRepository { + + boolean existsByName(String name); + + boolean existsByNameAndIdNot(String name, Long id); + } diff --git a/src/main/java/com/example/codebase/domain/team/service/TeamService.java b/src/main/java/com/example/codebase/domain/team/service/TeamService.java new file mode 100644 index 00000000..5f8daccc --- /dev/null +++ b/src/main/java/com/example/codebase/domain/team/service/TeamService.java @@ -0,0 +1,62 @@ +package com.example.codebase.domain.team.service; + +import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.dto.TeamRequest; +import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.entity.TeamUserRole; +import com.example.codebase.domain.team.repository.TeamRepository; +import com.example.codebase.exception.NotFoundException; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Transactional +public class TeamService { + + private final TeamRepository teamRepository; + + @Autowired + public TeamService(TeamRepository teamRepository) { + this.teamRepository = teamRepository; + } + + public TeamResponse.Get createTeam(TeamRequest.Create request, Member member) { + if(teamRepository.existsByName(request.getName())) { + throw new RuntimeException("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํŒ€ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค."); + } + + Team team = Team.toEntity(request); + TeamUser teamUser = TeamUser.toEntity(request.getPosition(), member, team, TeamUserRole.OWNER); + + team.addTeamUser(teamUser); + + teamRepository.save(team); + return TeamResponse.Get.from(team); + } + + @Transactional(readOnly = true) + public TeamResponse.Get getTeam(Long teamId) { + Team team = teamRepository.findById(teamId).orElseThrow(() -> new NotFoundException("ํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + return TeamResponse.Get.from(team); + } + + public TeamResponse.Get updateTeam(TeamRequest.Update request, TeamUser teamUser) { + Team team = teamUser.getTeam(); + + if (teamRepository.existsByNameAndIdNot(request.getName(), team.getId())) { + throw new RuntimeException("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํŒ€ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค."); + } + + team.update(request); + teamRepository.save(team); + return TeamResponse.Get.from(team); + } + + public void deleteTeam(TeamUser teamUser) { + Team team = teamUser.getTeam(); + teamRepository.delete(team); + } +} From d3ab2e145391f85e3f6d5a5e80736af673f280b5 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 1 Apr 2024 08:11:17 +0900 Subject: [PATCH 037/103] =?UTF-8?q?feat:=20TeamUser=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20crud=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TeamUserController.java | 111 ++++++++++++++++++ .../domain/team/dto/TeamUserRequest.java | 43 +++++++ .../domain/team/dto/TeamUserResponse.java | 67 +++++++++++ .../domain/team/entity/TeamUserRole.java | 2 +- .../team/repository/TeamUserRepository.java | 14 +++ .../domain/team/service/TeamUserService.java | 88 ++++++++++++++ 6 files changed, 324 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/example/codebase/controller/TeamUserController.java create mode 100644 src/main/java/com/example/codebase/domain/team/dto/TeamUserRequest.java create mode 100644 src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java create mode 100644 src/main/java/com/example/codebase/domain/team/service/TeamUserService.java diff --git a/src/main/java/com/example/codebase/controller/TeamUserController.java b/src/main/java/com/example/codebase/controller/TeamUserController.java new file mode 100644 index 00000000..5bf6304b --- /dev/null +++ b/src/main/java/com/example/codebase/controller/TeamUserController.java @@ -0,0 +1,111 @@ +package com.example.codebase.controller; + +import com.example.codebase.annotation.LoginOnly; +import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.member.service.MemberService; +import com.example.codebase.domain.notification.service.NotificationSendService; +import com.example.codebase.domain.team.dto.TeamUserRequest; +import com.example.codebase.domain.team.dto.TeamUserResponse; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.service.TeamUserService; +import com.example.codebase.util.SecurityUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@Tag(name = "ํŒ€ ์œ ์ € API", description = "ํŒ€ ์œ ์ €์™€ ๊ถŒํ•œ์— ๊ด€๋ จ๋œ API ") +@RequestMapping("/api/team-users") +public class TeamUserController { + + private final TeamUserService teamUserService; + + private final MemberService memberService; + + @Autowired + public TeamUserController(TeamUserService teamUserService, MemberService memberService) { + this.teamUserService = teamUserService; + this.memberService = memberService; + } + + @GetMapping("{teamId}") + @Operation(summary = "ํŒ€ ์†Œ์† ์œ ์ € ์กฐํšŒ", description = "ํŒ€์— ์†ํ•œ ์œ ์ €๋ฅผ ๋ชจ๋‘ ์กฐํšŒ ํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity getTeamUsers(@PathVariable Long teamId) { + TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); + return new ResponseEntity(response, HttpStatus.OK); + } + + @PostMapping("{teamId}/invitations") + @LoginOnly + @Operation(summary = "ํŒ€ ์œ ์ € ์ถ”๊ฐ€", description = "ํŒ€์— ์œ ์ €๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity addTeamUser(@PathVariable Long teamId, @RequestBody @Valid TeamUserRequest.Create request) { + String loginUsername = SecurityUtil.getCurrentUsernameValue(); + TeamUser loginUser = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); + loginUser.validOwner(); + + Member inviteUser = memberService.getEntity(request.getUsername()); + + teamUserService.addTeamUser(loginUser, inviteUser, request); + TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); + + return new ResponseEntity(response, HttpStatus.OK); + } + + @DeleteMapping("{teamId}/{username}") + @LoginOnly + @Operation(summary = "ํŒ€ ์œ ์ € ์‚ญ์ œ(์ถ”๋ฐฉ)", description = "ํŒ€์— ์†ํ•œ ์œ ์ €๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity deleteTeamUser(@PathVariable Long teamId, @PathVariable String username) { + String loginUsername = SecurityUtil.getCurrentUsernameValue(); + TeamUser loginUser = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); + loginUser.validOwner(); + + TeamUser deleteUser = teamUserService.findByTeamIdAndUsername(teamId, username); + + teamUserService.deleteTeamUser(loginUser,deleteUser); + TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); + return new ResponseEntity(response, HttpStatus.NO_CONTENT); + } + + @PostMapping("{teamId}/{username}/transfer") + @LoginOnly + @Operation(summary = "ํŒ€ ๊ถŒํ•œ ์–‘๋„", description = "ํŒ€์˜ ๊ถŒํ•œ์„ ์–‘๋„ํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity transferToOwner(@PathVariable Long teamId, @PathVariable String username) { + String loginUsername = SecurityUtil.getCurrentUsernameValue(); + TeamUser loginUser = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); + loginUser.validOwner(); + + TeamUser transferUser = teamUserService.findByTeamIdAndUsername(teamId, username); + + teamUserService.transferToOwner(loginUser, transferUser); + TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); + return new ResponseEntity(response, HttpStatus.OK); + } + + @DeleteMapping("{teamId}/leave") + @LoginOnly + @Operation(summary = "ํŒ€ ํƒˆํ‡ด", description = "ํŒ€์—์„œ ํƒˆํ‡ดํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity leaveTeam(@PathVariable Long teamId) { + String loginUsername = SecurityUtil.getCurrentUsernameValue(); + TeamUser member = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); + teamUserService.leaveTeamUser(member); + + return new ResponseEntity("ํŒ€์—์„œ ํƒˆํ‡ดํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.NO_CONTENT); + } + + @PutMapping ("{teamId}") + @LoginOnly + @Operation(summary = "ํŒ€ ์œ ์ € ์ง์ฑ… ๋ณ€๊ฒฝ", description = "ํŒ€ ์œ ์ €์˜ ์ง์ฑ…์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity updateTeamUser(@PathVariable Long teamId, @RequestBody @Valid TeamUserRequest.Update request) { + String loginUsername = SecurityUtil.getCurrentUsernameValue(); + TeamUser member = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); + + teamUserService.updateTeamUser(member, request); + + return new ResponseEntity("์ง์ฑ…์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.OK); + } + +} diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamUserRequest.java b/src/main/java/com/example/codebase/domain/team/dto/TeamUserRequest.java new file mode 100644 index 00000000..d8163ef9 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamUserRequest.java @@ -0,0 +1,43 @@ +package com.example.codebase.domain.team.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import static lombok.AccessLevel.PROTECTED; + +public class TeamUserRequest { + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor(access = PROTECTED) + @Schema(name = "TeamUserRequest.Create", description = "ํŒ€ ์œ ์ € ์ถ”๊ฐ€ DTO") + public static class Create { + + @NotEmpty(message = "์œ ์ € ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") + private String username; + + @NotEmpty(message = "ํฌ์ง€์…˜์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") + @Size(max = 100 , message = "ํฌ์ง€์…˜์€ 100์ž ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") + private String position; + } + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor(access = PROTECTED) + @Schema(name = "TeamUserRequest.Update", description = "ํŒ€ ์œ ์ € ์ˆ˜์ • DTO") + public static class Update { + + private String username; + + @NotEmpty(message = "ํฌ์ง€์…˜์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") + @Size(max = 100 , message = "ํฌ์ง€์…˜์€ 100์ž ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") + private String position; + } +} diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java b/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java new file mode 100644 index 00000000..c1ff646d --- /dev/null +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java @@ -0,0 +1,67 @@ +package com.example.codebase.domain.team.dto; + +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.entity.TeamUserRole; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.List; + +import static lombok.AccessLevel.PROTECTED; + +public class TeamUserResponse { + + @Getter + @Setter + @NoArgsConstructor(access = PROTECTED) + @Schema(name = "TeamUserResponse.Get", description = "ํŒ€ ์œ ์ € ์กฐํšŒ DTO") + public static class Get{ + + private String username; + + private String profileImage; + + private String position; + + private TeamUserRole role; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createdTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime updatedTime; + + + public static TeamUserResponse.Get from(TeamUser teamUser) { + TeamUserResponse.Get get = new TeamUserResponse.Get(); + get.setUsername(teamUser.getMember().getUsername()); + get.setProfileImage(teamUser.getMember().getPicture()); + get.setPosition(teamUser.getPosition()); + get.setRole(teamUser.getRole()); + get.setCreatedTime(teamUser.getCreatedTime()); + get.setUpdatedTime(teamUser.getUpdatedTime()); + return get; + } + } + + @Getter + @Setter + @Schema(name = "TeamUserResponse.GetAll", description = "ํŒ€ ์œ ์ € ์ „์ฒด ์กฐํšŒ DTO") + public static class GetAll{ + + private List teamUsers; + + public static TeamUserResponse.GetAll from(List teamUsers) { + GetAll getAll = new GetAll(); + getAll.teamUsers = teamUsers.stream() + .map(TeamUserResponse.Get::from) + .toList(); + return getAll; + } + } +} diff --git a/src/main/java/com/example/codebase/domain/team/entity/TeamUserRole.java b/src/main/java/com/example/codebase/domain/team/entity/TeamUserRole.java index 1f9795df..5be4db17 100644 --- a/src/main/java/com/example/codebase/domain/team/entity/TeamUserRole.java +++ b/src/main/java/com/example/codebase/domain/team/entity/TeamUserRole.java @@ -2,5 +2,5 @@ public enum TeamUserRole { OWNER, // ์†Œ์œ ์ž - MEMBER, // ๋ฉค๋ฒ„ + MEMBER // ๋ฉค๋ฒ„ } diff --git a/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java index 521f5ace..06495456 100644 --- a/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java +++ b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java @@ -1,7 +1,21 @@ package com.example.codebase.domain.team.repository; +import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.entity.Team; import com.example.codebase.domain.team.entity.TeamUser; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; +import java.util.Optional; public interface TeamUserRepository extends JpaRepository { + + @Query("SELECT tu FROM TeamUser tu LEFT JOIN Member m ON tu.member.username = m.username WHERE tu.team.id = :teamId AND m.username = :username") + Optional findByTeamIdAndUsername(Long teamId, String username); + + @Query("SELECT tu FROM TeamUser tu WHERE tu.team = :team ORDER BY CASE WHEN tu.role = 'OWNER' THEN 0 ELSE 1 END, tu.createdTime ASC") + List findAllByTeamOrderByRole(Team team); + + boolean existsByTeamAndMember(Team team, Member member); } diff --git a/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java new file mode 100644 index 00000000..39b04aaf --- /dev/null +++ b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java @@ -0,0 +1,88 @@ +package com.example.codebase.domain.team.service; + +import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.dto.TeamUserRequest; +import com.example.codebase.domain.team.dto.TeamUserResponse; +import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.entity.TeamUserRole; +import com.example.codebase.domain.team.repository.TeamRepository; +import com.example.codebase.domain.team.repository.TeamUserRepository; +import com.example.codebase.exception.NotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Objects; + +@Service +@Transactional +public class TeamUserService { + + private final TeamUserRepository teamUserRepository; + + private final TeamRepository teamRepository; + + @Autowired + public TeamUserService(TeamUserRepository teamUserRepository, TeamRepository teamRepository) { + this.teamUserRepository = teamUserRepository; + this.teamRepository = teamRepository; + } + + @Transactional(readOnly = true) + public TeamUser findByTeamIdAndUsername(Long teamId, String username) { + if (!teamRepository.existsById(teamId)) { + throw new NotFoundException("ํ•ด๋‹น ํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."); + } + + return teamUserRepository.findByTeamIdAndUsername(teamId, username) + .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ํŒ€์— ์†ํ•ด์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + } + + @Transactional(readOnly = true) + public TeamUserResponse.GetAll getTeamUsers(Long teamId) { + Team team = teamRepository.findById(teamId) + .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + + return TeamUserResponse.GetAll.from(teamUserRepository.findAllByTeamOrderByRole(team)); + } + + public void addTeamUser(TeamUser member, Member inviteUser, TeamUserRequest.Create request) { + Team team = member.getTeam(); + if (teamUserRepository.existsByTeamAndMember(team, inviteUser)) { + throw new RuntimeException("์ด๋ฏธ ํŒ€์— ์†ํ•œ ๋ฉค๋ฒ„์ž…๋‹ˆ๋‹ค."); + } + + teamUserRepository.save(TeamUser.toEntity(request.getPosition(), inviteUser, team, TeamUserRole.MEMBER)); + } + + public void deleteTeamUser(TeamUser loginUser, TeamUser deleteUser) { + if(loginUser.isOwner() && deleteUser.isOwner()) { + throw new RuntimeException("ํŒ€์žฅ์€ ์ž์‹ ์„ ์ถ”๋ฐฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + teamUserRepository.delete(deleteUser); + } + + public void leaveTeamUser(TeamUser member) { + if (member.isOwner()) throw new RuntimeException("ํŒ€์žฅ์€ ํŒ€์„ ๋‚˜๊ฐˆ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + + teamUserRepository.delete(member); + } + + public void transferToOwner(TeamUser loginUser, TeamUser transferUser) { + loginUser.transferOwner(transferUser); + + teamUserRepository.save(loginUser); + teamUserRepository.save(transferUser); + } + + public void updateTeamUser(TeamUser member, TeamUserRequest.Update request) { + if (!Objects.equals(member.getMember().getUsername(), request.getUsername()) && !member.isOwner()) { + throw new RuntimeException("๋ณธ์ธ ๋˜๋Š” ํŒ€์žฅ๋งŒ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."); + } + + member.update(request); + teamUserRepository.save(member); + } + +} From 0cd7ea3fa7434ed667db064bcfe87bd7a09a6c95 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 1 Apr 2024 08:12:08 +0900 Subject: [PATCH 038/103] =?UTF-8?q?feat:=20member=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=EC=8B=9C=20=EB=B3=B8=EC=9D=B8=EC=9D=B4=20=ED=8C=80=EC=9E=A5?= =?UTF-8?q?=EC=9D=B8=20=ED=8C=80=EC=9D=B4=20=EC=9E=88=EC=9C=BC=EB=A9=B4=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=EB=A5=BC=20=EB=B0=98=ED=99=98=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/domain/member/entity/Member.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/com/example/codebase/domain/member/entity/Member.java b/src/main/java/com/example/codebase/domain/member/entity/Member.java index 3174194f..a0c28903 100644 --- a/src/main/java/com/example/codebase/domain/member/entity/Member.java +++ b/src/main/java/com/example/codebase/domain/member/entity/Member.java @@ -12,6 +12,7 @@ import com.example.codebase.domain.notification.entity.NotificationReceivedStatus; import com.example.codebase.domain.notification.entity.NotificationSetting; import com.example.codebase.domain.post.entity.Post; +import com.example.codebase.domain.team.entity.TeamUser; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -130,6 +131,10 @@ public class Member { @OneToMany(mappedBy = "following", cascade = CascadeType.ALL) private List followers = new ArrayList<>(); + @Builder.Default + @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE, orphanRemoval = true) + private List teamUserRoles = new ArrayList<>(); + public static User toUser(Member member) { return new User(member.getUsername(), member.getPassword(), member.getAuthorities().stream() .map(authority -> new SimpleGrantedAuthority(authority.getAuthority().getAuthorityName())) @@ -326,4 +331,14 @@ public void updateEmailReceive(boolean emailReceive) { public void setNotificationSetting(NotificationSetting notificationSetting) { this.notificationSettings = notificationSetting; } + + @PreRemove + private void preRemove() { + for(TeamUser teamUser : teamUserRoles) { + if(teamUser.isOwner()) { + throw new RuntimeException("ํŒ€์žฅ์ธ ํŒ€์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ํŒ€์žฅ์„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ํŒ€์„ ์‚ญ์ œํ•œ ํ›„์— ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."); + } + } + } + } From 7751e0c168f9fc8ff3e8f06a4c35363e02535051 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 1 Apr 2024 08:13:14 +0900 Subject: [PATCH 039/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20-=20team,=20teamUser?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TeamControllerTest.java | 262 +++++++++++++ .../controller/TeamUserControllerTest.java | 369 ++++++++++++++++++ .../team/service/TeamUserServiceTest.java | 92 +++++ 3 files changed, 723 insertions(+) create mode 100644 src/test/java/com/example/codebase/controller/TeamControllerTest.java create mode 100644 src/test/java/com/example/codebase/controller/TeamUserControllerTest.java create mode 100644 src/test/java/com/example/codebase/domain/team/service/TeamUserServiceTest.java diff --git a/src/test/java/com/example/codebase/controller/TeamControllerTest.java b/src/test/java/com/example/codebase/controller/TeamControllerTest.java new file mode 100644 index 00000000..1f3a88f4 --- /dev/null +++ b/src/test/java/com/example/codebase/controller/TeamControllerTest.java @@ -0,0 +1,262 @@ +package com.example.codebase.controller; + +import com.example.codebase.domain.auth.WithMockCustomUser; +import com.example.codebase.domain.member.dto.CreateMemberDTO; +import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.member.service.MemberService; +import com.example.codebase.domain.team.dto.TeamRequest; +import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.service.TeamService; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.extern.slf4j.Slf4j; +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.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) +@AutoConfigureMockMvc +@ActiveProfiles("test") +@Transactional +@Slf4j +class TeamControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext context; + + @Autowired + private MemberService memberService; + + @Autowired + private TeamService teamService; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + public void setUp() { + mockMvc = MockMvcBuilders + .webAppContextSetup(context) + .apply(springSecurity()) + .build(); + objectMapper.registerModule(new JavaTimeModule()); + } + + public Member createMember(String username) { + CreateMemberDTO createMemberDTO = new CreateMemberDTO(); + createMemberDTO.setUsername(username); + createMemberDTO.setPassword("password"); + createMemberDTO.setName("name"); + createMemberDTO.setEmail("email" + "@" + username + ".com"); + createMemberDTO.setAllowEmailReceive(true); + + memberService.createMember(createMemberDTO); + return memberService.getEntity(username); + } + + public TeamRequest.Create createTeamRequest(String name) { + TeamRequest.Create request = new TeamRequest.Create( + name, + "ํŒ€ ์ฃผ์†Œ", + "http://test.com/profile.jpg", + "http://test.com/background.jpg", + "ํŒ€์†Œ๊ฐœ", + "์ž์‹ ์˜ ํฌ์ง€์…˜, ์ง๊ธ‰" + ); + return request; + } + + public TeamResponse.Get createTeam(Member member, String name) { + return teamService.createTeam(createTeamRequest(name), member); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ์ƒ์„ฑ ํ…Œ์ŠคํŠธ") + @Test + void ํŒ€_์ƒ์„ฑ() throws Exception { + // given + Member member = createMember("testid"); + TeamRequest.Create request = createTeamRequest("ํŒ€์ƒ์„ฑ"); + + // when + String response = mockMvc.perform( + post("/api/teams") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ) + .andDo(print()) + .andExpect(status().isCreated()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + TeamResponse.Get teamResponse = objectMapper.readValue(response, TeamResponse.Get.class); + assertEquals(request.getName(), teamResponse.getName()); + assertEquals(request.getAddress(), teamResponse.getAddress()); + assertEquals(request.getProfileImage(), teamResponse.getProfileImage()); + assertEquals(request.getBackgroundImage(), teamResponse.getBackgroundImage()); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํŒ€ ์ด๋ฆ„์ด ์žˆ์„ ๋•Œ ์‹คํŒจ") + @Test + void ์ด๋ฏธ_์กด์žฌํ•˜๋Š”_ํŒ€_์ด๋ฆ„์ด_์žˆ์„๋–„_์‹คํŒจ() throws Exception { + // given + Member member = createMember("testid"); + createTeam(member, "๋™์ผํ•œ์ด๋ฆ„"); + TeamRequest.Create request = createTeamRequest("๋™์ผํ•œ์ด๋ฆ„"); + + // when + mockMvc.perform( + post("/api/teams") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํŒ€ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + @DisplayName("ํŒ€ ์ •๋ณด ์ƒ์„ธ ์กฐํšŒ") + @Test + void ํŒ€_์ •๋ณด_์ƒ์„ธ_์กฐํšŒ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ƒ์„ฑ"); + + String response = mockMvc.perform( + get("/api/teams/" + team.getId()) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + TeamResponse.Get teamResponse = objectMapper.readValue(response, TeamResponse.Get.class); + assertEquals(team.getName(), teamResponse.getName()); + assertEquals(team.getAddress(), teamResponse.getAddress()); + assertEquals(team.getProfileImage(), teamResponse.getProfileImage()); + assertEquals(team.getBackgroundImage(), teamResponse.getBackgroundImage()); + } + + @DisplayName("ํŒ€ ์ •๋ณด ์ˆ˜์ •") + @WithMockCustomUser(username = "testid", role = "USER") + @Test + void ํŒ€_์ •๋ณด_์ˆ˜์ •() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ˆ˜์ •"); + + TeamRequest.Update request = new TeamRequest.Update( + "์ˆ˜์ •๋œํŒ€์ด๋ฆ„", + "์ˆ˜์ •๋œํŒ€์ฃผ์†Œ", + "http://test.com/profile.jpg", + "http://test.com/background.jpg", + "์ˆ˜์ •๋œํŒ€์†Œ๊ฐœ" + ); + + // when + String response = mockMvc.perform( + put("/api/teams/" + team.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + TeamResponse.Get teamResponse = objectMapper.readValue(response, TeamResponse.Get.class); + assertEquals(request.getName(), teamResponse.getName()); + assertEquals(request.getAddress(), teamResponse.getAddress()); + assertEquals(request.getProfileImage(), teamResponse.getProfileImage()); + assertEquals(request.getBackgroundImage(), teamResponse.getBackgroundImage()); + assertEquals(request.getDescription(), teamResponse.getDescription()); + } + + @DisplayName("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํŒ€ ์ด๋ฆ„์ด ์žˆ์„ ๋•Œ ํŒ€ ์ •๋ณด ์ˆ˜์ • ์‹คํŒจ") + @WithMockCustomUser(username = "testid", role = "USER") + @Test + void ์ด๋ฏธ_์กด์žฌํ•˜๋Š”_ํŒ€_์ด๋ฆ„์ด_์žˆ์„๋•Œ_ํŒ€_์ •๋ณด_์ˆ˜์ •_์‹คํŒจ() throws Exception { + // given + Member member = createMember("testid"); + createTeam(member, "์ค‘๋ณต๋œํŒ€์ด๋ฆ„"); + TeamResponse.Get team = createTeam(member, "ํ…Œ์ŠคํŠธ์ด๋ฆ„"); + TeamRequest.Update request = new TeamRequest.Update( + "์ค‘๋ณต๋œํŒ€์ด๋ฆ„", + "ํŒ€ ์ฃผ์†Œ", + "http://test.com/profile.jpg", + "http://test.com/background.jpg", + "ํŒ€์†Œ๊ฐœ" + ); + + // when + mockMvc.perform( + put("/api/teams/" + team.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํŒ€ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + @DisplayName("๊ธฐ์กด์˜ ํŒ€๊ณผ ๋™์ผํ•œ ์ด๋ฆ„์œผ๋กœ ํŒ€ ์ •๋ณด ์ˆ˜์ •์‹œ") + @WithMockCustomUser(username = "testid", role = "USER") + @Test + void ๊ธฐ์กด์˜_ํŒ€๊ณผ_๋™์ผํ•œ_์ด๋ฆ„์œผ๋กœ_ํŒ€_์ •๋ณด_์ˆ˜์ •์‹œ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamRequest.Update request = new TeamRequest.Update( + "ํŒ€์ด๋ฆ„", + "ํŒ€ ์ฃผ์†Œ", + "http://test.com/profile.jpg", + "http://test.com/background.jpg", + "ํŒ€์†Œ๊ฐœ" + ); + + // when + mockMvc.perform( + put("/api/teams/" + team.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isOk()); + } + + @DisplayName("ํŒ€ ์‚ญ์ œ") + @WithMockCustomUser(username = "testid", role = "USER") + @Test + void ํŒ€_์‚ญ์ œ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + + // when + mockMvc.perform( + delete("/api/teams/" + team.getId()) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isNoContent()); + } +} diff --git a/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java b/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java new file mode 100644 index 00000000..9582043f --- /dev/null +++ b/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java @@ -0,0 +1,369 @@ +package com.example.codebase.controller; + +import com.example.codebase.domain.auth.WithMockCustomUser; +import com.example.codebase.domain.member.dto.CreateMemberDTO; +import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.member.service.MemberService; +import com.example.codebase.domain.team.dto.TeamRequest; +import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.dto.TeamUserRequest; +import com.example.codebase.domain.team.dto.TeamUserResponse; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.service.TeamService; +import com.example.codebase.domain.team.service.TeamUserService; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.transaction.Transactional; +import lombok.extern.slf4j.Slf4j; +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.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) +@AutoConfigureMockMvc +@ActiveProfiles("test") +@Transactional +@Slf4j +class TeamUserControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext context; + + @Autowired + private MemberService memberService; + + @Autowired + private TeamService teamService; + + @Autowired + private TeamUserService teamUserService; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + public void setUp() { + mockMvc = MockMvcBuilders + .webAppContextSetup(context) + .apply(springSecurity()) + .build(); + objectMapper.registerModule(new JavaTimeModule()); + } + + public Member createMember(String username) { + CreateMemberDTO createMemberDTO = new CreateMemberDTO(); + createMemberDTO.setUsername(username); + createMemberDTO.setPassword("password"); + createMemberDTO.setName("name"); + createMemberDTO.setEmail("email" + "@" + username + ".com"); + createMemberDTO.setAllowEmailReceive(true); + + memberService.createMember(createMemberDTO); + return memberService.getEntity(username); + } + + public TeamRequest.Create createTeamRequest(String name) { + return new TeamRequest.Create( + name, + "ํŒ€ ์ฃผ์†Œ", + "http://test.com/profile.jpg", + "http://test.com/background.jpg", + "ํŒ€์†Œ๊ฐœ", + "์ž์‹ ์˜ ํฌ์ง€์…˜, ์ง๊ธ‰" + ); + } + + public TeamResponse.Get createTeam(Member member, String name) { + return teamService.createTeam(createTeamRequest(name), member); + } + + public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { + TeamUserRequest.Create request = new TeamUserRequest.Create( + inviteMember.getUsername(), + "ํŒ€์›" + ); + teamUserService.addTeamUser(loginUser, inviteMember, request); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ์†Œ์† ์œ ์ € ์กฐํšŒ") + @Test + void ํŒ€_์†Œ์†_์œ ์ €_์กฐํšŒ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + + // when + String response = mockMvc.perform(get("/api/team-users/" + team.getId()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); + assertEquals(1, teamUserResponse.getTeamUsers().size()); + assertEquals("testid", teamUserResponse.getTeamUsers().get(0).getUsername()); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ์†Œ์† ์œ ์ € ์ถ”๊ฐ€") + @Test + void ํŒ€_์†Œ์†_์œ ์ €_์ถ”๊ฐ€() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + Member inviteMember = createMember("์ถ”๊ฐ€ํ• ์‚ฌ๋žŒ"); + + TeamUserRequest.Create request = new TeamUserRequest.Create( + inviteMember.getUsername(), + "ํŒ€์›" + ); + + // when + String response = mockMvc.perform( + post("/api/team-users/" + team.getId() + "/invitations") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); + assertEquals(2, teamUserResponse.getTeamUsers().size()); + assertEquals("testid", teamUserResponse.getTeamUsers().get(0).getUsername()); + assertEquals("์ถ”๊ฐ€ํ• ์‚ฌ๋žŒ", teamUserResponse.getTeamUsers().get(1).getUsername()); + assertEquals("ํŒ€์›", teamUserResponse.getTeamUsers().get(1).getPosition()); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ์†Œ์† ์œ ์ € ์‚ญ์ œ(์ถ”๋ฐฉ)") + @Test + void ํŒ€_์†Œ์†_์œ ์ €_์‚ญ์ œ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + Member inviteMember = createMember("removedUser"); + createAndInviteMember(teamOwner, inviteMember); + + // when + String response = mockMvc.perform( + delete("/api/team-users/" + team.getId() + "/" + inviteMember.getUsername()) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isNoContent()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); + assertEquals(1, teamUserResponse.getTeamUsers().size()); + assertEquals("testid", teamUserResponse.getTeamUsers().get(0).getUsername()); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€์žฅ์ธ ๊ฒฝ์šฐ ์ž์‹ ์„ ํŒ€์—์„œ ์ถ”๋ฐฉ์‹œ ์‹คํŒจ") + @Test + void ํŒ€์žฅ์ธ_๊ฒฝ์šฐ_์ž์‹ ์„_ํŒ€์—์„œ_์ถ”๋ฐฉ_์‹คํŒจ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + + // when + String response = mockMvc.perform( + delete("/api/team-users/" + team.getId() + "/" + member.getUsername()) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + assertTrue(response.contains("ํŒ€์žฅ์€ ์ž์‹ ์„ ์ถ”๋ฐฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); // ๊ธฐ์–ตํ• ๊ฒƒ contains + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€์žฅ ๊ถŒํ•œ ์–‘๋„") + @Test + void ํŒ€์žฅ_๊ถŒํ•œ_์–‘๋„() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + Member inviteMember = createMember("transferUser"); + createAndInviteMember(teamOwner, inviteMember); + + // when + String response = mockMvc.perform( + post("/api/team-users/" + team.getId() + "/" + inviteMember.getUsername() + "/transfer") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); + assertEquals(2, teamUserResponse.getTeamUsers().size()); + assertEquals("transferUser", teamUserResponse.getTeamUsers().get(0).getUsername()); + assertEquals("OWNER", teamUserResponse.getTeamUsers().get(0).getRole().name()); + assertEquals("testid", teamUserResponse.getTeamUsers().get(1).getUsername()); + assertEquals("MEMBER", teamUserResponse.getTeamUsers().get(1).getRole().name()); + } + + @WithMockCustomUser(username = "ํƒˆํ‡ดํ• ์‚ฌ๋žŒ", role = "USER") + @DisplayName("ํŒ€ ํƒˆํ‡ด ์„ฑ๊ณต") + @Test + void ํŒ€_ํƒˆํ‡ด_์„ฑ๊ณต() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + Member inviteMember = createMember("ํƒˆํ‡ดํ• ์‚ฌ๋žŒ"); + createAndInviteMember(teamOwner, inviteMember); + + // when + String response = mockMvc.perform( + delete("/api/team-users/" + team.getId() + "/" + "leave") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isNoContent()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + assertTrue(response.contains("ํŒ€์—์„œ ํƒˆํ‡ดํ–ˆ์Šต๋‹ˆ๋‹ค.")); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€์žฅ์ด ํŒ€ ํƒˆํ‡ด ์‹œ ์‹คํŒจ") + @Test + void ํŒ€์žฅ์ด_ํŒ€ํƒˆํ‡ด_์‹œ_์‹คํŒจ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + + // when + String response = mockMvc.perform( + delete("/api/team-users/" + team.getId() + "/" + "leave") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + assertTrue(response.contains("ํŒ€์žฅ์€ ํŒ€์„ ๋‚˜๊ฐˆ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€์žฅ์ด ํŒ€ ์œ ์ € ์ง์ฑ… ๋ณ€๊ฒฝ") + @Test + void ํŒ€์žฅ์ด_ํŒ€_์œ ์ €_์ง์ฑ…_๋ณ€๊ฒฝ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + Member inviteMember = createMember("changePositionUser"); + createAndInviteMember(teamOwner, inviteMember); // ํŒ€์› + + TeamUserRequest.Update request = new TeamUserRequest.Update( + inviteMember.getUsername(), + "๋ณ€๊ฒฝ๋œํฌ์ง€์…˜" + ); + + // when + String response = mockMvc.perform( + put("/api/team-users/" + team.getId() ) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + assertTrue(response.contains("์ง์ฑ…์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")); + } + + @WithMockCustomUser(username = "ํ”„๋กœํ•„๋ณ€๊ฒฝํ• ์‚ฌ๋žŒ", role = "USER") + @DisplayName("ํŒ€์›์ด ๋ณธ์ธ ์ง์ฑ… ๋ณ€๊ฒฝ") + @Test + void ํŒ€์›์ด_๋ณธ์ธ_์ง์ฑ…_๋ณ€๊ฒฝ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + Member inviteMember = createMember("ํ”„๋กœํ•„๋ณ€๊ฒฝํ• ์‚ฌ๋žŒ"); + createAndInviteMember(teamOwner, inviteMember); // ํŒ€์› + + TeamUserRequest.Update request = new TeamUserRequest.Update( + inviteMember.getUsername(), + "๋ณ€๊ฒฝ๋œํฌ์ง€์…˜" + ); + + // when + String response = mockMvc.perform( + put("/api/team-users/" + team.getId() ) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + assertTrue(response.contains("์ง์ฑ…์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")); + } + + @WithMockCustomUser(username = "๋‹ค๋ฅธ์œ ์ €", role = "USER") + @DisplayName("๋‹ค๋ฅธ ์œ ์ €๊ฐ€ ํŒ€์› ์ง์ฑ… ๋ณ€๊ฒฝ์‹œ๋„์‹œ ์‹คํŒจ") + @Test + void ๋‹ค๋ฅธ_์œ ์ €๊ฐ€_ํŒ€์›_์ง์ฑ…_๋ณ€๊ฒฝ์‹œ๋„์‹œ_์‹คํŒจ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + Member teamMember1 = createMember("ํŒ€์›1"); + createAndInviteMember(teamOwner, teamMember1); // ํŒ€์› + + Member teamMember2 = createMember("๋‹ค๋ฅธ์œ ์ €"); + createAndInviteMember(teamOwner, teamMember2); // ํŒ€์› + + TeamUserRequest.Update request = new TeamUserRequest.Update( + teamMember1.getUsername(), + "๋ณ€๊ฒฝ๋œํฌ์ง€์…˜" + ); + + // when + String response = mockMvc.perform( + put("/api/team-users/" + team.getId() ) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + assertTrue(response.contains("๋ณธ์ธ ๋˜๋Š” ํŒ€์žฅ๋งŒ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")); + } +} diff --git a/src/test/java/com/example/codebase/domain/team/service/TeamUserServiceTest.java b/src/test/java/com/example/codebase/domain/team/service/TeamUserServiceTest.java new file mode 100644 index 00000000..379e58ab --- /dev/null +++ b/src/test/java/com/example/codebase/domain/team/service/TeamUserServiceTest.java @@ -0,0 +1,92 @@ +package com.example.codebase.domain.team.service; + +import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.repository.TeamRepository; +import com.example.codebase.domain.team.repository.TeamUserRepository; +import com.example.codebase.exception.NotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.context.ActiveProfiles; + +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +@ActiveProfiles("test") +public class TeamUserServiceTest { + + @InjectMocks + private TeamUserService teamUserService; + + @Mock + private TeamRepository teamRepository; + + @Mock + private TeamUserRepository teamUserRepository; + + @Nested + @DisplayName("findByTeamIdAndUsername ๋ฉ”์†Œ๋“œ ๊ฒ€์ฆ") + class findByTeamIdAndUsername{ + + @Test + @DisplayName("ํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ NotFoundException ๋ฐœ์ƒ") + void ํŒ€์ด_์กด์žฌํ•˜์ง€_์•Š์„๋•Œ() { + //given + Long testTeamId = 1L; + String testUsername = "testUsername"; + + given(teamRepository.existsById(testTeamId)).willReturn(false); // ํ•ด๋‹น ์š”์ฒญ์‹œ false ๋ฐ˜ํ™˜ + + // when + assertThatThrownBy(() -> teamUserService.findByTeamIdAndUsername(testTeamId, testUsername)) + .isInstanceOf(NotFoundException.class) + .hasMessage("ํ•ด๋‹น ํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."); + + } + + @Test + @DisplayName("ํŒ€์— ์†ํ•ด์žˆ์ง€ ์•Š์„ ๋•Œ NotFoundException ๋ฐœ์ƒ") + void ํŒ€์—_์†ํ•ด์žˆ์ง€_์•Š์„๋•Œ() { + //given + Long testTeamId = 1L; + String testUsername = "testUsername"; + + given(teamRepository.existsById(testTeamId)).willReturn(true); + given(teamUserRepository.findByTeamIdAndUsername(testTeamId, testUsername)).willReturn(Optional.empty()); + + // when + assertThatThrownBy(() -> teamUserService.findByTeamIdAndUsername(testTeamId, testUsername)) + .isInstanceOf(NotFoundException.class) + .hasMessage("ํ•ด๋‹น ํŒ€์— ์†ํ•ด์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค."); + } + + @Test + @DisplayName("ํŒ€์— ์†ํ•ด์žˆ์„ ๋•Œ") + void ํŒ€์—_์†ํ•ด์žˆ์„๋•Œ() { + //given + Long testTeamId = 1L; + String testUsername = "testUsername"; + Team team = Team.builder().id(testTeamId).build(); + TeamUser teamUser = TeamUser.builder().team(team).build(); + + given(teamRepository.existsById(testTeamId)).willReturn(true); + given(teamUserRepository.findByTeamIdAndUsername(testTeamId, testUsername)).willReturn(Optional.ofNullable(teamUser)); + + // when + teamUserService.findByTeamIdAndUsername( testTeamId, testUsername); + + // then + verify(teamRepository).existsById(teamUser.getTeam().getId()); // ํ•ด๋‹น ์š”์ฒญ์ด ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ + verify(teamUserRepository).findByTeamIdAndUsername(testTeamId, testUsername); + } + } +} From 46698ca5db1924c6ededd2348ba89485341c89e6 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 1 Apr 2024 16:58:50 +0900 Subject: [PATCH 040/103] =?UTF-8?q?test:=20testUser=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=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 --- .../controller/TeamUserControllerTest.java | 88 +++++++++++++------ 1 file changed, 60 insertions(+), 28 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java b/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java index 9582043f..ed29c6b6 100644 --- a/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java +++ b/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java @@ -13,7 +13,6 @@ import com.example.codebase.domain.team.service.TeamUserService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import io.swagger.v3.oas.annotations.Operation; import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; @@ -230,7 +229,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertEquals("MEMBER", teamUserResponse.getTeamUsers().get(1).getRole().name()); } - @WithMockCustomUser(username = "ํƒˆํ‡ดํ• ์‚ฌ๋žŒ", role = "USER") + @WithMockCustomUser(username = "leaveUser", role = "USER") @DisplayName("ํŒ€ ํƒˆํ‡ด ์„ฑ๊ณต") @Test void ํŒ€_ํƒˆํ‡ด_์„ฑ๊ณต() throws Exception { @@ -239,39 +238,20 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); - Member inviteMember = createMember("ํƒˆํ‡ดํ• ์‚ฌ๋žŒ"); + Member inviteMember = createMember("leaveUser"); createAndInviteMember(teamOwner, inviteMember); // when String response = mockMvc.perform( - delete("/api/team-users/" + team.getId() + "/" + "leave") + delete("/api/team-users/" + team.getId() + "/" + inviteMember.getUsername()) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isNoContent()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); // then - assertTrue(response.contains("ํŒ€์—์„œ ํƒˆํ‡ดํ–ˆ์Šต๋‹ˆ๋‹ค.")); - } - - @WithMockCustomUser(username = "testid", role = "USER") - @DisplayName("ํŒ€์žฅ์ด ํŒ€ ํƒˆํ‡ด ์‹œ ์‹คํŒจ") - @Test - void ํŒ€์žฅ์ด_ํŒ€ํƒˆํ‡ด_์‹œ_์‹คํŒจ() throws Exception { - // given - Member member = createMember("testid"); - TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); - - // when - String response = mockMvc.perform( - delete("/api/team-users/" + team.getId() + "/" + "leave") - .contentType(MediaType.APPLICATION_JSON)) - .andDo(print()) - .andExpect(status().isBadRequest()) - .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - - // then - assertTrue(response.contains("ํŒ€์žฅ์€ ํŒ€์„ ๋‚˜๊ฐˆ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); + assertEquals(1, teamUserResponse.getTeamUsers().size()); } @WithMockCustomUser(username = "testid", role = "USER") @@ -293,7 +273,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // when String response = mockMvc.perform( - put("/api/team-users/" + team.getId() ) + put("/api/team-users/" + team.getId()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) @@ -323,7 +303,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // when String response = mockMvc.perform( - put("/api/team-users/" + team.getId() ) + put("/api/team-users/" + team.getId()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) @@ -356,7 +336,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // when String response = mockMvc.perform( - put("/api/team-users/" + team.getId() ) + put("/api/team-users/" + team.getId()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) @@ -366,4 +346,56 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // then assertTrue(response.contains("๋ณธ์ธ ๋˜๋Š” ํŒ€์žฅ๋งŒ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")); } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ์†Œ์† ์œ ์ € ์ถ”๊ฐ€์‹œ ์ด๋ฏธ ํŒ€์— ์†ํ•œ ๋ฉค๋ฒ„์ธ ๊ฒฝ์šฐ ์‹คํŒจ") + @Test + void ํŒ€_์†Œ์†_์œ ์ €_์ถ”๊ฐ€์‹œ_์ด๋ฏธ_ํŒ€์—_์†ํ–ˆ์„๊ฒฝ์šฐ_์‹คํŒจ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + Member inviteMember = createMember("์ถ”๊ฐ€ํ• ์‚ฌ๋žŒ"); + createAndInviteMember(teamOwner, inviteMember); + + TeamUserRequest.Create request = new TeamUserRequest.Create( + inviteMember.getUsername(), + "ํŒ€์›" + ); + + // when + String response = mockMvc.perform( + post("/api/team-users/" + team.getId() + "/invitations") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + assertTrue(response.contains("์ด๋ฏธ ํŒ€์— ์†ํ•œ ๋ฉค๋ฒ„์ž…๋‹ˆ๋‹ค.")); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€์žฅ์ด ์ž๊ธฐ ์ž์‹ ์„ ์ถ”๋ฐฉ ํ•  ๊ฒฝ์šฐ ์‹คํŒจ") + @Test + void ํŒ€์žฅ์ด_์ž๊ธฐ_์ž์‹ ์„_์ถ”๋ฐฉ_ํ• _๊ฒฝ์šฐ_์‹คํŒจ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + + // when + String response = mockMvc.perform( + delete("/api/team-users/" + team.getId() + "/" + member.getUsername()) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + //then + assertTrue(response.contains("ํŒ€์žฅ์€ ์ž์‹ ์„ ์ถ”๋ฐฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + } + + } From 63d8439906e6a3bf1e8ab45095c8db83a7685c0a Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 2 Apr 2024 00:46:20 +0900 Subject: [PATCH 041/103] =?UTF-8?q?refactor:=20restful=20=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20api=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/controller/TeamController.java | 8 +++- .../controller/TeamUserController.java | 44 ++++++------------- .../domain/team/dto/TeamUserRequest.java | 5 --- .../domain/team/service/TeamUserService.java | 10 +---- 4 files changed, 22 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/TeamController.java b/src/main/java/com/example/codebase/controller/TeamController.java index 3afce66a..e8c32949 100644 --- a/src/main/java/com/example/codebase/controller/TeamController.java +++ b/src/main/java/com/example/codebase/controller/TeamController.java @@ -5,6 +5,7 @@ import com.example.codebase.domain.member.service.MemberService; import com.example.codebase.domain.team.dto.TeamRequest; import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.dto.TeamUserResponse; import com.example.codebase.domain.team.entity.TeamUser; import com.example.codebase.domain.team.service.TeamService; import com.example.codebase.domain.team.service.TeamUserService; @@ -81,5 +82,10 @@ public ResponseEntity deleteTeam(@PathVariable Long teamId) { return new ResponseEntity("ํŒ€์ด ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.NO_CONTENT); } - + @GetMapping("/{teamId}/people") + @Operation(summary = "ํŒ€ ์†Œ์† ์œ ์ € ์กฐํšŒ", description = "ํŒ€์— ์†ํ•œ ์œ ์ €๋ฅผ ๋ชจ๋‘ ์กฐํšŒ ํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity getTeamUsers(@PathVariable Long teamId) { + TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); + return new ResponseEntity(response, HttpStatus.OK); + } } diff --git a/src/main/java/com/example/codebase/controller/TeamUserController.java b/src/main/java/com/example/codebase/controller/TeamUserController.java index 5bf6304b..c0b4e2aa 100644 --- a/src/main/java/com/example/codebase/controller/TeamUserController.java +++ b/src/main/java/com/example/codebase/controller/TeamUserController.java @@ -3,7 +3,6 @@ import com.example.codebase.annotation.LoginOnly; import com.example.codebase.domain.member.entity.Member; import com.example.codebase.domain.member.service.MemberService; -import com.example.codebase.domain.notification.service.NotificationSendService; import com.example.codebase.domain.team.dto.TeamUserRequest; import com.example.codebase.domain.team.dto.TeamUserResponse; import com.example.codebase.domain.team.entity.TeamUser; @@ -32,22 +31,15 @@ public TeamUserController(TeamUserService teamUserService, MemberService memberS this.memberService = memberService; } - @GetMapping("{teamId}") - @Operation(summary = "ํŒ€ ์†Œ์† ์œ ์ € ์กฐํšŒ", description = "ํŒ€์— ์†ํ•œ ์œ ์ €๋ฅผ ๋ชจ๋‘ ์กฐํšŒ ํ•ฉ๋‹ˆ๋‹ค.") - public ResponseEntity getTeamUsers(@PathVariable Long teamId) { - TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); - return new ResponseEntity(response, HttpStatus.OK); - } - - @PostMapping("{teamId}/invitations") + @PostMapping("/{username}") @LoginOnly @Operation(summary = "ํŒ€ ์œ ์ € ์ถ”๊ฐ€", description = "ํŒ€์— ์œ ์ €๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.") - public ResponseEntity addTeamUser(@PathVariable Long teamId, @RequestBody @Valid TeamUserRequest.Create request) { + public ResponseEntity addTeamUser(@PathVariable String username, @RequestParam Long teamId, @RequestBody @Valid TeamUserRequest.Create request) { String loginUsername = SecurityUtil.getCurrentUsernameValue(); TeamUser loginUser = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); loginUser.validOwner(); - Member inviteUser = memberService.getEntity(request.getUsername()); + Member inviteUser = memberService.getEntity(username); teamUserService.addTeamUser(loginUser, inviteUser, request); TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); @@ -55,25 +47,24 @@ public ResponseEntity addTeamUser(@PathVariable Long teamId, @RequestBody @Valid return new ResponseEntity(response, HttpStatus.OK); } - @DeleteMapping("{teamId}/{username}") + @DeleteMapping("/{username}") @LoginOnly @Operation(summary = "ํŒ€ ์œ ์ € ์‚ญ์ œ(์ถ”๋ฐฉ)", description = "ํŒ€์— ์†ํ•œ ์œ ์ €๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.") - public ResponseEntity deleteTeamUser(@PathVariable Long teamId, @PathVariable String username) { + public ResponseEntity deleteTeamUser(@PathVariable String username, @RequestParam Long teamId) { String loginUsername = SecurityUtil.getCurrentUsernameValue(); TeamUser loginUser = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); - loginUser.validOwner(); TeamUser deleteUser = teamUserService.findByTeamIdAndUsername(teamId, username); - teamUserService.deleteTeamUser(loginUser,deleteUser); + teamUserService.deleteTeamUser(loginUser, deleteUser); TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); return new ResponseEntity(response, HttpStatus.NO_CONTENT); } - @PostMapping("{teamId}/{username}/transfer") + @PatchMapping("/{username}/owner") @LoginOnly @Operation(summary = "ํŒ€ ๊ถŒํ•œ ์–‘๋„", description = "ํŒ€์˜ ๊ถŒํ•œ์„ ์–‘๋„ํ•ฉ๋‹ˆ๋‹ค.") - public ResponseEntity transferToOwner(@PathVariable Long teamId, @PathVariable String username) { + public ResponseEntity transferToOwner(@PathVariable String username, @RequestParam Long teamId) { String loginUsername = SecurityUtil.getCurrentUsernameValue(); TeamUser loginUser = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); loginUser.validOwner(); @@ -85,25 +76,16 @@ public ResponseEntity transferToOwner(@PathVariable Long teamId, @PathVariable S return new ResponseEntity(response, HttpStatus.OK); } - @DeleteMapping("{teamId}/leave") - @LoginOnly - @Operation(summary = "ํŒ€ ํƒˆํ‡ด", description = "ํŒ€์—์„œ ํƒˆํ‡ดํ•ฉ๋‹ˆ๋‹ค.") - public ResponseEntity leaveTeam(@PathVariable Long teamId) { - String loginUsername = SecurityUtil.getCurrentUsernameValue(); - TeamUser member = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); - teamUserService.leaveTeamUser(member); - - return new ResponseEntity("ํŒ€์—์„œ ํƒˆํ‡ดํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.NO_CONTENT); - } - - @PutMapping ("{teamId}") + @PatchMapping("{username}") @LoginOnly @Operation(summary = "ํŒ€ ์œ ์ € ์ง์ฑ… ๋ณ€๊ฒฝ", description = "ํŒ€ ์œ ์ €์˜ ์ง์ฑ…์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.") - public ResponseEntity updateTeamUser(@PathVariable Long teamId, @RequestBody @Valid TeamUserRequest.Update request) { + public ResponseEntity updateTeamUser(@PathVariable String username, @RequestParam Long teamId, @RequestBody @Valid TeamUserRequest.Update request) { String loginUsername = SecurityUtil.getCurrentUsernameValue(); TeamUser member = teamUserService.findByTeamIdAndUsername(teamId, loginUsername); - teamUserService.updateTeamUser(member, request); + TeamUser changeMember = teamUserService.findByTeamIdAndUsername(teamId, username); + + teamUserService.updateTeamUser(member, changeMember, request); return new ResponseEntity("์ง์ฑ…์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.OK); } diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamUserRequest.java b/src/main/java/com/example/codebase/domain/team/dto/TeamUserRequest.java index d8163ef9..ccd9167d 100644 --- a/src/main/java/com/example/codebase/domain/team/dto/TeamUserRequest.java +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamUserRequest.java @@ -19,9 +19,6 @@ public class TeamUserRequest { @Schema(name = "TeamUserRequest.Create", description = "ํŒ€ ์œ ์ € ์ถ”๊ฐ€ DTO") public static class Create { - @NotEmpty(message = "์œ ์ € ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") - private String username; - @NotEmpty(message = "ํฌ์ง€์…˜์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") @Size(max = 100 , message = "ํฌ์ง€์…˜์€ 100์ž ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") private String position; @@ -34,8 +31,6 @@ public static class Create { @Schema(name = "TeamUserRequest.Update", description = "ํŒ€ ์œ ์ € ์ˆ˜์ • DTO") public static class Update { - private String username; - @NotEmpty(message = "ํฌ์ง€์…˜์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") @Size(max = 100 , message = "ํฌ์ง€์…˜์€ 100์ž ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") private String position; diff --git a/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java index 39b04aaf..d147a559 100644 --- a/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java +++ b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java @@ -63,12 +63,6 @@ public void deleteTeamUser(TeamUser loginUser, TeamUser deleteUser) { teamUserRepository.delete(deleteUser); } - public void leaveTeamUser(TeamUser member) { - if (member.isOwner()) throw new RuntimeException("ํŒ€์žฅ์€ ํŒ€์„ ๋‚˜๊ฐˆ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - - teamUserRepository.delete(member); - } - public void transferToOwner(TeamUser loginUser, TeamUser transferUser) { loginUser.transferOwner(transferUser); @@ -76,8 +70,8 @@ public void transferToOwner(TeamUser loginUser, TeamUser transferUser) { teamUserRepository.save(transferUser); } - public void updateTeamUser(TeamUser member, TeamUserRequest.Update request) { - if (!Objects.equals(member.getMember().getUsername(), request.getUsername()) && !member.isOwner()) { + public void updateTeamUser(TeamUser member, TeamUser changeMember, TeamUserRequest.Update request) { + if(!(Objects.equals(member.getId(), changeMember.getId())) && !member.isOwner()) { throw new RuntimeException("๋ณธ์ธ ๋˜๋Š” ํŒ€์žฅ๋งŒ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."); } From f3ace6156c4dbac53b4d277537619290fbcaa807 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 2 Apr 2024 00:46:40 +0900 Subject: [PATCH 042/103] =?UTF-8?q?test:=20=EB=B3=80=EA=B2=BD=EB=90=9C=20a?= =?UTF-8?q?pi=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=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 --- .../controller/TeamControllerTest.java | 21 ++++++ .../controller/TeamUserControllerTest.java | 65 +++++-------------- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/TeamControllerTest.java b/src/test/java/com/example/codebase/controller/TeamControllerTest.java index 1f3a88f4..28a6858a 100644 --- a/src/test/java/com/example/codebase/controller/TeamControllerTest.java +++ b/src/test/java/com/example/codebase/controller/TeamControllerTest.java @@ -6,6 +6,7 @@ import com.example.codebase.domain.member.service.MemberService; import com.example.codebase.domain.team.dto.TeamRequest; import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.dto.TeamUserResponse; import com.example.codebase.domain.team.service.TeamService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @@ -259,4 +260,24 @@ public TeamResponse.Get createTeam(Member member, String name) { .andDo(print()) .andExpect(status().isNoContent()); } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ์†Œ์† ์œ ์ € ์กฐํšŒ") + @Test + void ํŒ€_์†Œ์†_์œ ์ €_์กฐํšŒ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + + // when + String response = mockMvc.perform(get("/api/teams/" + team.getId() + "/people") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); + assertEquals(1, teamUserResponse.getTeamUsers().size()); + assertEquals("testid", teamUserResponse.getTeamUsers().get(0).getUsername()); + } } diff --git a/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java b/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java index ed29c6b6..e068ef9e 100644 --- a/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java +++ b/src/test/java/com/example/codebase/controller/TeamUserControllerTest.java @@ -98,32 +98,11 @@ public TeamResponse.Get createTeam(Member member, String name) { public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { TeamUserRequest.Create request = new TeamUserRequest.Create( - inviteMember.getUsername(), "ํŒ€์›" ); teamUserService.addTeamUser(loginUser, inviteMember, request); } - @WithMockCustomUser(username = "testid", role = "USER") - @DisplayName("ํŒ€ ์†Œ์† ์œ ์ € ์กฐํšŒ") - @Test - void ํŒ€_์†Œ์†_์œ ์ €_์กฐํšŒ() throws Exception { - // given - Member member = createMember("testid"); - TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); - - // when - String response = mockMvc.perform(get("/api/team-users/" + team.getId()) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - - // then - TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); - assertEquals(1, teamUserResponse.getTeamUsers().size()); - assertEquals("testid", teamUserResponse.getTeamUsers().get(0).getUsername()); - } - @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("ํŒ€ ์†Œ์† ์œ ์ € ์ถ”๊ฐ€") @Test @@ -131,16 +110,15 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // given Member member = createMember("testid"); TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); - Member inviteMember = createMember("์ถ”๊ฐ€ํ• ์‚ฌ๋žŒ"); + Member inviteMember = createMember("inviteMember"); TeamUserRequest.Create request = new TeamUserRequest.Create( - inviteMember.getUsername(), "ํŒ€์›" ); // when String response = mockMvc.perform( - post("/api/team-users/" + team.getId() + "/invitations") + post("/api/team-users/" + inviteMember.getUsername() + "?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) @@ -151,7 +129,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); assertEquals(2, teamUserResponse.getTeamUsers().size()); assertEquals("testid", teamUserResponse.getTeamUsers().get(0).getUsername()); - assertEquals("์ถ”๊ฐ€ํ• ์‚ฌ๋žŒ", teamUserResponse.getTeamUsers().get(1).getUsername()); + assertEquals("inviteMember", teamUserResponse.getTeamUsers().get(1).getUsername()); assertEquals("ํŒ€์›", teamUserResponse.getTeamUsers().get(1).getPosition()); } @@ -169,7 +147,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // when String response = mockMvc.perform( - delete("/api/team-users/" + team.getId() + "/" + inviteMember.getUsername()) + delete("/api/team-users/" + inviteMember.getUsername() + "?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isNoContent()) @@ -190,7 +168,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // when String response = mockMvc.perform( - delete("/api/team-users/" + team.getId() + "/" + member.getUsername()) + delete("/api/team-users/" + member.getUsername() + "?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isBadRequest()) @@ -214,7 +192,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // when String response = mockMvc.perform( - post("/api/team-users/" + team.getId() + "/" + inviteMember.getUsername() + "/transfer") + patch("/api/team-users/" + inviteMember.getUsername() + "/owner?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) @@ -243,7 +221,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // when String response = mockMvc.perform( - delete("/api/team-users/" + team.getId() + "/" + inviteMember.getUsername()) + delete("/api/team-users/" + inviteMember.getUsername() + "?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isNoContent()) @@ -267,13 +245,12 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { createAndInviteMember(teamOwner, inviteMember); // ํŒ€์› TeamUserRequest.Update request = new TeamUserRequest.Update( - inviteMember.getUsername(), "๋ณ€๊ฒฝ๋œํฌ์ง€์…˜" ); // when String response = mockMvc.perform( - put("/api/team-users/" + team.getId()) + patch("/api/team-users/" + inviteMember.getUsername() + "?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) @@ -284,7 +261,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertTrue(response.contains("์ง์ฑ…์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")); } - @WithMockCustomUser(username = "ํ”„๋กœํ•„๋ณ€๊ฒฝํ• ์‚ฌ๋žŒ", role = "USER") + @WithMockCustomUser(username = "changePositionUser", role = "USER") @DisplayName("ํŒ€์›์ด ๋ณธ์ธ ์ง์ฑ… ๋ณ€๊ฒฝ") @Test void ํŒ€์›์ด_๋ณธ์ธ_์ง์ฑ…_๋ณ€๊ฒฝ() throws Exception { @@ -293,17 +270,16 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); - Member inviteMember = createMember("ํ”„๋กœํ•„๋ณ€๊ฒฝํ• ์‚ฌ๋žŒ"); + Member inviteMember = createMember("changePositionUser"); createAndInviteMember(teamOwner, inviteMember); // ํŒ€์› TeamUserRequest.Update request = new TeamUserRequest.Update( - inviteMember.getUsername(), "๋ณ€๊ฒฝ๋œํฌ์ง€์…˜" ); // when String response = mockMvc.perform( - put("/api/team-users/" + team.getId()) + patch("/api/team-users/" + inviteMember.getUsername() + "?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) @@ -314,7 +290,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertTrue(response.contains("์ง์ฑ…์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")); } - @WithMockCustomUser(username = "๋‹ค๋ฅธ์œ ์ €", role = "USER") + @WithMockCustomUser(username = "otherUser", role = "USER") @DisplayName("๋‹ค๋ฅธ ์œ ์ €๊ฐ€ ํŒ€์› ์ง์ฑ… ๋ณ€๊ฒฝ์‹œ๋„์‹œ ์‹คํŒจ") @Test void ๋‹ค๋ฅธ_์œ ์ €๊ฐ€_ํŒ€์›_์ง์ฑ…_๋ณ€๊ฒฝ์‹œ๋„์‹œ_์‹คํŒจ() throws Exception { @@ -323,20 +299,19 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); - Member teamMember1 = createMember("ํŒ€์›1"); + Member teamMember1 = createMember("user1"); createAndInviteMember(teamOwner, teamMember1); // ํŒ€์› - Member teamMember2 = createMember("๋‹ค๋ฅธ์œ ์ €"); + Member teamMember2 = createMember("otherUser"); createAndInviteMember(teamOwner, teamMember2); // ํŒ€์› TeamUserRequest.Update request = new TeamUserRequest.Update( - teamMember1.getUsername(), "๋ณ€๊ฒฝ๋œํฌ์ง€์…˜" ); // when String response = mockMvc.perform( - put("/api/team-users/" + team.getId()) + patch("/api/team-users/" + teamMember1.getUsername() + "?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) @@ -356,17 +331,16 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); - Member inviteMember = createMember("์ถ”๊ฐ€ํ• ์‚ฌ๋žŒ"); + Member inviteMember = createMember("addMember"); createAndInviteMember(teamOwner, inviteMember); TeamUserRequest.Create request = new TeamUserRequest.Create( - inviteMember.getUsername(), "ํŒ€์›" ); // when String response = mockMvc.perform( - post("/api/team-users/" + team.getId() + "/invitations") + post("/api/team-users/" + inviteMember.getUsername() + "?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) @@ -385,9 +359,8 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { Member member = createMember("testid"); TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); - // when String response = mockMvc.perform( - delete("/api/team-users/" + team.getId() + "/" + member.getUsername()) + delete("/api/team-users/" + member.getUsername() + "?teamId=" + team.getId()) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isBadRequest()) @@ -396,6 +369,4 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { //then assertTrue(response.contains("ํŒ€์žฅ์€ ์ž์‹ ์„ ์ถ”๋ฐฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); } - - } From b253513adf56df1b2f700019c292348c630ca2bc Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 14:28:29 +0900 Subject: [PATCH 043/103] =?UTF-8?q?refactor:=20FollowIds=20=EB=B3=B5?= =?UTF-8?q?=ED=95=A9=ED=82=A4=20=EC=A0=9C=EA=B1=B0=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/follow/entity/FollowIds.java | 31 ----------------- .../follow/repository/FollowRepository.java | 20 +++++++---- .../domain/follow/service/FollowService.java | 34 +++++++++---------- 3 files changed, 29 insertions(+), 56 deletions(-) delete mode 100644 src/main/java/com/example/codebase/domain/follow/entity/FollowIds.java diff --git a/src/main/java/com/example/codebase/domain/follow/entity/FollowIds.java b/src/main/java/com/example/codebase/domain/follow/entity/FollowIds.java deleted file mode 100644 index f2f75fc8..00000000 --- a/src/main/java/com/example/codebase/domain/follow/entity/FollowIds.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.example.codebase.domain.follow.entity; - -import com.example.codebase.domain.member.entity.Member; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.io.Serializable; -import java.util.UUID; - -@Getter -@NoArgsConstructor -@AllArgsConstructor -public class FollowIds implements Serializable { - - private UUID follower; - - private UUID following; - - - public static FollowIds of(Member follower, Member following) { - return new FollowIds(follower.getId(), following.getId()); - } - - public void valid() { - if (follower == following) { - throw new RuntimeException("์ž์‹ ์„ ํŒ”๋กœ์šฐ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - } - } - -} diff --git a/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java b/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java index 7b5b8911..6e2122c9 100644 --- a/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java +++ b/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java @@ -1,7 +1,6 @@ package com.example.codebase.domain.follow.repository; import com.example.codebase.domain.follow.entity.Follow; -import com.example.codebase.domain.follow.entity.FollowIds; import com.example.codebase.domain.follow.entity.FollowWithIsFollow; import com.example.codebase.domain.member.entity.Member; import org.springframework.data.domain.Page; @@ -9,16 +8,18 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -public interface FollowRepository extends JpaRepository { +import java.util.Optional; + +public interface FollowRepository extends JpaRepository { @Query("SELECT f AS follow, " + - "CASE WHEN f.following = :loginMember THEN 'self' " + + "CASE WHEN f.followingMember = :loginMember THEN 'self' " + "WHEN f2.follower = :loginMember THEN 'follow' ELSE 'none' END as status " + - "FROM Follow f LEFT JOIN Follow f2 ON f.following = f2.following " + + "FROM Follow f LEFT JOIN Follow f2 ON f.followingMember = f2.followingMember " + "AND f2.follower = :loginMember " + "WHERE f.follower = :targetMember " + - "ORDER BY CASE WHEN f.following = :loginMember THEN 1 " + + "ORDER BY CASE WHEN f.followingMember = :loginMember THEN 1 " + "WHEN f2.follower = :loginMember THEN 2 ELSE 3 END, " + "f.followTime ASC") Page findFollowingByTargetMember(Member targetMember, Member loginMember, PageRequest pageRequest); @@ -27,11 +28,16 @@ public interface FollowRepository extends JpaRepository { @Query("SELECT f AS follow, " + "CASE WHEN f.follower = :loginMember THEN 'self' " + "WHEN f2.follower = :loginMember THEN 'follow' ELSE 'none' END as status " + - "FROM Follow f LEFT JOIN Follow f2 ON f.follower = f2.following " + + "FROM Follow f LEFT JOIN Follow f2 ON f.follower = f2.followingMember " + "AND f2.follower = :loginMember " + - "WHERE f.following = :targetMember " + + "WHERE f.followingMember = :targetMember " + "ORDER BY CASE WHEN f.follower = :loginMember THEN 1 " + "WHEN f2.follower = :loginMember THEN 2 ELSE 3 END, " + "f.followTime ASC") Page findFollowerByTargetMember(Member targetMember, Member loginMember, PageRequest pageRequest); + + + Optional findByFollowerAndFollowingMember(Member follower, Member followingMember); + + boolean existsByFollowerAndFollowingMember(Member follower, Member followingMember); } \ No newline at end of file diff --git a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java index bbd60258..6b1091b1 100644 --- a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java +++ b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java @@ -4,7 +4,6 @@ import com.example.codebase.domain.follow.dto.FollowMemberDetailResponseDTO; import com.example.codebase.domain.follow.dto.FollowMembersResponseDTO; import com.example.codebase.domain.follow.entity.Follow; -import com.example.codebase.domain.follow.entity.FollowIds; import com.example.codebase.domain.follow.entity.FollowWithIsFollow; import com.example.codebase.domain.follow.repository.FollowRepository; import com.example.codebase.domain.member.entity.Member; @@ -19,7 +18,9 @@ import java.util.List; import java.util.Optional; + @Service +@Transactional(readOnly = true) public class FollowService { private final FollowRepository followRepository; @@ -33,36 +34,34 @@ public FollowService(FollowRepository followRepository, MemberRepository memberR } @Transactional - public void followMember(String username, String followUser) { - Member followerUser = memberRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); - Member followingUser = memberRepository.findByUsername(followUser).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); + public void followMember(String username, String followMember) { + Member follower = memberRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); + Member following = memberRepository.findByUsername(followMember).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); + + checkFollowing(follower, following); - FollowIds followIds = FollowIds.of(followerUser, followingUser); - followIds.valid(); + followRepository.save(Follow.of(follower, following)); + } - Optional alreadyFollow = followRepository.findById(followIds); - if (alreadyFollow.isPresent()) { + private void checkFollowing(Member follower, Member following) { + if (followRepository.existsByFollowerAndFollowingMember(follower, following)) { throw new RuntimeException("์ด๋ฏธ ํŒ”๋กœ์ž‰ ์ค‘์ž…๋‹ˆ๋‹ค."); } - followRepository.save(Follow.of(followerUser, followingUser)); - } @Transactional - public void unfollowMember(String username, String followUser) { - Member followerUser = memberRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); - Member followingUser = memberRepository.findByUsername(followUser).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); - - FollowIds followIds = FollowIds.of(followerUser, followingUser); + public void unfollowMember(String username, String followMember) { + Member follower = memberRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); + Member following = memberRepository.findByUsername(followMember).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); - Optional alreadyFollow = followRepository.findById(followIds); + Optional alreadyFollow = followRepository.findByFollowerAndFollowingMember(follower, following); if (alreadyFollow.isEmpty()) { throw new RuntimeException("ํŒ”๋กœ์ž‰ ์ค‘์ด ์•„๋‹™๋‹ˆ๋‹ค."); } followRepository.delete(alreadyFollow.get()); } - @Transactional(readOnly = true) + public FollowMembersResponseDTO getFollowingList(Optional loginUsername, String targetUsername, PageRequest pageRequest) { Member targetMember = memberRepository.findByUsername(targetUsername).orElseThrow(NotFoundMemberException::new); Member loginMember = loginUsername.map(s -> memberRepository.findByUsername(s) @@ -80,7 +79,6 @@ public FollowMembersResponseDTO getFollowingList(Optional loginUsername, return FollowMembersResponseDTO.of(followingMemberResponses, pageInfo); } - @Transactional(readOnly = true) public FollowMembersResponseDTO getFollowerList(Optional loginUsername, String targetUsername, PageRequest pageRequest) { Member targetMember = memberRepository.findByUsername(targetUsername).orElseThrow(NotFoundMemberException::new); Member loginMember = loginUsername.map(s -> memberRepository.findByUsername(s) From d148c00ebbd158b24153555cb4f227feb305fff6 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 14:29:12 +0900 Subject: [PATCH 044/103] =?UTF-8?q?refactor:=20Following=20->=20FollowingM?= =?UTF-8?q?ember=20=EB=B3=80=EC=88=98=EB=AA=85=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=A6=AC=ED=8C=A9=ED=84=B0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/domain/magazine/repository/MagazineRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java index 5268fd85..dbb14ff1 100644 --- a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java +++ b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java @@ -16,7 +16,7 @@ public interface MagazineRepository extends JpaRepository { Page findByMember(Member member, PageRequest pageRequest); - @Query("SELECT m FROM Magazine m LEFT JOIN Follow f ON (f.follower= :member) WHERE f.following = m.member") + @Query("SELECT m FROM Magazine m LEFT JOIN Follow f ON (f.follower= :member) WHERE f.followingMember = m.member") Page findByMemberToFollowing(Member member, PageRequest pageRequest); } \ No newline at end of file From c0e8c2fb9f85cbac994d4d7920f4408f72dce61e Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 14:35:08 +0900 Subject: [PATCH 045/103] =?UTF-8?q?refactor:=20Follow=20API=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0=EB=A7=81=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CurationControllerTest ์นดํ…Œ๊ณ ๋ฆฌ ์ค‘๋ณต ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์–ด ๋žœ๋ค ๋ฒ”์œ„ ํ™•์žฅ --- .../codebase/controller/FollowController.java | 20 +++++++++---- .../controller/CurationControllerTest.java | 2 +- .../controller/FollowControllerTest.java | 30 +++++++------------ 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/FollowController.java b/src/main/java/com/example/codebase/controller/FollowController.java index 24654604..8eea7ca9 100644 --- a/src/main/java/com/example/codebase/controller/FollowController.java +++ b/src/main/java/com/example/codebase/controller/FollowController.java @@ -38,20 +38,30 @@ public FollowController(FollowService followService, NotificationSendService not @Operation(summary = "ํŒ”๋กœ์šฐ", description = "์ƒ๋Œ€๋ฐฉ์„ ํŒ”๋กœ์ž‰ ํ•ฉ๋‹ˆ๋‹ค") @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER', 'ROLE_ADMIN')") @PostMapping("{username}") - public ResponseEntity followMember(@PathVariable("username") String followUser) { + public ResponseEntity followMember(@PathVariable("username") String followMember) { String username = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); - followService.followMember(username, followUser); - notificationService.send(username, followUser, NotificationType.NEW_FOLLOWER); + checkSameMember(username, followMember, "ํŒ”๋กœ์ž‰"); + + followService.followMember(username, followMember); + notificationService.send(username, followMember, NotificationType.NEW_FOLLOWER); return new ResponseEntity("ํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.CREATED); } + private void checkSameMember(String username1, String username2, String message) { + if (username1.equals(username2)) { + throw new RuntimeException("์ž๊ธฐ ์ž์‹ ์„ %s ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค".formatted(message)); + } + } + @Operation(summary = "์–ธํŒ”๋กœ์šฐ" , description = "์ƒ๋Œ€๋ฐฉ์„ ์–ธ ํŒ”๋กœ์ž‰ ํ•ฉ๋‹ˆ๋‹ค") @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER', 'ROLE_ADMIN')") @DeleteMapping("{username}") - public ResponseEntity unfollowMember(@PathVariable("username") String followUser) { + public ResponseEntity unfollowMember(@PathVariable("username") String followMember) { String username = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); - followService.unfollowMember(username, followUser); + checkSameMember(username, followMember, "์–ธํŒ”๋กœ์ž‰"); + + followService.unfollowMember(username, followMember); return new ResponseEntity("์–ธํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.NO_CONTENT); } diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index ed39c88f..15f6e2a9 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -139,7 +139,7 @@ public Member createOrLoadMember(String username, String... authorities) { public MagazineCategory createCategory() { Random random = new Random(System.currentTimeMillis()); - String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(10000); char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); diff --git a/src/test/java/com/example/codebase/controller/FollowControllerTest.java b/src/test/java/com/example/codebase/controller/FollowControllerTest.java index c6e887e2..1670a91f 100644 --- a/src/test/java/com/example/codebase/controller/FollowControllerTest.java +++ b/src/test/java/com/example/codebase/controller/FollowControllerTest.java @@ -1,18 +1,7 @@ package com.example.codebase.controller; -import com.amazonaws.services.s3.AmazonS3; -import com.example.codebase.config.S3MockConfig; -import com.example.codebase.domain.artwork.dto.ArtworkCommentCreateDTO; -import com.example.codebase.domain.artwork.dto.ArtworkCreateDTO; -import com.example.codebase.domain.artwork.dto.ArtworkMediaCreateDTO; -import com.example.codebase.domain.artwork.dto.ArtworkUpdateDTO; -import com.example.codebase.domain.artwork.entity.Artwork; -import com.example.codebase.domain.artwork.entity.ArtworkComment; -import com.example.codebase.domain.artwork.entity.ArtworkMedia; -import com.example.codebase.domain.artwork.repository.ArtworkRepository; import com.example.codebase.domain.auth.WithMockCustomUser; import com.example.codebase.domain.follow.entity.Follow; -import com.example.codebase.domain.follow.entity.FollowIds; import com.example.codebase.domain.follow.repository.FollowRepository; import com.example.codebase.domain.member.entity.Authority; import com.example.codebase.domain.member.entity.Member; @@ -20,19 +9,14 @@ import com.example.codebase.domain.member.repository.MemberAuthorityRepository; import com.example.codebase.domain.member.repository.MemberRepository; import com.example.codebase.domain.notification.entity.NotificationSetting; -import com.example.codebase.s3.S3Service; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import io.findify.s3mock.S3Mock; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; import org.springframework.core.io.ResourceLoader; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; @@ -41,9 +25,6 @@ import jakarta.transaction.Transactional; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; import java.time.LocalDateTime; import java.util.*; @@ -149,6 +130,17 @@ public Follow createOrLoadFollow(Member follower, Member following) { .andExpect(status().isBadRequest()); } + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("์ž๊ธฐ ์ž์‹ ์„ ์–ธ ํŒ”๋กœ์šฐ ํ• ์‹œ") + @Test + public void ์ž๊ธฐ_์ž์‹ ์„_์–ธํŒ”๋กœ์šฐ_ํ• ๋–„() throws Exception { + createOrLoadMember("testid", "ROLE_CURATOR"); + + mockMvc.perform(delete(String.format("/api/follow/%s", "testid"))) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("ํŒ”๋กœ์šฐ ์ค‘์ด ์•„๋‹๋•Œ ์–ธํŒ”๋กœ์šฐ๋ฅผ ์‹œ๋„ํ• ์‹œ") @Test From fb7b0407c09ae8474367d645a022f5b4864bbd9d Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 15:55:47 +0900 Subject: [PATCH 046/103] =?UTF-8?q?feat:=20=ED=8C=94=EB=A1=9C=EC=9E=89/?= =?UTF-8?q?=ED=8C=94=EB=A1=9C=EC=9A=B0=20=ED=8C=80=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=99=95=EC=9E=A5=20=EB=B0=8F=20=EB=A6=AC=ED=8C=A9=ED=84=B0?= =?UTF-8?q?=EB=A7=81=20/=20API=20=ED=98=B8=EC=B6=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ํŒ”๋กœ์šฐ/์–ธํŒ”๋กœ์šฐ API ๋ฅผ ํ†ตํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค. - ํ•ด๋‹น API๋Š” ACTION๊ณผ URN์„ ๋ฐ›์•„์„œ ๋‚ด๋ถ€์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค. - ํŒ”๋กœ์šฐ ๊ด€๋ จ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์ค‘๋ณต ์š”์ฒญ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. - 3์ดˆ ์ด๋‚ด ๋ฐ˜๋ณต์ ์ธ ๋™์ผํ•œ ์š”์ฒญ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. - ํŒ€ ๊ด€๋ จ ํŒ”๋กœ์šฐ ๊ธฐ๋Šฅ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. --- .../codebase/controller/FollowController.java | 65 +++++++++++++------ .../domain/follow/dto/FollowRequest.java | 54 +++++++++++++++ .../follow/repository/FollowRepository.java | 5 ++ .../domain/follow/service/FollowService.java | 64 +++++++++++++++--- .../exception/DuplicatedRequestException.java | 12 ++++ 5 files changed, 172 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java create mode 100644 src/main/java/com/example/codebase/exception/DuplicatedRequestException.java diff --git a/src/main/java/com/example/codebase/controller/FollowController.java b/src/main/java/com/example/codebase/controller/FollowController.java index 8eea7ca9..63ac99af 100644 --- a/src/main/java/com/example/codebase/controller/FollowController.java +++ b/src/main/java/com/example/codebase/controller/FollowController.java @@ -1,6 +1,7 @@ package com.example.codebase.controller; import com.example.codebase.domain.follow.dto.FollowMembersResponseDTO; +import com.example.codebase.domain.follow.dto.FollowRequest; import com.example.codebase.domain.follow.service.FollowService; import com.example.codebase.domain.notification.entity.NotificationType; import com.example.codebase.domain.notification.service.NotificationSendService; @@ -8,6 +9,9 @@ import com.example.codebase.util.SecurityUtil; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.PositiveOrZero; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; @@ -35,17 +39,51 @@ public FollowController(FollowService followService, NotificationSendService not this.notificationService = notificationService; } - @Operation(summary = "ํŒ”๋กœ์šฐ", description = "์ƒ๋Œ€๋ฐฉ์„ ํŒ”๋กœ์ž‰ ํ•ฉ๋‹ˆ๋‹ค") + @Operation(summary = "ํŒ”๋กœ์šฐ/์–ธํŒ”๋กœ์šฐ", description = "์ƒ๋Œ€๋ฐฉ์„ ํŒ”๋กœ์šฐ/์–ธํŒ”๋กœ์šฐ ํ•ฉ๋‹ˆ๋‹ค.") @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER', 'ROLE_ADMIN')") - @PostMapping("{username}") - public ResponseEntity followMember(@PathVariable("username") String followMember) { + @PostMapping + public ResponseEntity follow( + @RequestParam("action") @Pattern(regexp = "follow|unfollow", message = "์ž˜๋ชป๋œ action ๊ฐ’์ž…๋‹ˆ๋‹ค.") String action, + @RequestBody FollowRequest.Create request + ) { String username = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); - checkSameMember(username, followMember, "ํŒ”๋กœ์ž‰"); + FollowRequest.FollowEntityUrn entityUrn = FollowRequest.FollowEntityUrn.from(request.getUrn()); - followService.followMember(username, followMember); - notificationService.send(username, followMember, NotificationType.NEW_FOLLOWER); + if (action.equals("unfollow")) { + return unfollow(username, entityUrn); + } + return follow(username, entityUrn); + } - return new ResponseEntity("ํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.CREATED); + private ResponseEntity follow(String username, FollowRequest.FollowEntityUrn entityUrn) { + switch (entityUrn) { + case MEMBER -> { + checkSameMember(username, entityUrn.getId(), "ํŒ”๋กœ์ž‰"); + followService.followMember(username, entityUrn.getId()); + notificationService.send(username, entityUrn.getId(), NotificationType.NEW_FOLLOWER); + return new ResponseEntity("ํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.CREATED); + } + case TEAM -> { + followService.followTeam(username, entityUrn.getId()); + return new ResponseEntity("ํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.CREATED); + } + default -> throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š๋Š” URN ์ž…๋‹ˆ๋‹ค."); + } + } + + private ResponseEntity unfollow(String username, FollowRequest.FollowEntityUrn entityUrn) { + switch (entityUrn) { + case MEMBER -> { + checkSameMember(username, entityUrn.getId(), "์–ธํŒ”๋กœ์šฐ"); + followService.unfollowMember(username, entityUrn.getId()); + return new ResponseEntity("์–ธํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.OK); + } + case TEAM -> { + followService.unfollowTeam(username, entityUrn.getId()); + return new ResponseEntity("์–ธํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.OK); + } + default -> throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š๋Š” URN ์ž…๋‹ˆ๋‹ค."); + } } private void checkSameMember(String username1, String username2, String message) { @@ -54,19 +92,6 @@ private void checkSameMember(String username1, String username2, String message) } } - @Operation(summary = "์–ธํŒ”๋กœ์šฐ" , description = "์ƒ๋Œ€๋ฐฉ์„ ์–ธ ํŒ”๋กœ์ž‰ ํ•ฉ๋‹ˆ๋‹ค") - @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER', 'ROLE_ADMIN')") - @DeleteMapping("{username}") - public ResponseEntity unfollowMember(@PathVariable("username") String followMember) { - String username = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); - checkSameMember(username, followMember, "์–ธํŒ”๋กœ์ž‰"); - - followService.unfollowMember(username, followMember); - - return new ResponseEntity("์–ธํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.NO_CONTENT); - } - - @Operation(summary = "ํŒ”๋กœ์ž‰", description = "ํ•ด๋‹น ์œ ์ €๊ฐ€ ํŒ”๋กœ์ž‰ ํ•˜๋Š” ์‚ฌ๋žŒ ๋ชฉ๋ก์„ ์กฐํšŒ ํ•ฉ๋‹ˆ๋‹ค") @GetMapping("/{username}/following") public ResponseEntity getFollowingList(@PathVariable("username") String username, diff --git a/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java new file mode 100644 index 00000000..48a0c8b0 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java @@ -0,0 +1,54 @@ +package com.example.codebase.domain.follow.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Getter; +import lombok.Setter; + +import javax.annotation.RegEx; + +public class FollowRequest { + + public enum FollowEntityUrn { + MEMBER("urn:member"), + TEAM("urn:team"); + private final String resource; + + @Getter + private String id; + + FollowEntityUrn(String resource) { + this.resource = resource; + this.id = null; + } + + public static FollowEntityUrn from(String urn) { + String resource = urn.split(":")[1]; + String id = urn.split(":")[2]; + try { + FollowEntityUrn followEntityUrn = FollowEntityUrn.valueOf(resource.toUpperCase()); + followEntityUrn.id = id; + return followEntityUrn; + } catch (IllegalArgumentException e) { + throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š์€ URN์ž…๋‹ˆ๋‹ค."); + } + } + + public boolean isMember() { + return this == MEMBER; + } + + public boolean isTeam() { + return this == TEAM; + } + } + + @Getter + @Setter + public static class Create { + + @NotBlank(message = "URN์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.") + @Pattern(regexp = "urn:[a-z]:+", message = "์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.") + private String urn; + } +} diff --git a/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java b/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java index 6e2122c9..6077b2f5 100644 --- a/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java +++ b/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java @@ -3,6 +3,7 @@ import com.example.codebase.domain.follow.entity.Follow; import com.example.codebase.domain.follow.entity.FollowWithIsFollow; import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.entity.Team; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; @@ -40,4 +41,8 @@ public interface FollowRepository extends JpaRepository { Optional findByFollowerAndFollowingMember(Member follower, Member followingMember); boolean existsByFollowerAndFollowingMember(Member follower, Member followingMember); + + boolean existsByFollowerAndFollowingTeam(Member follower, Team team); + + Optional findByFollowerAndFollowingTeam(Member follower, Team team); } \ No newline at end of file diff --git a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java index 6b1091b1..6ea3e2d2 100644 --- a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java +++ b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java @@ -9,48 +9,59 @@ import com.example.codebase.domain.member.entity.Member; import com.example.codebase.domain.member.exception.NotFoundMemberException; import com.example.codebase.domain.member.repository.MemberRepository; +import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.repository.TeamRepository; +import com.example.codebase.exception.DuplicatedRequestException; +import com.example.codebase.util.RedisUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; -@Service @Transactional(readOnly = true) +@Service public class FollowService { private final FollowRepository followRepository; private final MemberRepository memberRepository; + private final TeamRepository teamRepository; + + private final RedisUtil redisUtil; + @Autowired - public FollowService(FollowRepository followRepository, MemberRepository memberRepository) { + public FollowService(FollowRepository followRepository, MemberRepository memberRepository, TeamRepository teamRepository, RedisUtil redisUtil) { this.followRepository = followRepository; this.memberRepository = memberRepository; + this.teamRepository = teamRepository; + this.redisUtil = redisUtil; } @Transactional public void followMember(String username, String followMember) { + checkDuplicatedRequest(username, followMember); + Member follower = memberRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); Member following = memberRepository.findByUsername(followMember).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); - checkFollowing(follower, following); - - followRepository.save(Follow.of(follower, following)); - } - - private void checkFollowing(Member follower, Member following) { if (followRepository.existsByFollowerAndFollowingMember(follower, following)) { throw new RuntimeException("์ด๋ฏธ ํŒ”๋กœ์ž‰ ์ค‘์ž…๋‹ˆ๋‹ค."); } + + followRepository.save(Follow.of(follower, following)); } @Transactional public void unfollowMember(String username, String followMember) { + checkDuplicatedRequest(username, followMember); + Member follower = memberRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); Member following = memberRepository.findByUsername(followMember).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); @@ -61,6 +72,42 @@ public void unfollowMember(String username, String followMember) { followRepository.delete(alreadyFollow.get()); } + @Transactional + public void followTeam(String username, String teamId) { + checkDuplicatedRequest(username, teamId); + + Member follower = memberRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); + Team team = teamRepository.findById(Long.valueOf(teamId)).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํŒ€์ž…๋‹ˆ๋‹ค.")); + + if (followRepository.existsByFollowerAndFollowingTeam(follower, team)) { + throw new RuntimeException("์ด๋ฏธ ํŒ”๋กœ์ž‰ ์ค‘์ž…๋‹ˆ๋‹ค."); + } + + followRepository.save(Follow.of(follower, team)); + } + + @Transactional + public void unfollowTeam(String username, String teamId) { + checkDuplicatedRequest(username, teamId); + + Member follower = memberRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); + Team team = teamRepository.findById(Long.valueOf(teamId)).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํŒ€์ž…๋‹ˆ๋‹ค.")); + + Optional alreadyFollow = followRepository.findByFollowerAndFollowingTeam(follower, team); + if (alreadyFollow.isEmpty()) { + throw new RuntimeException("ํŒ”๋กœ์ž‰ ์ค‘์ด ์•„๋‹™๋‹ˆ๋‹ค."); + } + followRepository.delete(alreadyFollow.get()); + } + + // TODO : AOP ๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ + private void checkDuplicatedRequest(String follower, String following) { + if(redisUtil.getData(follower + "->" + following).isPresent()) { + throw new DuplicatedRequestException(); + } + redisUtil.setDataAndExpire(follower + "->" + following, String.valueOf(LocalDateTime.now()), 3000); + } + public FollowMembersResponseDTO getFollowingList(Optional loginUsername, String targetUsername, PageRequest pageRequest) { Member targetMember = memberRepository.findByUsername(targetUsername).orElseThrow(NotFoundMemberException::new); @@ -95,4 +142,5 @@ public FollowMembersResponseDTO getFollowerList(Optional loginUsername, return FollowMembersResponseDTO.of(followerMemberResponses, pageInfo); } + } diff --git a/src/main/java/com/example/codebase/exception/DuplicatedRequestException.java b/src/main/java/com/example/codebase/exception/DuplicatedRequestException.java new file mode 100644 index 00000000..f1a77523 --- /dev/null +++ b/src/main/java/com/example/codebase/exception/DuplicatedRequestException.java @@ -0,0 +1,12 @@ +package com.example.codebase.exception; + +public class DuplicatedRequestException extends RuntimeException { + + public DuplicatedRequestException() { + super("์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."); + } + + public DuplicatedRequestException(String message) { + super(message); + } +} From cb18d4a33d714570d133180a1de5c6dc7a7ea4ba Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 16:03:16 +0900 Subject: [PATCH 047/103] =?UTF-8?q?feat:=20Member=20=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9E=89=20=EA=B4=80=EB=A0=A8=20=EC=96=91=EB=B0=A9=ED=96=A5=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/codebase/domain/member/entity/Member.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/member/entity/Member.java b/src/main/java/com/example/codebase/domain/member/entity/Member.java index a0c28903..0b4b2f40 100644 --- a/src/main/java/com/example/codebase/domain/member/entity/Member.java +++ b/src/main/java/com/example/codebase/domain/member/entity/Member.java @@ -123,14 +123,6 @@ public class Member { @OneToOne(mappedBy = "member", optional=false, cascade = CascadeType.ALL, fetch = FetchType.LAZY) private NotificationSetting notificationSettings; - @Builder.Default - @OneToMany(mappedBy = "follower", cascade = CascadeType.ALL) - private List followings = new ArrayList<>(); - - @Builder.Default - @OneToMany(mappedBy = "following", cascade = CascadeType.ALL) - private List followers = new ArrayList<>(); - @Builder.Default @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE, orphanRemoval = true) private List teamUserRoles = new ArrayList<>(); From f77434347ab5c6edef6204a9c9e179c770c2a070 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 16:21:04 +0900 Subject: [PATCH 048/103] =?UTF-8?q?refactor:=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=20API=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ๋กœ์ง ์ •๋ฆฌ ๋ฐ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ๊ฐœ์„  --- .../example/codebase/controller/FollowController.java | 11 ++++------- .../codebase/domain/follow/dto/FollowRequest.java | 10 +++++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/FollowController.java b/src/main/java/com/example/codebase/controller/FollowController.java index 63ac99af..144e779a 100644 --- a/src/main/java/com/example/codebase/controller/FollowController.java +++ b/src/main/java/com/example/codebase/controller/FollowController.java @@ -9,6 +9,7 @@ import com.example.codebase.util.SecurityUtil; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; @@ -44,7 +45,7 @@ public FollowController(FollowService followService, NotificationSendService not @PostMapping public ResponseEntity follow( @RequestParam("action") @Pattern(regexp = "follow|unfollow", message = "์ž˜๋ชป๋œ action ๊ฐ’์ž…๋‹ˆ๋‹ค.") String action, - @RequestBody FollowRequest.Create request + @RequestBody @Valid FollowRequest.Create request ) { String username = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); FollowRequest.FollowEntityUrn entityUrn = FollowRequest.FollowEntityUrn.from(request.getUrn()); @@ -61,14 +62,12 @@ private ResponseEntity follow(String username, FollowRequest.FollowEntityUrn ent checkSameMember(username, entityUrn.getId(), "ํŒ”๋กœ์ž‰"); followService.followMember(username, entityUrn.getId()); notificationService.send(username, entityUrn.getId(), NotificationType.NEW_FOLLOWER); - return new ResponseEntity("ํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.CREATED); } case TEAM -> { followService.followTeam(username, entityUrn.getId()); - return new ResponseEntity("ํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.CREATED); } - default -> throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š๋Š” URN ์ž…๋‹ˆ๋‹ค."); } + return new ResponseEntity("ํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.CREATED); } private ResponseEntity unfollow(String username, FollowRequest.FollowEntityUrn entityUrn) { @@ -76,14 +75,12 @@ private ResponseEntity unfollow(String username, FollowRequest.FollowEntityUrn e case MEMBER -> { checkSameMember(username, entityUrn.getId(), "์–ธํŒ”๋กœ์šฐ"); followService.unfollowMember(username, entityUrn.getId()); - return new ResponseEntity("์–ธํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.OK); } case TEAM -> { followService.unfollowTeam(username, entityUrn.getId()); - return new ResponseEntity("์–ธํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.OK); } - default -> throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š๋Š” URN ์ž…๋‹ˆ๋‹ค."); } + return new ResponseEntity("์–ธํŒ”๋กœ์ž‰ ํ–ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.OK); } private void checkSameMember(String username1, String username2, String message) { diff --git a/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java index 48a0c8b0..d813e546 100644 --- a/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java +++ b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java @@ -23,14 +23,18 @@ public enum FollowEntityUrn { } public static FollowEntityUrn from(String urn) { - String resource = urn.split(":")[1]; - String id = urn.split(":")[2]; try { + if (!urn.startsWith("urn:")) { + throw new IllegalArgumentException(); + } + String resource = urn.split(":")[1]; + String id = urn.split(":")[2]; + FollowEntityUrn followEntityUrn = FollowEntityUrn.valueOf(resource.toUpperCase()); followEntityUrn.id = id; return followEntityUrn; } catch (IllegalArgumentException e) { - throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š์€ URN์ž…๋‹ˆ๋‹ค."); + throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š์€ URN ์ž…๋‹ˆ๋‹ค."); } } From dc4f098235deae29de2f5091421b099454ad2f3a Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 16:54:10 +0900 Subject: [PATCH 049/103] =?UTF-8?q?test:=20Follow=20API=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - API ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ ์ฝ”๋“œ ์ˆ˜์ • - ํŒ€ ํŒ”๋กœ์ž‰ ๊ด€๋ จ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ - ์ค‘๋ณต ์š”์ฒญ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ --- .../domain/follow/dto/FollowRequest.java | 2 +- .../team/repository/TeamRepository.java | 4 + .../controller/FollowControllerTest.java | 225 +++++++++++++++++- 3 files changed, 218 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java index d813e546..f859f0ee 100644 --- a/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java +++ b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java @@ -52,7 +52,7 @@ public boolean isTeam() { public static class Create { @NotBlank(message = "URN์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.") - @Pattern(regexp = "urn:[a-z]:+", message = "์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.") + @Pattern(regexp = "^urn:[a-z]+:\\w+$", message = "์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.") private String urn; } } diff --git a/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java b/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java index e09481a3..446b4db4 100644 --- a/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java +++ b/src/main/java/com/example/codebase/domain/team/repository/TeamRepository.java @@ -3,10 +3,14 @@ import com.example.codebase.domain.team.entity.Team; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface TeamRepository extends JpaRepository { boolean existsByName(String name); boolean existsByNameAndIdNot(String name, Long id); + Optional findByName(String name); + } diff --git a/src/test/java/com/example/codebase/controller/FollowControllerTest.java b/src/test/java/com/example/codebase/controller/FollowControllerTest.java index 1670a91f..bf78d76e 100644 --- a/src/test/java/com/example/codebase/controller/FollowControllerTest.java +++ b/src/test/java/com/example/codebase/controller/FollowControllerTest.java @@ -1,6 +1,7 @@ package com.example.codebase.controller; import com.example.codebase.domain.auth.WithMockCustomUser; +import com.example.codebase.domain.follow.dto.FollowRequest; import com.example.codebase.domain.follow.entity.Follow; import com.example.codebase.domain.follow.repository.FollowRepository; import com.example.codebase.domain.member.entity.Authority; @@ -9,10 +10,15 @@ import com.example.codebase.domain.member.repository.MemberAuthorityRepository; import com.example.codebase.domain.member.repository.MemberRepository; import com.example.codebase.domain.notification.entity.NotificationSetting; +import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.repository.TeamRepository; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.*; +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.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -23,11 +29,12 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import jakarta.transaction.Transactional; - +import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; -import java.util.*; +import java.util.Optional; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -61,6 +68,9 @@ class FollowControllerTest { @Autowired private FollowRepository followRepository; + @Autowired + private TeamRepository teamRepository; + private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach @@ -103,10 +113,37 @@ public Member createOrLoadMember(String username, String... authorities) { return memberRepository.save(dummy); } + public Team createOrLoadTeam() { + return createOrLoadTeam("testteam"); + } + + public Team createOrLoadTeam(String teamName) { + Optional team = teamRepository.findByName(teamName); + if (team.isPresent()) { + return team.get(); + } + + Team dummy = Team.builder() + .name(teamName) + .description("test") + .backgroundImage("test") + .profileImage("test") + .address("test") + .createdTime(LocalDateTime.now()) + .build(); + + return teamRepository.save(dummy); + } + public Follow createOrLoadFollow(Member follower, Member following) { return followRepository.save(Follow.of(follower, following)); } + public Follow createOrLoadFollow(Member follower, Team following) { + return followRepository.save(Follow.of(follower, following)); + } + + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("์ƒ๋Œ€๋ฐฉ ํŒ”๋กœ์šฐ ์„ฑ๊ณต") @Test @@ -114,7 +151,15 @@ public Follow createOrLoadFollow(Member follower, Member following) { createOrLoadMember(); Member followUser = createOrLoadMember("followUser", "ROLE_USER"); - mockMvc.perform(post("/api/follow/" + followUser.getUsername())) + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:member:" + followUser.getUsername()); + + mockMvc.perform( + post("/api/follow") + .param("action", "follow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) .andDo(print()) .andExpect(status().isCreated()); } @@ -125,7 +170,14 @@ public Follow createOrLoadFollow(Member follower, Member following) { public void ์ž๊ธฐ_์ž์‹ ์„_ํŒ”๋กœ์šฐ_ํ• ๋–„() throws Exception { createOrLoadMember("testid", "ROLE_CURATOR"); - mockMvc.perform(post(String.format("/api/follow/%s", "testid"))) + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:member:testid"); + + mockMvc.perform(post("/api/follow") + .param("action", "follow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -136,7 +188,14 @@ public Follow createOrLoadFollow(Member follower, Member following) { public void ์ž๊ธฐ_์ž์‹ ์„_์–ธํŒ”๋กœ์šฐ_ํ• ๋–„() throws Exception { createOrLoadMember("testid", "ROLE_CURATOR"); - mockMvc.perform(delete(String.format("/api/follow/%s", "testid"))) + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:member:testid"); + + mockMvc.perform(post("/api/follow") + .param("action", "unfollow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -148,7 +207,14 @@ public Follow createOrLoadFollow(Member follower, Member following) { createOrLoadMember(); Member followUser = createOrLoadMember("followUser", "ROLE_USER"); - mockMvc.perform(delete(String.format("/api/follow/" + followUser.getUsername()))) + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:member:" + followUser.getUsername()); + + mockMvc.perform(post("/api/follow") + .param("action", "unfollow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -158,14 +224,146 @@ public Follow createOrLoadFollow(Member follower, Member following) { @Test public void ์–ธํŒ”๋กœ์šฐ_์„ฑ๊ณต() throws Exception { createOrLoadMember(); - Member followUser = createOrLoadMember("followUser", "ROLE_USER"); + Member followUser = createOrLoadMember("unfollowUser", "ROLE_USER"); createOrLoadFollow(createOrLoadMember(), followUser); - mockMvc.perform(delete(String.format("/api/follow/" + followUser.getUsername()))) - .andExpect(status().isNoContent()); + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:member:" + followUser.getUsername()); + + mockMvc.perform(post("/api/follow") + .param("action", "unfollow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andExpect(status().isOk()); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ํŒ”๋กœ์ž‰ ์‹œ") + @Test + public void ํŒ€_ํŒ”๋กœ์ž‰() throws Exception { + createOrLoadMember(); + + Team team = createOrLoadTeam(); + + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:team:" + team.getId()); + + mockMvc.perform(post("/api/follow") + .param("action", "follow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isCreated()); } + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ์–ธํŒ”๋กœ์šฐ ์‹œ") + @Test + public void ํŒ€_์–ธํŒ”๋กœ์ž‰() throws Exception { + Member member = createOrLoadMember(); + Team team = createOrLoadTeam(); + + createOrLoadFollow(member,team); + + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:team:" + team.getId()); + + mockMvc.perform(post("/api/follow") + .param("action", "unfollow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isOk()); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ”๋กœ์šฐ API๋ฅผ ์—ฐ์†์œผ๋กœ ํ˜ธ์ถœํ•  ์‹œ") + @Test + public void ํŒ”๋กœ์šฐAPI_์—ฐ์†ํ˜ธ์ถœ() throws Exception { + // given + Member member = createOrLoadMember(); + Member followUser = createOrLoadMember("followUser", "ROLE_USER"); + + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:member:" + followUser.getUsername()); + + mockMvc.perform(post("/api/follow") + .param("action", "follow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isCreated()); + + // when + String response = mockMvc.perform(post("/api/follow") + .param("action", "follow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + // then + assertTrue(response.contains("์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("์ž˜๋ชป๋œ URN ํ˜•์‹์œผ๋กœ ์š”์ฒญ ์‹œ") + @Test + public void ์ž˜๋ชป๋œ_URN() throws Exception { + // given + Member member = createOrLoadMember(); + Team team = createOrLoadTeam(); + createOrLoadFollow(member,team); + + FollowRequest.Create request = new FollowRequest.Create(); + + // when1 -> urn:teamdd:1 + request.setUrn("urn:teamdd:" + team.getId()); + String response1 = mockMvc.perform(post("/api/follow") + .param("action", "unfollow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertTrue(response1.contains("์œ ํšจํ•˜์ง€ ์•Š์€ URN ์ž…๋‹ˆ๋‹ค.")); + + // when2 -> asdsd + request.setUrn("aadsd"); + String response2 = mockMvc.perform(post("/api/follow") + .param("action", "unfollow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + assertTrue(response2.contains("์œ ํšจํ•˜์ง€ ์•Š์€ URN ์ž…๋‹ˆ๋‹ค.")); + + // when3 -> urn::asd + request.setUrn("urn::asd"); + String response3 = mockMvc.perform(post("/api/follow") + .param("action", "unfollow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + assertTrue(response3.contains("์œ ํšจํ•˜์ง€ ์•Š์€ URN ์ž…๋‹ˆ๋‹ค.")); + } + + + @DisplayName("๋น„ ๋กœ๊ทธ์ธ ์ƒํƒœ๋กœ ํŒ”๋กœ์ž‰ ๋ชฉ๋ก ์กฐํšŒ์‹œ") @Test public void ๋น„๋กœ๊ทธ์ธ_ํŒ”๋กœ์ž‰_๋ชฉ๋ก_์กฐํšŒ() throws Exception { @@ -175,13 +373,14 @@ public Follow createOrLoadFollow(Member follower, Member following) { Member followUser3 = createOrLoadMember("followUser3", "ROLE_USER"); Member followUser4 = createOrLoadMember("followUser4", "ROLE_USER"); Member followUser5 = createOrLoadMember("followUser5", "ROLE_USER"); + Team team = createOrLoadTeam(); createOrLoadFollow(member, followUser4); createOrLoadFollow(member, followUser); createOrLoadFollow(member, followUser2); createOrLoadFollow(member, followUser3); createOrLoadFollow(member, followUser5); - + createOrLoadFollow(member, team); mockMvc.perform(get(String.format("/api/follow/%s/following", member.getUsername()))) .andDo(print()) @@ -208,6 +407,7 @@ public Follow createOrLoadFollow(Member follower, Member following) { Member followUser11 = createOrLoadMember("followUser11", "ROLE_USER"); Member followUser12 = createOrLoadMember("followUser12", "ROLE_USER"); Member followUser13 = createOrLoadMember("followUser13", "ROLE_USER"); + Team team = createOrLoadTeam(); createOrLoadFollow(member, followUser4); createOrLoadFollow(member, followUser1); @@ -228,6 +428,7 @@ public Follow createOrLoadFollow(Member follower, Member following) { createOrLoadFollow(loginUser, followUser1); createOrLoadFollow(loginUser, followUser4); createOrLoadFollow(loginUser, followUser6); + createOrLoadFollow(loginUser, team); mockMvc.perform(get(String.format("/api/follow/%s/following", member.getUsername()))) .andDo(print()) From 849aece9f590d200cc09f5c77bf9d8cd2666cef8 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 17:23:58 +0900 Subject: [PATCH 050/103] =?UTF-8?q?feat:=20=ED=8C=80=20=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20DTO=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FollowRepository ํŒ”๋กœ์ž‰ ์กฐํšŒ ์‹œ Team๋„ ํŒ”๋กœ์ž‰ ์ค‘์ธ์ง€ ์—ฌ๋ถ€ ์ฒดํฌ๋ฅผ ์œ„ํ•œ JPQL ์ˆ˜์ • - ์กฐํšŒ ํ›„ ํŒ”๋กœ์ž‰ ๋Œ€์ƒ์ด ์‚ฌ์šฉ์ž, ํŒ€์— ๋”ฐ๋ผ DTO ํŒŒ์‹ฑ ๋ถ„๊ธฐ - FollowDetailResponseDTO ํ•„๋“œ๋ฅผ ๊ณตํ†ต์  ์˜๋ฏธ๋ฅผ ๋“œ๋Ÿฌ๋‚˜๋„๋ก ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ช… ์ˆ˜์ • --- .../codebase/controller/FollowController.java | 12 ++--- .../follow/dto/FollowDetailResponseDTO.java | 48 +++++++++++++++++++ .../dto/FollowMemberDetailResponseDTO.java | 33 ------------- .../follow/dto/FollowMembersResponseDTO.java | 25 ---------- .../domain/follow/dto/FollowResponseDTO.java | 25 ++++++++++ .../follow/repository/FollowRepository.java | 8 ++-- .../domain/follow/service/FollowService.java | 39 ++++++++------- 7 files changed, 102 insertions(+), 88 deletions(-) create mode 100644 src/main/java/com/example/codebase/domain/follow/dto/FollowDetailResponseDTO.java delete mode 100644 src/main/java/com/example/codebase/domain/follow/dto/FollowMemberDetailResponseDTO.java delete mode 100644 src/main/java/com/example/codebase/domain/follow/dto/FollowMembersResponseDTO.java create mode 100644 src/main/java/com/example/codebase/domain/follow/dto/FollowResponseDTO.java diff --git a/src/main/java/com/example/codebase/controller/FollowController.java b/src/main/java/com/example/codebase/controller/FollowController.java index 144e779a..11460c47 100644 --- a/src/main/java/com/example/codebase/controller/FollowController.java +++ b/src/main/java/com/example/codebase/controller/FollowController.java @@ -1,6 +1,6 @@ package com.example.codebase.controller; -import com.example.codebase.domain.follow.dto.FollowMembersResponseDTO; +import com.example.codebase.domain.follow.dto.FollowResponseDTO; import com.example.codebase.domain.follow.dto.FollowRequest; import com.example.codebase.domain.follow.service.FollowService; import com.example.codebase.domain.notification.entity.NotificationType; @@ -10,8 +10,6 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.PositiveOrZero; import org.springframework.beans.factory.annotation.Autowired; @@ -89,27 +87,27 @@ private void checkSameMember(String username1, String username2, String message) } } - @Operation(summary = "ํŒ”๋กœ์ž‰", description = "ํ•ด๋‹น ์œ ์ €๊ฐ€ ํŒ”๋กœ์ž‰ ํ•˜๋Š” ์‚ฌ๋žŒ ๋ชฉ๋ก์„ ์กฐํšŒ ํ•ฉ๋‹ˆ๋‹ค") + @Operation(summary = "ํŒ”๋กœ์ž‰ ์กฐํšŒ", description = "ํ•ด๋‹น ์œ ์ €๊ฐ€ ํŒ”๋กœ์ž‰ ํ•˜๋Š” ์‚ฌ๋žŒ ๋ชฉ๋ก์„ ์กฐํšŒ ํ•ฉ๋‹ˆ๋‹ค") @GetMapping("/{username}/following") public ResponseEntity getFollowingList(@PathVariable("username") String username, @PositiveOrZero @RequestParam(defaultValue = "0") int page, @PositiveOrZero @RequestParam(defaultValue = "10") int size) { Optional loginUsername = SecurityUtil.getCurrentUsername(); PageRequest pageRequest = PageRequest.of(page, size); - FollowMembersResponseDTO followingListResponse = followService.getFollowingList(loginUsername, username, pageRequest); + FollowResponseDTO followingListResponse = followService.getFollowingList(loginUsername, username, pageRequest); return new ResponseEntity(followingListResponse, HttpStatus.OK); } - @Operation(summary = "ํŒ”๋กœ์›Œ", description = "ํ•ด๋‹น ์œ ์ €๋ฅผ ํŒ”๋กœ์›Œ ํ•˜๋Š” ์‚ฌ๋žŒ ๋ชฉ๋ก์„ ์กฐํšŒ ํ•ฉ๋‹ˆ๋‹ค") + @Operation(summary = "ํŒ”๋กœ์›Œ ์กฐํšŒ", description = "ํ•ด๋‹น ์œ ์ €๋ฅผ ํŒ”๋กœ์›Œ ํ•˜๋Š” ์‚ฌ๋žŒ ๋ชฉ๋ก์„ ์กฐํšŒ ํ•ฉ๋‹ˆ๋‹ค") @GetMapping("{username}/follower") public ResponseEntity getFollowerList(@PathVariable("username") String username, @PositiveOrZero @RequestParam(defaultValue = "0") int page, @PositiveOrZero @RequestParam(defaultValue = "10") int size) { Optional loginUsername = SecurityUtil.getCurrentUsername(); PageRequest pageRequest = PageRequest.of(page, size); - FollowMembersResponseDTO followingListResponse = followService.getFollowerList(loginUsername, username, pageRequest); + FollowResponseDTO followingListResponse = followService.getFollowerList(loginUsername, username, pageRequest); return new ResponseEntity(followingListResponse, HttpStatus.OK); } diff --git a/src/main/java/com/example/codebase/domain/follow/dto/FollowDetailResponseDTO.java b/src/main/java/com/example/codebase/domain/follow/dto/FollowDetailResponseDTO.java new file mode 100644 index 00000000..b1486a13 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/follow/dto/FollowDetailResponseDTO.java @@ -0,0 +1,48 @@ +package com.example.codebase.domain.follow.dto; + +import com.example.codebase.domain.follow.entity.Follow; +import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.member.entity.RoleStatus; +import com.example.codebase.domain.team.entity.Team; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@Builder +public class FollowDetailResponseDTO { + + private String id; + private String name; + private String profileImage; + private String introduction; + private String entityType; + private String followStatus; + + public static FollowDetailResponseDTO of(Member member, String status){ + return FollowDetailResponseDTO.builder() + .id(member.getId().toString()) + .name(member.getUsername()) + .profileImage(member.getPicture()) + .introduction(member.getIntroduction()) + .entityType("member") + .followStatus(status) + .build(); + } + + public static FollowDetailResponseDTO of(Team team, String status){ + String teamDescription = team.getDescription().length() > 25 ? team.getDescription().substring(0, 25) + "..." : team.getDescription(); + return FollowDetailResponseDTO.builder() + .id(team.getId().toString()) + .name(team.getName()) + .profileImage(team.getProfileImage()) + .introduction(teamDescription) + .entityType("team") + .followStatus(status) + .build(); + } + +} diff --git a/src/main/java/com/example/codebase/domain/follow/dto/FollowMemberDetailResponseDTO.java b/src/main/java/com/example/codebase/domain/follow/dto/FollowMemberDetailResponseDTO.java deleted file mode 100644 index de5b197e..00000000 --- a/src/main/java/com/example/codebase/domain/follow/dto/FollowMemberDetailResponseDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.example.codebase.domain.follow.dto; - -import com.example.codebase.domain.member.entity.Member; -import com.example.codebase.domain.member.entity.RoleStatus; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@AllArgsConstructor -@Builder -public class FollowMemberDetailResponseDTO { - - private String userId; - private String username; - private String profileImage; - private String introduction; - private RoleStatus roleStatus; - private String status; - - public static FollowMemberDetailResponseDTO of(Member member, String status){ - return FollowMemberDetailResponseDTO.builder() - .userId(member.getId().toString()) - .username(member.getUsername()) - .profileImage(member.getPicture()) - .introduction(member.getIntroduction()) - .roleStatus(member.getRoleStatus()) - .status(status) - .build(); - } -} diff --git a/src/main/java/com/example/codebase/domain/follow/dto/FollowMembersResponseDTO.java b/src/main/java/com/example/codebase/domain/follow/dto/FollowMembersResponseDTO.java deleted file mode 100644 index d2f0149c..00000000 --- a/src/main/java/com/example/codebase/domain/follow/dto/FollowMembersResponseDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.example.codebase.domain.follow.dto; - -import com.example.codebase.controller.dto.PageInfo; -import lombok.*; - -import java.util.List; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class FollowMembersResponseDTO { - - private List followingList; - - private PageInfo pageInfo; - - public static FollowMembersResponseDTO of(List followMemberDetailResponseDTO, PageInfo pageInfo ) { - return FollowMembersResponseDTO.builder() - .followingList(followMemberDetailResponseDTO) - .pageInfo(pageInfo) - .build(); - } -} diff --git a/src/main/java/com/example/codebase/domain/follow/dto/FollowResponseDTO.java b/src/main/java/com/example/codebase/domain/follow/dto/FollowResponseDTO.java new file mode 100644 index 00000000..8a9e5887 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/follow/dto/FollowResponseDTO.java @@ -0,0 +1,25 @@ +package com.example.codebase.domain.follow.dto; + +import com.example.codebase.controller.dto.PageInfo; +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class FollowResponseDTO { + + private List follows; + + private PageInfo pageInfo; + + public static FollowResponseDTO of(List followDetailResponseDTO, PageInfo pageInfo ) { + return FollowResponseDTO.builder() + .follows(followDetailResponseDTO) + .pageInfo(pageInfo) + .build(); + } +} diff --git a/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java b/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java index 6077b2f5..4784027f 100644 --- a/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java +++ b/src/main/java/com/example/codebase/domain/follow/repository/FollowRepository.java @@ -15,17 +15,15 @@ public interface FollowRepository extends JpaRepository { @Query("SELECT f AS follow, " + - "CASE WHEN f.followingMember = :loginMember THEN 'self' " + - "WHEN f2.follower = :loginMember THEN 'follow' ELSE 'none' END as status " + - "FROM Follow f LEFT JOIN Follow f2 ON f.followingMember = f2.followingMember " + - "AND f2.follower = :loginMember " + + "CASE WHEN f.followingMember = :loginMember THEN 'self' " + + "WHEN f2.follower = :loginMember THEN 'follow' ELSE 'none' END as status " + + "FROM Follow f LEFT JOIN Follow f2 ON (f.followingMember = f2.followingMember OR f.followingTeam = f2.followingTeam) AND f2.follower = :loginMember " + "WHERE f.follower = :targetMember " + "ORDER BY CASE WHEN f.followingMember = :loginMember THEN 1 " + "WHEN f2.follower = :loginMember THEN 2 ELSE 3 END, " + "f.followTime ASC") Page findFollowingByTargetMember(Member targetMember, Member loginMember, PageRequest pageRequest); - @Query("SELECT f AS follow, " + "CASE WHEN f.follower = :loginMember THEN 'self' " + "WHEN f2.follower = :loginMember THEN 'follow' ELSE 'none' END as status " + diff --git a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java index 6ea3e2d2..06fe0d20 100644 --- a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java +++ b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java @@ -1,8 +1,8 @@ package com.example.codebase.domain.follow.service; import com.example.codebase.controller.dto.PageInfo; -import com.example.codebase.domain.follow.dto.FollowMemberDetailResponseDTO; -import com.example.codebase.domain.follow.dto.FollowMembersResponseDTO; +import com.example.codebase.domain.follow.dto.FollowDetailResponseDTO; +import com.example.codebase.domain.follow.dto.FollowResponseDTO; import com.example.codebase.domain.follow.entity.Follow; import com.example.codebase.domain.follow.entity.FollowWithIsFollow; import com.example.codebase.domain.follow.repository.FollowRepository; @@ -102,45 +102,48 @@ public void unfollowTeam(String username, String teamId) { // TODO : AOP ๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ private void checkDuplicatedRequest(String follower, String following) { - if(redisUtil.getData(follower + "->" + following).isPresent()) { + if (redisUtil.getData(follower + "->" + following).isPresent()) { throw new DuplicatedRequestException(); } redisUtil.setDataAndExpire(follower + "->" + following, String.valueOf(LocalDateTime.now()), 3000); } - - public FollowMembersResponseDTO getFollowingList(Optional loginUsername, String targetUsername, PageRequest pageRequest) { + public FollowResponseDTO getFollowingList(Optional loginUsername, String targetUsername, PageRequest pageRequest) { Member targetMember = memberRepository.findByUsername(targetUsername).orElseThrow(NotFoundMemberException::new); Member loginMember = loginUsername.map(s -> memberRepository.findByUsername(s) - .orElseThrow(NotFoundMemberException::new)) - .orElse(null); + .orElseThrow(NotFoundMemberException::new)).orElse(null); Page followingList = followRepository.findFollowingByTargetMember(targetMember, loginMember, pageRequest); PageInfo pageInfo = PageInfo.from(followingList); - List followingMemberResponses = followingList.getContent().stream() - .map(followWithIsFollow -> - FollowMemberDetailResponseDTO.of(followWithIsFollow.getFollow().getFollowingMember(), followWithIsFollow.getStatus())) + List followingMemberResponses = followingList.getContent().stream() + .map(this::getFollowMemberDetailResponseDTO) .toList(); - return FollowMembersResponseDTO.of(followingMemberResponses, pageInfo); + return FollowResponseDTO.of(followingMemberResponses, pageInfo); } - public FollowMembersResponseDTO getFollowerList(Optional loginUsername, String targetUsername, PageRequest pageRequest) { + public FollowResponseDTO getFollowerList(Optional loginUsername, String targetUsername, PageRequest pageRequest) { Member targetMember = memberRepository.findByUsername(targetUsername).orElseThrow(NotFoundMemberException::new); Member loginMember = loginUsername.map(s -> memberRepository.findByUsername(s) - .orElseThrow(NotFoundMemberException::new)) - .orElse(null); + .orElseThrow(NotFoundMemberException::new)).orElse(null); Page followerList = followRepository.findFollowerByTargetMember(targetMember, loginMember, pageRequest); PageInfo pageInfo = PageInfo.from(followerList); - List followerMemberResponses = followerList.getContent().stream() - .map(followWithIsFollow -> - FollowMemberDetailResponseDTO.of(followWithIsFollow.getFollow().getFollower(), followWithIsFollow.getStatus())) + List followerMemberResponses = followerList.getContent().stream() + .map(this::getFollowMemberDetailResponseDTO) .toList(); - return FollowMembersResponseDTO.of(followerMemberResponses, pageInfo); + return FollowResponseDTO.of(followerMemberResponses, pageInfo); } + private FollowDetailResponseDTO getFollowMemberDetailResponseDTO(FollowWithIsFollow followWithIsFollow) { + Follow follow = followWithIsFollow.getFollow(); + // ํŒ”๋กœ์ž‰ ๋Œ€์ƒ์ด ์‚ฌ์šฉ์ž์ธ ๊ฒฝ์šฐ + if (follow.getFollowingMember() != null) { + return FollowDetailResponseDTO.of(follow.getFollowingMember(), followWithIsFollow.getStatus()); + } + return FollowDetailResponseDTO.of(follow.getFollowingTeam(), followWithIsFollow.getStatus()); + } } From 82b9b92abc077ec9617b3893fc2dbc9525d9d211 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 17:24:24 +0900 Subject: [PATCH 051/103] =?UTF-8?q?test:=20=ED=8C=80=20=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9E=89=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=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 --- .../controller/FollowControllerTest.java | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/FollowControllerTest.java b/src/test/java/com/example/codebase/controller/FollowControllerTest.java index bf78d76e..0ec76fa5 100644 --- a/src/test/java/com/example/codebase/controller/FollowControllerTest.java +++ b/src/test/java/com/example/codebase/controller/FollowControllerTest.java @@ -125,7 +125,7 @@ public Team createOrLoadTeam(String teamName) { Team dummy = Team.builder() .name(teamName) - .description("test") + .description("์šฐ๋ฆฌ ํšŒ์‚ฌ๋Š” ์ตœ๊ณ ์˜ ๊ตญ๋‚ด ๊ธฐ์—…์ด๊ณ  ํ˜„์žฌ๋Š” ์–ด์ฉŒ๊ตฌ ์ €์ฉŒ๊ตฌ ์—ฐ๋งค์ถœ์€ ์–ด๋–ป๊ณ  ๋ญ ๋ณต์ง€๋Š” ์ข‹๊ณ  ๋ง๊ณ ") .backgroundImage("test") .profileImage("test") .address("test") @@ -399,14 +399,6 @@ public Follow createOrLoadFollow(Member follower, Team following) { Member followUser3 = createOrLoadMember("followUser3", "ROLE_USER"); Member followUser4 = createOrLoadMember("followUser4", "ROLE_USER"); Member followUser5 = createOrLoadMember("followUser5", "ROLE_USER"); - Member followUser6 = createOrLoadMember("followUser6", "ROLE_USER"); - Member followUser7 = createOrLoadMember("followUser7", "ROLE_USER"); - Member followUser8 = createOrLoadMember("followUser8", "ROLE_USER"); - Member followUser9 = createOrLoadMember("followUser9", "ROLE_USER"); - Member followUser10 = createOrLoadMember("followUser10", "ROLE_USER"); - Member followUser11 = createOrLoadMember("followUser11", "ROLE_USER"); - Member followUser12 = createOrLoadMember("followUser12", "ROLE_USER"); - Member followUser13 = createOrLoadMember("followUser13", "ROLE_USER"); Team team = createOrLoadTeam(); createOrLoadFollow(member, followUser4); @@ -414,20 +406,39 @@ public Follow createOrLoadFollow(Member follower, Team following) { createOrLoadFollow(member, followUser2); createOrLoadFollow(member, followUser3); createOrLoadFollow(member, followUser5); - createOrLoadFollow(member, followUser6); - createOrLoadFollow(member, followUser7); - createOrLoadFollow(member, followUser8); - createOrLoadFollow(member, followUser9); - createOrLoadFollow(member, followUser10); - createOrLoadFollow(member, followUser11); - createOrLoadFollow(member, followUser12); - createOrLoadFollow(member, followUser13); + createOrLoadFollow(member, team); createOrLoadFollow(member, loginUser); createOrLoadFollow(loginUser, followUser1); createOrLoadFollow(loginUser, followUser4); - createOrLoadFollow(loginUser, followUser6); + createOrLoadFollow(loginUser, followUser5); + createOrLoadFollow(loginUser, team); + + mockMvc.perform(get(String.format("/api/follow/%s/following", member.getUsername()))) + .andDo(print()) + .andExpect(status().isOk()); + } + + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("๋กœ๊ทธ์ธ ์ƒํƒœ๋กœ ํŒ”๋กœ์ž‰ ๋ชฉ๋ก ์กฐํšŒ์‹œ - ํŒ€ ํŒ”๋กœ์ž‰ ์—ฌ๋ถ€") + @Test + public void ๋กœ๊ทธ์ธ_์ƒํƒœ๋กœ_ํŒ”๋กœ์ž‰_๋ชฉ๋ก_์กฐํšŒ2() throws Exception { + Member loginUser = createOrLoadMember(); + + Member member = createOrLoadMember("targetUser", "ROLE_USER"); + Member followUser1 = createOrLoadMember("followUser1", "ROLE_USER"); + Member followUser2 = createOrLoadMember("followUser2", "ROLE_USER"); + Team team = createOrLoadTeam(); + Team team2 = createOrLoadTeam("team2"); + + createOrLoadFollow(member, followUser1); + createOrLoadFollow(member, followUser2); + createOrLoadFollow(member, team); + createOrLoadFollow(member, team2); + createOrLoadFollow(member, loginUser); + + createOrLoadFollow(loginUser, followUser1); createOrLoadFollow(loginUser, team); mockMvc.perform(get(String.format("/api/follow/%s/following", member.getUsername()))) @@ -469,32 +480,21 @@ public Follow createOrLoadFollow(Member follower, Team following) { Member followUser3 = createOrLoadMember("followUser3", "ROLE_USER"); Member followUser4 = createOrLoadMember("followUser4", "ROLE_USER"); Member followUser5 = createOrLoadMember("followUser5", "ROLE_USER"); - Member followUser6 = createOrLoadMember("followUser6", "ROLE_USER"); - Member followUser7 = createOrLoadMember("followUser7", "ROLE_USER"); - Member followUser8 = createOrLoadMember("followUser8", "ROLE_USER"); - Member followUser9 = createOrLoadMember("followUser9", "ROLE_USER"); - Member followUser10 = createOrLoadMember("followUser10", "ROLE_USER"); - Member followUser11 = createOrLoadMember("followUser11", "ROLE_USER"); createOrLoadFollow(followUser4, member); createOrLoadFollow(followUser1, member); createOrLoadFollow(followUser2, member); createOrLoadFollow(followUser3, member); createOrLoadFollow(followUser5, member); - createOrLoadFollow(followUser6, member); - - createOrLoadFollow(followUser7, member); - createOrLoadFollow(followUser8, member); createOrLoadFollow(loginUser, followUser1); createOrLoadFollow(loginUser, followUser4); - createOrLoadFollow(loginUser, followUser6); createOrLoadFollow(loginUser, followUser2); createOrLoadFollow(loginUser, followUser3); - createOrLoadFollow(followUser9, member); - createOrLoadFollow(followUser10, member); - createOrLoadFollow(followUser11, member); + createOrLoadFollow(followUser4, member); + createOrLoadFollow(followUser2, member); + createOrLoadFollow(followUser3, member); createOrLoadFollow(loginUser, member); From 51e4b534a6556b2c0607b3380eb185e118f60630 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 17:35:48 +0900 Subject: [PATCH 052/103] =?UTF-8?q?feat:=20FollowEntityUrn=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EC=B2=98=EB=A6=AC=20=EB=A9=94=EC=8B=9C=EC=A7=80=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 - ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ˆ˜์ • ๋ฐ ์ž˜๋ชป๋œ Action์œผ๋กœ API ํ˜ธ์ถœ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ --- .../domain/follow/dto/FollowRequest.java | 15 +------- .../controller/FollowControllerTest.java | 37 +++++++++++++++++-- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java index f859f0ee..eb29afaf 100644 --- a/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java +++ b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java @@ -5,8 +5,6 @@ import lombok.Getter; import lombok.Setter; -import javax.annotation.RegEx; - public class FollowRequest { public enum FollowEntityUrn { @@ -24,9 +22,6 @@ public enum FollowEntityUrn { public static FollowEntityUrn from(String urn) { try { - if (!urn.startsWith("urn:")) { - throw new IllegalArgumentException(); - } String resource = urn.split(":")[1]; String id = urn.split(":")[2]; @@ -34,17 +29,9 @@ public static FollowEntityUrn from(String urn) { followEntityUrn.id = id; return followEntityUrn; } catch (IllegalArgumentException e) { - throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š์€ URN ์ž…๋‹ˆ๋‹ค."); + throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š์€ EntityUrn ์ž…๋‹ˆ๋‹ค."); } } - - public boolean isMember() { - return this == MEMBER; - } - - public boolean isTeam() { - return this == TEAM; - } } @Getter diff --git a/src/test/java/com/example/codebase/controller/FollowControllerTest.java b/src/test/java/com/example/codebase/controller/FollowControllerTest.java index 0ec76fa5..8f371ab5 100644 --- a/src/test/java/com/example/codebase/controller/FollowControllerTest.java +++ b/src/test/java/com/example/codebase/controller/FollowControllerTest.java @@ -333,7 +333,7 @@ public Follow createOrLoadFollow(Member follower, Team following) { .andDo(print()) .andExpect(status().isBadRequest()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - assertTrue(response1.contains("์œ ํšจํ•˜์ง€ ์•Š์€ URN ์ž…๋‹ˆ๋‹ค.")); + assertTrue(response1.contains("์œ ํšจํ•˜์ง€ ์•Š์€ EntityUrn ์ž…๋‹ˆ๋‹ค.")); // when2 -> asdsd request.setUrn("aadsd"); @@ -346,7 +346,7 @@ public Follow createOrLoadFollow(Member follower, Team following) { .andExpect(status().isBadRequest()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - assertTrue(response2.contains("์œ ํšจํ•˜์ง€ ์•Š์€ URN ์ž…๋‹ˆ๋‹ค.")); + assertTrue(response2.contains("์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")); // when3 -> urn::asd request.setUrn("urn::asd"); @@ -359,10 +359,41 @@ public Follow createOrLoadFollow(Member follower, Team following) { .andExpect(status().isBadRequest()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - assertTrue(response3.contains("์œ ํšจํ•˜์ง€ ์•Š์€ URN ์ž…๋‹ˆ๋‹ค.")); + assertTrue(response3.contains("์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")); } + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("์ž˜๋ชป๋œ Action์œผ๋กœ API ํ˜ธ์ถœ ์‹œ") + @Test + public void ์ž˜๋ชป๋œ_ACTION () throws Exception { + Member member = createOrLoadMember(); + Team team = createOrLoadTeam(); + + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:team:" + team.getId()); + // when1 + String response1 = mockMvc.perform(post("/api/follow") + .param("action", "cc") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertTrue(response1.contains("์ž˜๋ชป๋œ action ๊ฐ’์ž…๋‹ˆ๋‹ค.")); + + // when2 + String response2 = mockMvc.perform(post("/api/follow") + .param("action", "") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertTrue(response2.contains("์ž˜๋ชป๋œ action ๊ฐ’์ž…๋‹ˆ๋‹ค.")); + } @DisplayName("๋น„ ๋กœ๊ทธ์ธ ์ƒํƒœ๋กœ ํŒ”๋กœ์ž‰ ๋ชฉ๋ก ์กฐํšŒ์‹œ") @Test From 4e342c7c4adc1b69bdad2b6d66f5389c4a0b3c12 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 17:50:01 +0900 Subject: [PATCH 053/103] =?UTF-8?q?feat:=20=ED=8C=94=EB=A1=9C=EC=9A=B0=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ์ผ๋ถ€ ์ผ€์ด์Šค์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ - ํŒ”๋กœ์šฐ API ์— ๋Œ€ํ•ด ๊ฒฐ๊ณผ๊ฐ’๊นŒ์ง€ ๊ฒ€์ฆ (400 ์— ๋Œ€ํ•œ ์˜ˆ์™ธ์ผ€์ด์Šค๊ฐ€ ๋งŽ์œผ๋ฏ€๋กœ ๋ถ„๋ช…ํ•˜๊ฒŒ ๊ฒ€์ฆ) --- .../codebase/controller/FollowController.java | 2 +- .../domain/follow/service/FollowService.java | 8 +- .../controller/FollowControllerTest.java | 93 +++++++++++++++++-- 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/FollowController.java b/src/main/java/com/example/codebase/controller/FollowController.java index 11460c47..16d4f798 100644 --- a/src/main/java/com/example/codebase/controller/FollowController.java +++ b/src/main/java/com/example/codebase/controller/FollowController.java @@ -57,7 +57,7 @@ public ResponseEntity follow( private ResponseEntity follow(String username, FollowRequest.FollowEntityUrn entityUrn) { switch (entityUrn) { case MEMBER -> { - checkSameMember(username, entityUrn.getId(), "ํŒ”๋กœ์ž‰"); + checkSameMember(username, entityUrn.getId(), "ํŒ”๋กœ์šฐ"); followService.followMember(username, entityUrn.getId()); notificationService.send(username, entityUrn.getId(), NotificationType.NEW_FOLLOWER); } diff --git a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java index 06fe0d20..6a2eb4f3 100644 --- a/src/main/java/com/example/codebase/domain/follow/service/FollowService.java +++ b/src/main/java/com/example/codebase/domain/follow/service/FollowService.java @@ -52,7 +52,7 @@ public void followMember(String username, String followMember) { Member following = memberRepository.findByUsername(followMember).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.")); if (followRepository.existsByFollowerAndFollowingMember(follower, following)) { - throw new RuntimeException("์ด๋ฏธ ํŒ”๋กœ์ž‰ ์ค‘์ž…๋‹ˆ๋‹ค."); + throw new RuntimeException("์ด๋ฏธ ํŒ”๋กœ์šฐ ์ค‘์ž…๋‹ˆ๋‹ค."); } followRepository.save(Follow.of(follower, following)); @@ -67,7 +67,7 @@ public void unfollowMember(String username, String followMember) { Optional alreadyFollow = followRepository.findByFollowerAndFollowingMember(follower, following); if (alreadyFollow.isEmpty()) { - throw new RuntimeException("ํŒ”๋กœ์ž‰ ์ค‘์ด ์•„๋‹™๋‹ˆ๋‹ค."); + throw new RuntimeException("ํŒ”๋กœ์šฐ ์ค‘์ด ์•„๋‹™๋‹ˆ๋‹ค."); } followRepository.delete(alreadyFollow.get()); } @@ -80,7 +80,7 @@ public void followTeam(String username, String teamId) { Team team = teamRepository.findById(Long.valueOf(teamId)).orElseThrow(() -> new RuntimeException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํŒ€์ž…๋‹ˆ๋‹ค.")); if (followRepository.existsByFollowerAndFollowingTeam(follower, team)) { - throw new RuntimeException("์ด๋ฏธ ํŒ”๋กœ์ž‰ ์ค‘์ž…๋‹ˆ๋‹ค."); + throw new RuntimeException("์ด๋ฏธ ํŒ”๋กœ์šฐ ์ค‘์ž…๋‹ˆ๋‹ค."); } followRepository.save(Follow.of(follower, team)); @@ -95,7 +95,7 @@ public void unfollowTeam(String username, String teamId) { Optional alreadyFollow = followRepository.findByFollowerAndFollowingTeam(follower, team); if (alreadyFollow.isEmpty()) { - throw new RuntimeException("ํŒ”๋กœ์ž‰ ์ค‘์ด ์•„๋‹™๋‹ˆ๋‹ค."); + throw new RuntimeException("ํŒ”๋กœ์šฐ ์ค‘์ด ์•„๋‹™๋‹ˆ๋‹ค."); } followRepository.delete(alreadyFollow.get()); } diff --git a/src/test/java/com/example/codebase/controller/FollowControllerTest.java b/src/test/java/com/example/codebase/controller/FollowControllerTest.java index 8f371ab5..0d019965 100644 --- a/src/test/java/com/example/codebase/controller/FollowControllerTest.java +++ b/src/test/java/com/example/codebase/controller/FollowControllerTest.java @@ -164,40 +164,67 @@ public Follow createOrLoadFollow(Member follower, Team following) { .andExpect(status().isCreated()); } + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ”๋กœ์šฐ ์ค‘์ธ๋ฐ ๋‹ค์‹œ ํŒ”๋กœ์ž‰ ์‹œ") + @Test + public void ๋‹ค์‹œ_ํŒ”๋กœ์ž‰() throws Exception { + Member member = createOrLoadMember("testid", "ROLE_USER"); + Member member2 = createOrLoadMember("testid2", "ROLE_USER"); + createOrLoadFollow(member , member2); + + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:member:" + member2.getUsername()); + + String content = mockMvc.perform(post("/api/follow") + .param("action", "follow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertTrue(content.contains("์ด๋ฏธ ํŒ”๋กœ์šฐ ์ค‘์ž…๋‹ˆ๋‹ค.")); + } + + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("์ž๊ธฐ ์ž์‹ ์„ ํŒ”๋กœ์šฐ ํ• ์‹œ") @Test public void ์ž๊ธฐ_์ž์‹ ์„_ํŒ”๋กœ์šฐ_ํ• ๋–„() throws Exception { - createOrLoadMember("testid", "ROLE_CURATOR"); + createOrLoadMember("testid", "ROLE_USER"); FollowRequest.Create request = new FollowRequest.Create(); request.setUrn("urn:member:testid"); - mockMvc.perform(post("/api/follow") + String content = mockMvc.perform(post("/api/follow") .param("action", "follow") .contentType("application/json") .content(objectMapper.writeValueAsBytes(request)) ) .andDo(print()) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertTrue(content.contains("์ž๊ธฐ ์ž์‹ ์„ ํŒ”๋กœ์šฐ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค")); } @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("์ž๊ธฐ ์ž์‹ ์„ ์–ธ ํŒ”๋กœ์šฐ ํ• ์‹œ") @Test public void ์ž๊ธฐ_์ž์‹ ์„_์–ธํŒ”๋กœ์šฐ_ํ• ๋–„() throws Exception { - createOrLoadMember("testid", "ROLE_CURATOR"); + createOrLoadMember("testid", "ROLE_USER"); FollowRequest.Create request = new FollowRequest.Create(); request.setUrn("urn:member:testid"); - mockMvc.perform(post("/api/follow") + String content = mockMvc.perform(post("/api/follow") .param("action", "unfollow") .contentType("application/json") .content(objectMapper.writeValueAsBytes(request)) ) .andDo(print()) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertTrue(content.contains("์ž๊ธฐ ์ž์‹ ์„ ์–ธํŒ”๋กœ์šฐ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค")); } @WithMockCustomUser(username = "testid", role = "USER") @@ -205,18 +232,20 @@ public Follow createOrLoadFollow(Member follower, Team following) { @Test public void ์–ธํŒ”๋กœ์šฐ_์‹คํŒจ() throws Exception { createOrLoadMember(); - Member followUser = createOrLoadMember("followUser", "ROLE_USER"); + Member followUser = createOrLoadMember("followUserasda", "ROLE_USER"); FollowRequest.Create request = new FollowRequest.Create(); request.setUrn("urn:member:" + followUser.getUsername()); - mockMvc.perform(post("/api/follow") + String content = mockMvc.perform(post("/api/follow") .param("action", "unfollow") .contentType("application/json") .content(objectMapper.writeValueAsBytes(request)) ) .andDo(print()) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertTrue(content.contains("ํŒ”๋กœ์šฐ ์ค‘์ด ์•„๋‹™๋‹ˆ๋‹ค.")); } @WithMockCustomUser(username = "testid", role = "USER") @@ -259,6 +288,30 @@ public Follow createOrLoadFollow(Member follower, Team following) { .andExpect(status().isCreated()); } + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ํŒ”๋กœ์ž‰ ์ค‘์ธ๋ฐ ๋‹ค์‹œ ํŒ”๋กœ์ž‰ํ•  ์‹œ") + @Test + public void ํŒ€_ํŒ”๋กœ์šฐ_์‹คํŒจ() throws Exception { + Member member = createOrLoadMember(); + Team team = createOrLoadTeam(); + + createOrLoadFollow(member, team); + + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:team:" + team.getId()); + + String content = mockMvc.perform(post("/api/follow") + .param("action", "follow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertTrue(content.contains("์ด๋ฏธ ํŒ”๋กœ์šฐ ์ค‘์ž…๋‹ˆ๋‹ค.")); + } + + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("ํŒ€ ์–ธํŒ”๋กœ์šฐ ์‹œ") @Test @@ -280,6 +333,28 @@ public Follow createOrLoadFollow(Member follower, Team following) { .andExpect(status().isOk()); } + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ํŒ”๋กœ์šฐ ์ค‘์ด ์•„๋‹๋•Œ ์–ธํŒ”๋กœ์šฐ๋ฅผ ์‹œ๋„ํ• ์‹œ") + @Test + public void ํŒ€_์–ธํŒ”๋กœ์šฐ_์‹คํŒจ() throws Exception { + createOrLoadMember(); + Team team = createOrLoadTeam(); + + FollowRequest.Create request = new FollowRequest.Create(); + request.setUrn("urn:team:" + team.getId()); + + String content = mockMvc.perform(post("/api/follow") + .param("action", "unfollow") + .contentType("application/json") + .content(objectMapper.writeValueAsBytes(request)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertTrue(content.contains("ํŒ”๋กœ์šฐ ์ค‘์ด ์•„๋‹™๋‹ˆ๋‹ค.")); + } + + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("ํŒ”๋กœ์šฐ API๋ฅผ ์—ฐ์†์œผ๋กœ ํ˜ธ์ถœํ•  ์‹œ") @Test From 465343b29ea4b3012ed7a61213e018f7741c7a3a Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 2 Apr 2024 18:13:50 +0900 Subject: [PATCH 054/103] =?UTF-8?q?feat:=20=ED=8C=94=EB=A1=9C=EC=9A=B0=20A?= =?UTF-8?q?PI=20Swagger=20Metadata=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/controller/FollowController.java | 18 ++++++++++++++++-- .../domain/follow/dto/FollowRequest.java | 3 +++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/FollowController.java b/src/main/java/com/example/codebase/controller/FollowController.java index 16d4f798..b8fad5a7 100644 --- a/src/main/java/com/example/codebase/controller/FollowController.java +++ b/src/main/java/com/example/codebase/controller/FollowController.java @@ -1,13 +1,16 @@ package com.example.codebase.controller; -import com.example.codebase.domain.follow.dto.FollowResponseDTO; import com.example.codebase.domain.follow.dto.FollowRequest; +import com.example.codebase.domain.follow.dto.FollowResponseDTO; import com.example.codebase.domain.follow.service.FollowService; import com.example.codebase.domain.notification.entity.NotificationType; import com.example.codebase.domain.notification.service.NotificationSendService; import com.example.codebase.exception.LoginRequiredException; import com.example.codebase.util.SecurityUtil; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.Pattern; @@ -38,7 +41,18 @@ public FollowController(FollowService followService, NotificationSendService not this.notificationService = notificationService; } - @Operation(summary = "ํŒ”๋กœ์šฐ/์–ธํŒ”๋กœ์šฐ", description = "์ƒ๋Œ€๋ฐฉ์„ ํŒ”๋กœ์šฐ/์–ธํŒ”๋กœ์šฐ ํ•ฉ๋‹ˆ๋‹ค.") + @Operation(summary = "ํŒ”๋กœ์šฐ/์–ธํŒ”๋กœ์šฐ", description = "์ƒ๋Œ€๋ฐฉ์„ ํŒ”๋กœ์šฐ/์–ธํŒ”๋กœ์šฐ ํ•ฉ๋‹ˆ๋‹ค.", + parameters = @Parameter(name = "action", description = "follow: ํŒ”๋กœ์šฐ, unfollow: ์–ธํŒ”๋กœ์šฐ", required = true, example = "follow"), + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "ํŒ”๋กœ์šฐํ•  ๋Œ€์ƒ URN", required = true, content = @io.swagger.v3.oas.annotations.media.Content( + schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = FollowRequest.Create.class), + examples = @io.swagger.v3.oas.annotations.media.ExampleObject(value = "{\"urn\": \"urn:member:admin\"}")) + ) + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "ํŒ”๋กœ์šฐ ์„ฑ๊ณต"), + @ApiResponse(responseCode = "200", description = "์–ธํŒ”๋กœ์šฐ ์„ฑ๊ณต") + }) @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER', 'ROLE_ADMIN')") @PostMapping public ResponseEntity follow( diff --git a/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java index eb29afaf..fc7c464d 100644 --- a/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java +++ b/src/main/java/com/example/codebase/domain/follow/dto/FollowRequest.java @@ -1,5 +1,6 @@ package com.example.codebase.domain.follow.dto; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; import lombok.Getter; @@ -7,6 +8,7 @@ public class FollowRequest { + @Schema(name = "FollowRequest.FollowEntityUrn", description = "FollowEntityUrn Request") public enum FollowEntityUrn { MEMBER("urn:member"), TEAM("urn:team"); @@ -36,6 +38,7 @@ public static FollowEntityUrn from(String urn) { @Getter @Setter + @Schema(name = "FollowRequest.Create", description = "FollowRequest Create Request") public static class Create { @NotBlank(message = "URN์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.") From ab638fb0a972dda2df30aa36ed1674552a52b098 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 7 Apr 2024 22:40:47 +0900 Subject: [PATCH 055/103] =?UTF-8?q?feat:=20sql=EB=AC=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20-=20magazine=EC=97=90=20team=5Fid=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EC=99=B8=EB=9E=98=ED=82=A4=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 --- .../resources/db/migration/V202403051158__application.sql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/resources/db/migration/V202403051158__application.sql diff --git a/src/main/resources/db/migration/V202403051158__application.sql b/src/main/resources/db/migration/V202403051158__application.sql new file mode 100644 index 00000000..b367d399 --- /dev/null +++ b/src/main/resources/db/migration/V202403051158__application.sql @@ -0,0 +1,8 @@ +# ๋งค๊ฑฐ์ง„ ํ…Œ์ด๋ธ”์— teami๏ฟฝ coulum ์ถ”๊ฐ€ +# ์ž‘์„ฑ์ž : gimdonghyeon (haroya01@naver.com) +# ์ž‘์„ฑ ๋‚ ์งœ : 2024-04-04 +# ํ˜„์žฌ ๋ฒ„์ „ : V202403051158 (์ด์ „ ๋ฒ„์ „ : V202403051157__application.sql) + +ALTER TABLE `magazine` + ADD `team_id` BIGINT NULL, + ADD CONSTRAINT `fk_magazine_team_id` FOREIGN KEY (`team_id`) REFERENCES `team` (`team_id`); From 603809b034706c1bde868a793f5c18b692fbb5f4 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 7 Apr 2024 22:42:06 +0900 Subject: [PATCH 056/103] =?UTF-8?q?feat:=20team=5Fid=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20jpa=20=EB=A7=A4=ED=95=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/magazine/entity/Magazine.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java b/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java index 624bf3d8..b8868fd2 100644 --- a/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java +++ b/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java @@ -2,6 +2,8 @@ import com.example.codebase.domain.magazine.dto.MagazineRequest; import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.entity.TeamUser; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -88,6 +90,10 @@ public class Magazine { @OneToMany(mappedBy = "magazine", cascade = CascadeType.ALL) private List magazineMedias = new ArrayList<>(); + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( name = "team_id") + private Team team; + public static Magazine toEntity(MagazineRequest.Create magazineRequest, Member member, MagazineCategory category) { return Magazine.builder() .title(magazineRequest.getTitle()) @@ -100,6 +106,20 @@ public static Magazine toEntity(MagazineRequest.Create magazineRequest, Member m .build(); } + public static Magazine toEntity(MagazineRequest.Create magazineRequest, TeamUser teamUser, MagazineCategory category){ + return Magazine.builder() + .title(magazineRequest.getTitle()) + .content(magazineRequest.getContent()) + .metadata(magazineRequest.getMetadata()) + .team(teamUser.getTeam()) + .member(teamUser.getMember()) + .category(category) + .createdTime(LocalDateTime.now()) + .updatedTime(LocalDateTime.now()) + .build(); + } + + public boolean isOwner(String loginUsername) { return member.getUsername().equals(loginUsername); } From c8a4d4877f6459651e3deba04b25621532c87b31 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 7 Apr 2024 22:43:33 +0900 Subject: [PATCH 057/103] =?UTF-8?q?feat:=20MagazineRequest.Create=20dto?= =?UTF-8?q?=EC=97=90=20urn=EA=B0=92=EC=9D=84=20=EB=B0=9B=EC=9D=84=EC=88=98?= =?UTF-8?q?=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/magazine/dto/MagazineRequest.java | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java index 6f0d5aed..b5d58747 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java @@ -1,9 +1,7 @@ package com.example.codebase.domain.magazine.dto; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; +import jakarta.validation.constraints.*; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.URL; @@ -34,6 +32,11 @@ public static class Create { @Size(max = 10, message = "์ตœ๋Œ€ 10๊ฐœ ๊นŒ์ง€ ๋ฏธ๋””์–ด ์ฒจ๋ถ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.") private List<@URL(message = "์˜ฌ๋ฐ”๋ฅธ URL ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.") String> mediaUrls = Collections.emptyList(); + + @NotBlank(message = "URN์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.") + @Pattern(regexp = "^urn:(member|team:\\w+)$", message = "์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.") + private String urn; + } @Getter @@ -54,4 +57,42 @@ public static class Update { private List mediaUrls; } + + @Schema(name = "MagazineRequest.MagazineEntityUrn", description = "MagazineEntityUrn Request") + public enum MagazineEntityUrn { + MEMBER("urn:member"), + TEAM("urn:team"); + + private final String resource; + + @Getter String id; + + MagazineEntityUrn(String resource){ + this.resource = resource; + this.id = null; + } + + public static MagazineEntityUrn from (String urn){ + try { + String[] checkSplit = urn.split(":"); + + if (checkSplit.length == 2) { + return MagazineEntityUrn.valueOf(checkSplit[1].toUpperCase()); + } else if (checkSplit.length == 3) { + String resource = checkSplit[1]; + String id = checkSplit[2]; + + MagazineEntityUrn magazineEntityUrn = MagazineEntityUrn.valueOf(resource.toUpperCase()); + magazineEntityUrn.id = id; + return magazineEntityUrn; + } + else{ + throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š์€ EntityUrn ์ž…๋‹ˆ๋‹ค."); + } + } + catch (IllegalArgumentException e){ + throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š์€ EntityUrn ์ž…๋‹ˆ๋‹ค."); + } + } + } } From 3363ec11128dfde9c3d0137e5ec7371167a32cce Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 7 Apr 2024 22:46:34 +0900 Subject: [PATCH 058/103] =?UTF-8?q?feat:=20=EB=A7=A4=EA=B1=B0=EC=A7=84=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20api=EC=97=90=20=ED=8C=80=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80=20-=20=EB=A7=A4=EA=B1=B0?= =?UTF-8?q?=EC=A7=84=EC=9D=84=20=EB=A7=8C=EB=93=A4=EB=96=84=20=ED=8C=80?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=A7=8C=EB=93=A4=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=A7=A4=EA=B1=B0=EC=A7=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20api=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=97=80=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.=20-=20=EB=A7=A4=EA=B1=B0=EC=A7=84=EC=9D=84?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=ED=95=A0=EB=95=8C=20=ED=8C=80=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=EB=8F=84=20=EC=A1=B0=ED=9A=8C=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=EB=A7=A4=EA=B1=B0=EC=A7=84=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20api=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=96=88?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MagazineController.java | 106 +++++++++++++----- .../domain/magazine/dto/MagazineResponse.java | 10 ++ .../repository/MagazineRepository.java | 3 + .../magazine/service/MagazineService.java | 33 ++++-- .../domain/team/service/TeamService.java | 5 + 5 files changed, 122 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/MagazineController.java b/src/main/java/com/example/codebase/controller/MagazineController.java index 2edbe262..f9c664c6 100644 --- a/src/main/java/com/example/codebase/controller/MagazineController.java +++ b/src/main/java/com/example/codebase/controller/MagazineController.java @@ -1,7 +1,9 @@ package com.example.codebase.controller; +import com.example.codebase.annotation.CheckDuplicatedRequest; import com.example.codebase.annotation.LoginOnly; import com.example.codebase.annotation.UserOnly; +import com.example.codebase.domain.follow.dto.FollowRequest; import com.example.codebase.domain.magazine.dto.MagazineCommentRequest; import com.example.codebase.domain.magazine.dto.MagazineRequest; import com.example.codebase.domain.magazine.dto.MagazineResponse; @@ -10,11 +12,19 @@ import com.example.codebase.domain.magazine.service.MagazineService; import com.example.codebase.domain.member.entity.Member; import com.example.codebase.domain.member.service.MemberService; +import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.service.TeamService; +import com.example.codebase.domain.team.service.TeamUserService; import com.example.codebase.exception.LoginRequiredException; import com.example.codebase.util.SecurityUtil; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.PositiveOrZero; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; @@ -23,6 +33,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.Optional; + @Tag(name = "๋งค๊ฑฐ์ง„ API", description = "๋งค๊ฑฐ์ง„ ๊ด€๋ จ API") @RestController @RequestMapping("/api/magazines") @@ -34,32 +46,69 @@ public class MagazineController { private final MemberService memberService; + private final TeamUserService teamUserService; + + private final TeamService teamService; + + @Autowired - public MagazineController(MagazineService magazineService, MagazineCategoryService magazineCategoryService, MemberService memberService) { + public MagazineController(MagazineService magazineService, MagazineCategoryService magazineCategoryService, MemberService memberService, + TeamUserService teamUserService, TeamService teamService) { this.magazineService = magazineService; this.magazineCategoryService = magazineCategoryService; this.memberService = memberService; + this.teamUserService = teamUserService; + this.teamService = teamService; } - @Operation(summary = "๋งค๊ฑฐ์ง„ ์ƒ์„ฑ", description = "๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋งŒ ๋งค๊ฑฐ์ง„์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ๋ฏธ๋””์–ด ์ฒจ๋ถ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ JSON ํ˜•์‹์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.") - @PostMapping + @Operation(summary = "๋งค๊ฑฐ์ง„ ์ƒ์„ฑ", description = "๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋งŒ ๋งค๊ฑฐ์ง„์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ๋ฏธ๋””์–ด ์ฒจ๋ถ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ JSON ํ˜•์‹์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.", + parameters = @Parameter(name = "action", description = "member: ๊ฐœ์ธ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ, team: ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ", required = true, example = "team"), + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์ž URN", required = true, content = @io.swagger.v3.oas.annotations.media.Content( + schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = FollowRequest.Create.class), + examples = @io.swagger.v3.oas.annotations.media.ExampleObject(value = + "{\"title\": \"๋งค๊ฑฐ์ง„ ์ œ๋ชฉ\"," + + "\"content\": \"๋งค๊ฑฐ์ง„ ๋‚ด์šฉ\", " + + "\"categorySlug\": \"์นดํ…Œ๊ณ ๋ฆฌ ์Šฌ๋Ÿฌ๊ทธ\"," + + " \"metadata\": {\"key\": \"jsonํ˜•์‹์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ\"}," + + " \"mediaUrls\": [\"์ด๋ฏธ์ง€ url ์ตœ๋Œ€ 10๊ฐœ\", " + "\"http://example.com/image1.jpg\"], " + + "\"urn\": \"urn:team:id\"}")) + ) + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "๋งค๊ฑฐ์ง„ ์ž‘์„ฑ ์„ฑ๊ณต"), + @ApiResponse(responseCode = "200", description = "๋งค๊ฑฐ์ง„ ์ž‘์„ฑ ์‹คํŒจ") + }) @UserOnly + @PostMapping + @CheckDuplicatedRequest(target = "magazine") public ResponseEntity createMagazine( - @RequestBody @Valid MagazineRequest.Create magazineRequest - ) { + @RequestBody @Valid MagazineRequest.Create magazineRequest) { String loginUsername = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); - // ์—ฐ๊ด€ ๊ฐ์ฒด ์กฐํšŒ - Member member = memberService.getEntity(loginUsername); + MagazineRequest.MagazineEntityUrn entityUrn = MagazineRequest.MagazineEntityUrn.from(magazineRequest.getUrn()); + MagazineCategory category = magazineCategoryService.getEntity(magazineRequest.getCategorySlug()); - // ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ๋กœ์ง ์ˆ˜ํ–‰ - MagazineResponse.Get magazine = magazineService.create(magazineRequest, member, category); + return createMagazine(magazineRequest, loginUsername, category, entityUrn); + } + private ResponseEntity createMagazine(MagazineRequest.Create magazineRequest, String username, MagazineCategory category, MagazineRequest.MagazineEntityUrn entityUrn) { + MagazineResponse.Get magazine = null; + switch (entityUrn) { + case MEMBER -> { + Member member = memberService.getEntity(username); + magazine = magazineService.createMemberMagazine(magazineRequest, member, category); + } + case TEAM -> { + TeamUser teamUser = teamUserService.findByTeamIdAndUsername(Long.valueOf(entityUrn.getId()), username); + magazine = magazineService.createTeamMagazine(magazineRequest, teamUser, category); + } + } return new ResponseEntity(magazine, HttpStatus.CREATED); } - @Operation(summary = "๋งค๊ฑฐ์ง„ ์กฐํšŒ") + @Operation(summary = "๋งค๊ฑฐ์ง„ ๋‹จ์ผ ์กฐํšŒ") @GetMapping("/{id}") public ResponseEntity getMagazine( @PathVariable Long id @@ -69,32 +118,37 @@ public ResponseEntity getMagazine( return new ResponseEntity(magazine, HttpStatus.OK); } - @Operation(summary = "๋งค๊ฑฐ์ง„ ๋ชฉ๋ก ์กฐํšŒ") + @Operation(summary = "๋งค๊ฑฐ์ง„ ์ „์ฒด ์กฐํšŒ", description = "ํ•ด๋‹น ์‚ฌ์šฉ์ž ๋˜๋Š” ํŒ€์ด ์ž‘์„ฑํ•œ ๋งค๊ฑฐ์ง„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค( ๋งŒ์•ฝ action, name์ด ์—†๋‹ค๋ฉด ์ „์ฒด ๋งค๊ฑฐ์ง„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค). ํŽ˜์ด์ง€๋„ค์ด์…˜") @GetMapping - public ResponseEntity getMagazines( + public ResponseEntity getAllMagazines( + @RequestParam("action") Optional<@Pattern(regexp = "team|member", message = "์ž˜๋ชป๋œ action ๊ฐ’์ž…๋‹ˆ๋‹ค.") String> action, + @RequestParam("name") Optional name, @PositiveOrZero @RequestParam(value = "page", defaultValue = "0") int page, @PositiveOrZero @RequestParam(value = "size", defaultValue = "10") int size, @RequestParam(defaultValue = "DESC", required = false) String sortDirection ) { PageRequest pageRequest = PageRequest.of(page, size, Sort.by(Sort.Direction.fromString(sortDirection), "createdTime")); - MagazineResponse.GetAll allMagazines = magazineService.getAll(pageRequest); + if (action.isPresent() && name.isPresent()) { + return getAllMagazines(action.get(), name.get(), pageRequest); + } + + MagazineResponse.GetAll allMagazines = magazineService.getAll(pageRequest); return new ResponseEntity(allMagazines, HttpStatus.OK); } - @Operation(summary = "ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ๋งค๊ฑฐ์ง„ ์ „์ฒด ์กฐํšŒ", description = "ํ•ด๋‹น ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ๋งค๊ฑฐ์ง„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€๋„ค์ด์…˜") - @GetMapping("/members/{username}") - public ResponseEntity getMyMagazines( - @PathVariable String username, - @PositiveOrZero @RequestParam(value = "page", defaultValue = "0") int page, - @PositiveOrZero @RequestParam(value = "size", defaultValue = "10") int size, - @RequestParam(defaultValue = "DESC", required = false) String sortDirection - ) { - PageRequest pageRequest = PageRequest.of(page, size, Sort.by(Sort.Direction.fromString(sortDirection), "createdTime")); - Member member = memberService.getEntity(username); - - MagazineResponse.GetAll allMagazines = magazineService.getMemberMagazines(member, pageRequest); - + private ResponseEntity getAllMagazines(String action, String name, PageRequest pageRequest) { + MagazineResponse.GetAll allMagazines = null; + switch (action) { + case ("member") -> { + Member member = memberService.getEntity(name); + allMagazines = magazineService.getMemberMagazines(member, pageRequest); + } + case ("team") -> { + Team team = teamService.getEntity(name); + allMagazines = magazineService.getTeamMagazines(team, pageRequest); + } + } return new ResponseEntity(allMagazines, HttpStatus.OK); } diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java index 46244bc2..931ee5cf 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java @@ -45,6 +45,10 @@ public static class Get { private AuthorResponse author; + private String teamName; + + private Long teamId; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime createdTime; @@ -72,6 +76,12 @@ public static Get from(Magazine magazine) { response.createdTime = magazine.getCreatedTime(); response.updatedTime = magazine.getUpdatedTime(); + if (magazine.getTeam() != null) { + response.setTeamId(magazine.getTeam().getId()); + response.setTeamName(magazine.getTeam().getName()); + } + + // 1์ฐจ ๋Œ“๊ธ€๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ response.magazineComments = magazine.getMagazineComments().stream() .filter((comment) -> comment.getParentComment() == null) diff --git a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java index dbb14ff1..d49d866c 100644 --- a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java +++ b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java @@ -2,6 +2,7 @@ import com.example.codebase.domain.magazine.entity.Magazine; import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.entity.Team; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; @@ -14,9 +15,11 @@ public interface MagazineRepository extends JpaRepository { @Query("SELECT m FROM Magazine m WHERE m.id = :id AND m.isDeleted = false") Optional findById(Long id); + @Query("SELECT m FROM Magazine m WHERE m.member = :member AND m.team is null") Page findByMember(Member member, PageRequest pageRequest); @Query("SELECT m FROM Magazine m LEFT JOIN Follow f ON (f.follower= :member) WHERE f.followingMember = m.member") Page findByMemberToFollowing(Member member, PageRequest pageRequest); + Page findByTeam(Team team, PageRequest pageRequest); } \ No newline at end of file diff --git a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java index c6d24aee..40833ed1 100644 --- a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java +++ b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java @@ -7,11 +7,12 @@ import com.example.codebase.domain.magazine.entity.MagazineCategory; import com.example.codebase.domain.magazine.entity.MagazineComment; import com.example.codebase.domain.magazine.entity.MagazineMedia; -import com.example.codebase.domain.magazine.repository.MagazineCategoryRepository; import com.example.codebase.domain.magazine.repository.MagazineCommentRepository; import com.example.codebase.domain.magazine.repository.MagazineMediaRepository; import com.example.codebase.domain.magazine.repository.MagazineRepository; import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.entity.TeamUser; import com.example.codebase.exception.NotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -31,19 +32,16 @@ public class MagazineService { private final MagazineMediaRepository magazineMediaRepository; - private final MagazineCategoryRepository magazineCategoryRepository; - @Autowired - public MagazineService(MagazineRepository magazineRepository, MagazineCommentRepository magazineCommentRepository, MagazineMediaRepository magazineMediaRepository, MagazineCategoryRepository magazineCategoryRepository) { + public MagazineService(MagazineRepository magazineRepository, MagazineCommentRepository magazineCommentRepository, MagazineMediaRepository magazineMediaRepository) { this.magazineRepository = magazineRepository; this.magazineCommentRepository = magazineCommentRepository; this.magazineMediaRepository = magazineMediaRepository; - this.magazineCategoryRepository = magazineCategoryRepository; } @Transactional - public MagazineResponse.Get create(MagazineRequest.Create magazineRequest, Member member, MagazineCategory category) { + public MagazineResponse.Get createMemberMagazine(MagazineRequest.Create magazineRequest, Member member, MagazineCategory category) { Magazine newMagazine = Magazine.toEntity(magazineRequest, member, category); magazineRepository.save(newMagazine); @@ -52,6 +50,18 @@ public MagazineResponse.Get create(MagazineRequest.Create magazineRequest, Membe return MagazineResponse.Get.from(newMagazine); } + @Transactional + public MagazineResponse.Get createTeamMagazine(MagazineRequest.Create magazineRequest, TeamUser teamMember, MagazineCategory category) { + Magazine newMagazine = Magazine.toEntity(magazineRequest, teamMember, category); + magazineRepository.save(newMagazine); + + List magazineMedias = MagazineMedia.toList(magazineRequest.getMediaUrls(), newMagazine); + magazineMediaRepository.saveAll(magazineMedias); + + magazineRepository.save(newMagazine); + return MagazineResponse.Get.from(newMagazine); + } + @Transactional public MagazineResponse.Get get(Long id) { Magazine magazine = magazineRepository.findById(id) @@ -120,10 +130,9 @@ private void replyComment(MagazineComment newComment, Long parentCommentId) { MagazineComment parentComment = magazineCommentRepository.findByIdAndMagazine(parentCommentId, newComment.getMagazine()) .orElseThrow(() -> new NotFoundException("๋ถ€๋ชจ๋Œ“๊ธ€์ด ์กด์žฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ํ•ด๋‹น ๋งค๊ฑฐ์ง„์— ์ž‘์„ฑ๋œ ๋Œ“๊ธ€์ด ์•„๋‹™๋‹ˆ๋‹ค.")); - if (isMentionComment(parentComment)){ + if (isMentionComment(parentComment)) { newComment.mentionComment(parentComment); - } - else { + } else { newComment.setParentComment(parentComment); } } @@ -173,8 +182,14 @@ public MagazineResponse.GetAll getMemberMagazines(Member member, PageRequest pag return MagazineResponse.GetAll.from(magazines); } + public MagazineResponse.GetAll getTeamMagazines(Team team, PageRequest pageRequest) { + Page magazines = magazineRepository.findByTeam(team, pageRequest); + return MagazineResponse.GetAll.from(magazines); + } + public MagazineResponse.GetAll getFollowingMagazine(Member member, PageRequest pageRequest) { Page magazines = magazineRepository.findByMemberToFollowing(member, pageRequest); return MagazineResponse.GetAll.from(magazines); } + } diff --git a/src/main/java/com/example/codebase/domain/team/service/TeamService.java b/src/main/java/com/example/codebase/domain/team/service/TeamService.java index 5f8daccc..0be37e64 100644 --- a/src/main/java/com/example/codebase/domain/team/service/TeamService.java +++ b/src/main/java/com/example/codebase/domain/team/service/TeamService.java @@ -43,6 +43,11 @@ public TeamResponse.Get getTeam(Long teamId) { return TeamResponse.Get.from(team); } + @Transactional(readOnly = true) + public Team getEntity(String teamName) { + return teamRepository.findByName(teamName).orElseThrow(() -> new NotFoundException("ํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + } + public TeamResponse.Get updateTeam(TeamRequest.Update request, TeamUser teamUser) { Team team = teamUser.getTeam(); From a8af558e32bf9d242d5b73371b2e2aea21de5252 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 7 Apr 2024 22:51:45 +0900 Subject: [PATCH 059/103] =?UTF-8?q?feat:=20=EC=A4=91=EB=B3=B5=20=EB=A7=A4?= =?UTF-8?q?=EA=B1=B0=EC=A7=84=20=EC=83=9D=EC=84=B1=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=9D=84=20=EB=B0=A9=EC=A7=80=ED=95=98=EB=8A=94=20AOP=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80=20-=20@CheckDuplicatedRequest=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=EC=9D=84=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=EA=B2=80=EC=82=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20=EB=A7=A4=EA=B1=B0=EC=A7=84=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20API=EC=97=90=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=EC=9D=84=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=97=B0=EC=86=8D=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annotation/CheckDuplicatedRequest.java | 12 +++++ .../aop/CheckDuplicatedRequestAspect.java | 51 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/main/java/com/example/codebase/annotation/CheckDuplicatedRequest.java create mode 100644 src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java diff --git a/src/main/java/com/example/codebase/annotation/CheckDuplicatedRequest.java b/src/main/java/com/example/codebase/annotation/CheckDuplicatedRequest.java new file mode 100644 index 00000000..713a7b48 --- /dev/null +++ b/src/main/java/com/example/codebase/annotation/CheckDuplicatedRequest.java @@ -0,0 +1,12 @@ +package com.example.codebase.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface CheckDuplicatedRequest { + String target(); +} diff --git a/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java b/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java new file mode 100644 index 00000000..0a3eb652 --- /dev/null +++ b/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java @@ -0,0 +1,51 @@ +package com.example.codebase.aop; + +import com.example.codebase.annotation.CheckDuplicatedRequest; +import com.example.codebase.exception.DuplicatedRequestException; +import com.example.codebase.util.RedisUtil; +import com.example.codebase.util.SecurityUtil; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.time.LocalDateTime; + +@Aspect +@Component +public class CheckDuplicatedRequestAspect { + + private final RedisUtil redisUtil; + + @Autowired + private CheckDuplicatedRequestAspect(RedisUtil redisUtil) { + this.redisUtil = redisUtil; + } + + @Before("@annotation(com.example.codebase.annotation.CheckDuplicatedRequest)") + public void checkDuplicateRequest(JoinPoint joinPoint) { + CheckDuplicatedRequest checkDuplicatedRequest = getAnnotation(joinPoint); + String username = String.valueOf(SecurityUtil.getCurrentUsername()); + + if(username.isEmpty()){ + return; + } + + String target = checkDuplicatedRequest.target(); + + String uniqueKey = username + "->" + target; + if (redisUtil.getData(uniqueKey).isPresent()) { + throw new DuplicatedRequestException(); + } + redisUtil.setDataAndExpire(uniqueKey, String.valueOf(LocalDateTime.now()), 3000); + } + + private CheckDuplicatedRequest getAnnotation(JoinPoint joinPoint) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + return method.getAnnotation(CheckDuplicatedRequest.class); + } +} From dbd80e376eb3ff6b07b14412273342993991f798 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 7 Apr 2024 22:53:27 +0900 Subject: [PATCH 060/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20=EB=A7=A4=EA=B1=B0=EC=A7=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=8B=9C=20reqeust=EC=97=90=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=B4=EC=A7=84=20urn=EC=9D=84=20=EC=B6=94=EA=B0=80=20-=20?= =?UTF-8?q?=EC=97=B0=EC=86=8D=20=EC=A1=B0=ED=9A=8C=EC=99=80=20urn=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CurationControllerTest.java | 4 +- .../MagazineCategoryControllerTest.java | 2 +- .../controller/MagazineControllerTest.java | 347 ++++++++++++++++-- 3 files changed, 321 insertions(+), 32 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index 15f6e2a9..b008e6de 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -172,7 +172,7 @@ public MagazineResponse.Get createMagazine(Member member) { magazineRequest.setContent("๋‚ด์šฉ"); magazineRequest.setCategorySlug(category.getSlug()); - return magazineService.create(magazineRequest, member, category); + return magazineService.createMemberMagazine(magazineRequest, member, category); } MagazineResponse.Get createMagazine(Member member, MagazineCategory category) { @@ -181,7 +181,7 @@ MagazineResponse.Get createMagazine(Member member, MagazineCategory category) { magazineRequest.setContent("๋‚ด์šฉ"); magazineRequest.setCategorySlug(category.getSlug()); - return magazineService.create(magazineRequest, member, category); + return magazineService.createMemberMagazine(magazineRequest, member, category); } diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index 99d67986..3fcfe856 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -101,7 +101,7 @@ public MagazineResponse.Get createMagaizne(Member member, MagazineCategory categ "https://cdn.artscope.kr/local/2.jpg" )); - return magazineService.create(magazineRequest, member, category); + return magazineService.createMemberMagazine(magazineRequest, member, category); } public Member createMember(String username) { diff --git a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java index 4606a85f..d109a3c8 100644 --- a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java @@ -9,6 +9,12 @@ import com.example.codebase.domain.member.dto.CreateMemberDTO; import com.example.codebase.domain.member.entity.Member; import com.example.codebase.domain.member.service.MemberService; +import com.example.codebase.domain.team.dto.TeamRequest; +import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.dto.TeamUserRequest; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.service.TeamService; +import com.example.codebase.domain.team.service.TeamUserService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import jakarta.transaction.Transactional; @@ -61,6 +67,12 @@ class MagazineControllerTest { @Autowired private FollowService followService; + @Autowired + private TeamService teamService; + + @Autowired + private TeamUserService teamUserService; + private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach @@ -100,7 +112,26 @@ public MagazineResponse.Get createMagaizne(Member member) { "https://cdn.artscope.kr/local/2.jpg" )); - return magazineService.create(magazineRequest, member, category); + return magazineService.createMemberMagazine(magazineRequest, member, category); + } + + public MagazineResponse.Get createMagaizne(TeamUser teamUser) { + MagazineCategory category = createCategory(); + + MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); + magazineRequest.setTitle("์ œ๋ชฉ"); + magazineRequest.setContent("๋‚ด์šฉ"); + magazineRequest.setCategorySlug(category.getSlug()); + magazineRequest.setMetadata(Map.of( + "color", "blue", + "font", "godic" + )); + magazineRequest.setMediaUrls(List.of( + "https://cdn.artscope.kr/local/1.jpg", + "https://cdn.artscope.kr/local/2.jpg" + )); + + return magazineService.createTeamMagazine(magazineRequest, teamUser, category); } public MagazineCategory createCategory() { @@ -133,13 +164,35 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, return magazineService.newMagazineComment(magaizne.getId(), member, newChildComment); } + public TeamRequest.Create createTeamRequest(String name) { + return new TeamRequest.Create( + name, + "ํŒ€ ์ฃผ์†Œ", + "http://test.com/profile.jpg", + "http://test.com/background.jpg", + "ํŒ€์†Œ๊ฐœ", + "์ž์‹ ์˜ ํฌ์ง€์…˜, ์ง๊ธ‰" + ); + } - @WithMockCustomUser(username = "testid", role = "USER") + public TeamResponse.Get createTeam(Member member, String name) { + return teamService.createTeam(createTeamRequest(name), member); + } + + public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { + TeamUserRequest.Create request = new TeamUserRequest.Create( + "ํŒ€์›" + ); + teamUserService.addTeamUser(loginUser, inviteMember, request); + } + + + @WithMockCustomUser(username = "testid1", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์ด ๋œ๋‹ค.") @Test void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ() throws Exception { // given - createMember("testid"); + createMember("testid1"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -147,6 +200,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); magazineRequest.setCategorySlug(category.getSlug()); + magazineRequest.setUrn("urn:member"); // when String response = mockMvc.perform( @@ -166,16 +220,17 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, assertEquals(magazine.getCategoryId(), category.getId()); } - @WithMockCustomUser(username = "testid", role = "USER") + @WithMockCustomUser(username = "testid2", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด 400.") @Test void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ_์—๋Ÿฌ() throws Exception { // given - createMember("testid"); + createMember("testid2"); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); magazineRequest.setCategorySlug("slug"); + magazineRequest.setUrn("urn:member"); // when String content = mockMvc.perform( @@ -252,12 +307,12 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, assertTrue(content.contains("ํ•ด๋‹น ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); } - @WithMockCustomUser(username = "testid", role = "USER") + @WithMockCustomUser(username = "testid3", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ˆ˜์ •์ด ๋œ๋‹ค.") @Test void ๋งค๊ฑฐ์ง„_์ˆ˜์ •() throws Exception { // given - Member author = createMember("testid"); + Member author = createMember("testid3"); MagazineResponse.Get magazine = createMagaizne(author); MagazineRequest.Update magazineRequest = new MagazineRequest.Update(); @@ -283,7 +338,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, assertEquals(magazineRequest.getCategorySlug(), magazineResponse.getCategorySlug()); } - @WithMockCustomUser(username = "testid", role = "USER") + @WithMockCustomUser(username = "testid4", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ˆ˜์ •์‹œ ์—†๋Š” ๋งค๊ฑฐ์ง„์ด๋ฉด 404.") @Test void ๋งค๊ฑฐ์ง„_์ˆ˜์ •_์—๋Ÿฌ() throws Exception { @@ -308,12 +363,12 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, assertTrue(content.contains("ํ•ด๋‹น ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); } - @WithMockCustomUser(username = "testid", role = "USER") + @WithMockCustomUser(username = "testid5", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์‚ญ์ œ๊ฐ€ ๋œ๋‹ค.") @Test void ๋งค๊ฑฐ์ง„_์‚ญ์ œ() throws Exception { // given - Member author = createMember("testid"); + Member author = createMember("testid5"); MagazineResponse.Get magazine = createMagaizne(author); // when @@ -331,7 +386,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, .andExpect(status().isNotFound()); } - @WithMockCustomUser(username = "testid", role = "USER") + @WithMockCustomUser(username = "testid6", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์‚ญ์ œ์‹œ ์—†๋Š” ๋งค๊ฑฐ์ง„์ด๋ฉด 404.") @Test void ๋งค๊ฑฐ์ง„_์‚ญ์ œ_์—๋Ÿฌ() throws Exception { @@ -479,12 +534,12 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, .andExpect(status().isNotFound()); } - @WithMockCustomUser(username = "testid", role = "USER") + @WithMockCustomUser(username = "testid7", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ๋ฏธ๋””์–ด ์ฒจ๋ถ€๊ฐ€ ๋œ๋‹ค.") @Test void ๋งค๊ฑฐ์ง„_๋ฏธ๋””์–ด_์ƒ์„ฑ() throws Exception { // given - createMember("testid"); + createMember("testid7"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -496,6 +551,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, "https://cdn.artscope.kr/local/1.jpg", "https://cdn.artscope.kr/local/2.jpg" )); + magazineRequest.setUrn("urn:member"); // when String response = mockMvc.perform( @@ -515,12 +571,12 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, assertEquals(magazine.getMediaUrls().size(), 2); } - @WithMockCustomUser(username = "testid", role = "USER") + @WithMockCustomUser(username = "testid8", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ์ž˜๋ชป๋œ ๋ฏธ๋””์–ด URL์ด๋ฉด 400.") @Test void ๋งค๊ฑฐ์ง„_๋ฏธ๋””์–ด_์ž˜๋ชป๋œ_์ƒ์„ฑ() throws Exception { // given - createMember("testid"); + createMember("testid8"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -544,12 +600,12 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, .andExpect(status().isBadRequest()); } - @WithMockCustomUser(username = "testid", role = "USER") + @WithMockCustomUser(username = "testid9", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ๋ฏธ๋””์–ด ์ตœ๋Œ€ ๊ฐœ์ˆ˜ ์ด์ƒ ์ฒจ๋ถ€ ์‹œ 400.") @Test void ๋งค๊ฑฐ์ง„_๋ฏธ๋””์–ด_์ž˜๋ชป๋œ_์ƒ์„ฑ2() throws Exception { // given - createMember("testid"); + createMember("testid9"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -569,7 +625,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, "https://cdn.artscope.kr/local/9.jpg", "https://cdn.artscope.kr/local/10.jpg", "https://cdn.artscope.kr/local/11.jpg" - )); + )); // when mockMvc.perform( @@ -582,12 +638,12 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, .andExpect(status().isBadRequest()); } - @WithMockCustomUser(username = "testid", role = "USER") + @WithMockCustomUser(username = "testid10", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ฒจ๋ถ€ํ•œ๋‹ค.") @Test - void ๋งค๊ฑฐ์ง„_๋ฉ”ํƒ€๋ฐ์ดํ„ฐ_์ƒ์„ฑ () throws Exception { + void ๋งค๊ฑฐ์ง„_๋ฉ”ํƒ€๋ฐ์ดํ„ฐ_์ƒ์„ฑ() throws Exception { // given - createMember("testid"); + createMember("testid10"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -599,6 +655,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, "color", "blue", "font", "godic" )); + magazineRequest.setUrn("urn:member"); // when mockMvc.perform( @@ -614,7 +671,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ˆ˜์ •์ด ๋œ๋‹ค.") @Test - void ๋งค๊ฑฐ์ง„_๋ฉ”ํƒ€๋ฐ์ดํ„ฐ_์ˆ˜์ • () throws Exception { + void ๋งค๊ฑฐ์ง„_๋ฉ”ํƒ€๋ฐ์ดํ„ฐ_์ˆ˜์ •() throws Exception { // given Member member = createMember("testid"); MagazineResponse.Get magaizne = createMagaizne(member); @@ -660,7 +717,9 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, // when String response = mockMvc.perform( - get("/api/magazines/members/{username}", member.getUsername()) + get("/api/magazines", member.getUsername()) + .param("action", "member") + .param("name", member.getUsername()) .param("page", "0") .param("size", "10") ) @@ -676,7 +735,7 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("ํ•ด๋‹น ์‚ฌ์šฉ์ž๊ฐ€ ํŒ”๋กœ์šฐ ์ค‘์ธ ์œ ์ €์˜ ๋งค๊ฑฐ์ง„ ๋ชฉ๋ก ์กฐํšŒ") @Test - void ํ•ด๋‹น_์‚ฌ์šฉ์ž๊ฐ€_ํŒ”๋กœ์šฐ_์ค‘์ธ_์œ ์ €์˜_๋งค๊ฑฐ์ง„_๋ชฉ๋ก_์กฐํšŒ() throws Exception{ + void ํ•ด๋‹น_์‚ฌ์šฉ์ž๊ฐ€_ํŒ”๋กœ์šฐ_์ค‘์ธ_์œ ์ €์˜_๋งค๊ฑฐ์ง„_๋ชฉ๋ก_์กฐํšŒ() throws Exception { // given Member member = createMember("testid"); Member following = createMember("following"); @@ -692,10 +751,10 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, //when String response = mockMvc.perform( - get("/api/magazines/my/following/members") - .param("page", "0") - .param("size", "10") - ) + get("/api/magazines/my/following/members") + .param("page", "0") + .param("size", "10") + ) .andDo(print()) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); @@ -704,4 +763,234 @@ public MagazineResponse.Get createCommentHasChild(MagazineResponse.Get magaizne, MagazineResponse.GetAll magazineList = objectMapper.readValue(response, MagazineResponse.GetAll.class); assertEquals(magazineList.getMagazines().size(), 3); } -} \ No newline at end of file + + @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ „์ฒด ์กฐํšŒ ์‹œ") + @Test + void ํ•ด๋‹น_ํŒ€_๋งค๊ฑฐ์ง„_์ „์ฒด_์กฐํšŒ() throws Exception { + // given + Member member = createMember("testid"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamUser = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + createMagaizne(teamUser); + createMagaizne(teamUser); + createMagaizne(teamUser); + + // when + String response = mockMvc.perform( + get("/api/magazines", member.getUsername()) + .param("action", "team") + .param("name", teamUser.getTeam().getName()) + .param("page", "0") + .param("size", "10") + ) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + MagazineResponse.GetAll magazineList = objectMapper.readValue(response, MagazineResponse.GetAll.class); + assertEquals(magazineList.getMagazines().size(), 3); + } + + @WithMockCustomUser(username = "testid11", role = "USER") + @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ์ž˜๋ชป๋œ urn ํ˜•์‹์œผ๋กœ ์š”์ฒญ์‹œ ์‹คํŒจ") + @Test + void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ_urn_๋ฌธ์ œ_์‹คํŒจ() throws Exception { + // given + createMember("testid10"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); + + MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); + magazineRequest.setTitle("์ œ๋ชฉ"); + magazineRequest.setContent("๋‚ด์šฉ"); + magazineRequest.setCategorySlug(category.getSlug()); + magazineRequest.setMetadata(Map.of( + "color", "blue", + "font", "godic" + )); + magazineRequest.setUrn("urn:member:"); + + // when1 urn:member: + String response1 =mockMvc.perform( + post("/api/magazines") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(magazineRequest)) + ) + .andDo(print()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + assertTrue(response1.contains("์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")); + + // when2 urn:member:member + magazineRequest.setUrn("urn:member:member"); + String response2 = mockMvc.perform( + post("/api/magazines") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(magazineRequest)) + ) + .andDo(print()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + assertTrue(response1.contains("์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")); + + //when3 urn:asdf + magazineRequest.setUrn("urn:member:asdf"); + + String response3 = mockMvc.perform( + post("/api/magazines") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(magazineRequest)) + ) + .andDo(print()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + assertTrue(response1.contains("์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")); + } + + @WithMockCustomUser(username = "testid12", role = "USER") + @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ") + @Test + void ํŒ€_๋งค๊ฑฐ์ง„_์ƒ์„ฑ() throws Exception { + // given + Member member = createMember("testid12"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamUser = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); + + MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); + magazineRequest.setTitle("์ œ๋ชฉ"); + magazineRequest.setContent("๋‚ด์šฉ"); + magazineRequest.setCategorySlug(category.getSlug()); + magazineRequest.setMetadata(Map.of( + "color", "blue", + "font", "godic" + )); + magazineRequest.setUrn("urn:team:" + team.getId()); + + // when + String response = mockMvc.perform( + post("/api/magazines") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(magazineRequest)) + ) + .andDo(print()) + .andExpect(status().isCreated()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + //then + MagazineResponse.Get magazine = objectMapper.readValue(response, MagazineResponse.Get.class); + assertTrue(magazine.getId() > 0); + assertEquals(magazine.getTitle(), magazineRequest.getTitle()); + assertEquals(magazine.getContent(), magazineRequest.getContent()); + assertEquals(magazine.getCategoryId(), category.getId()); + } + + @WithMockCustomUser(username = "testid13", role = "USER") + @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ ํŒ€์— ์†ํ•ด์žˆ์ง€ ์•Š์€๊ฒฝ์šฐ ์‹คํŒจ") + @Test + void ํŒ€_๋งค๊ฑฐ์ง„_์ƒ์„ฑ์‹œ_ํ•ด๋‹น_ํŒ€_์œ ์ €๊ฐ€_์•„๋‹Œ๊ฒฝ์šฐ_์‹คํŒจ() throws Exception { + // given + Member member = createMember("admin"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamUser = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + createMember("testid13"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); + + MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); + magazineRequest.setTitle("์ œ๋ชฉ"); + magazineRequest.setContent("๋‚ด์šฉ"); + magazineRequest.setCategorySlug(category.getSlug()); + magazineRequest.setMetadata(Map.of( + "color", "blue", + "font", "godic" + )); + magazineRequest.setUrn("urn:team:" + team.getId()); + + // when + mockMvc.perform( + post("/api/magazines") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(magazineRequest)) + ) + .andDo(print()) + .andExpect(status().isNotFound()) + .andExpect(result -> assertEquals("ํ•ด๋‹น ํŒ€์— ์†ํ•ด์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + @WithMockCustomUser(username = "testid14", role = "USER") + @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ ํ•ด๋‹นํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์‹คํŒจ") + @Test + void ํŒ€_๋งค๊ฑฐ์ง„_์ƒ์„ฑ์‹œ_ํ•ด๋‹นํŒ€์ด_์กด์žฌํ•˜์ง€_์•Š์„_๊ฒฝ์šฐ_์‹คํŒจ() throws Exception { + // given + createMember("testid14"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); + + MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); + magazineRequest.setTitle("์ œ๋ชฉ"); + magazineRequest.setContent("๋‚ด์šฉ"); + magazineRequest.setCategorySlug(category.getSlug()); + magazineRequest.setMetadata(Map.of( + "color", "blue", + "font", "godic" + )); + magazineRequest.setUrn("urn:team:" + 14); + + // when + mockMvc.perform( + post("/api/magazines") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(magazineRequest)) + ) + .andDo(print()) + .andExpect(status().isNotFound()) + .andExpect(result -> assertEquals("ํ•ด๋‹น ํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + @WithMockCustomUser(username = "testid15", role = "USER") + @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ api๋ฅผ ์—ฐ์†์œผ๋กœ ํ˜ธ์ถœํ•  ์‹œ ์‹คํŒจ ") + @Test + void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ_api๋ฅผ_์—ฐ์†์œผ๋กœ_ํ˜ธ์ถœํ• _์‹œ_์‹คํŒจ() throws Exception { + // given + createMember("testid15"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); + + MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); + magazineRequest.setTitle("์ œ๋ชฉ"); + magazineRequest.setContent("๋‚ด์šฉ"); + magazineRequest.setCategorySlug(category.getSlug()); + magazineRequest.setMetadata(Map.of( + "color", "blue", + "font", "godic" + )); + magazineRequest.setUrn("urn:member"); + + mockMvc.perform( + post("/api/magazines") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(magazineRequest)) + ) + .andDo(print()) + .andExpect(status().isCreated()); + + // when + String response = mockMvc.perform( + post("/api/magazines") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(magazineRequest)) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + //then + assertTrue(response.contains("์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")); + } +} From f685a152329676e526834d6bbcb2ec5c163b2a68 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 7 Apr 2024 22:57:05 +0900 Subject: [PATCH 061/103] =?UTF-8?q?refactor:=20=EB=A7=A4=EA=B1=B0=EC=A7=84?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=EC=8B=9C=20=EC=9E=98=EB=AA=BB=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=EB=90=9C=20swagger=20=EC=8A=A4=ED=82=A4=EB=A7=88=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/codebase/controller/MagazineController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/controller/MagazineController.java b/src/main/java/com/example/codebase/controller/MagazineController.java index f9c664c6..3f9e439e 100644 --- a/src/main/java/com/example/codebase/controller/MagazineController.java +++ b/src/main/java/com/example/codebase/controller/MagazineController.java @@ -65,7 +65,7 @@ public MagazineController(MagazineService magazineService, MagazineCategoryServi parameters = @Parameter(name = "action", description = "member: ๊ฐœ์ธ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ, team: ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ", required = true, example = "team"), requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( description = "๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์ž URN", required = true, content = @io.swagger.v3.oas.annotations.media.Content( - schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = FollowRequest.Create.class), + schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = MagazineRequest.Create.class), examples = @io.swagger.v3.oas.annotations.media.ExampleObject(value = "{\"title\": \"๋งค๊ฑฐ์ง„ ์ œ๋ชฉ\"," + "\"content\": \"๋งค๊ฑฐ์ง„ ๋‚ด์šฉ\", " + From 27dce7b2a03bcf9fd368fe69c97d9f611c50fa29 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 9 Apr 2024 16:51:23 +0900 Subject: [PATCH 062/103] =?UTF-8?q?feat:=20SecurityUtil=20getLoginUsername?= =?UTF-8?q?()=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getLoginUsername() String์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ ์ •๋ณด๊ฐ€ ์—†๋‹ค๋ฉด null์„ ํ•ฉ๋‹ˆ๋‹ค. --- .../example/codebase/util/SecurityUtil.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/example/codebase/util/SecurityUtil.java b/src/main/java/com/example/codebase/util/SecurityUtil.java index 93facd1a..980c2b5d 100644 --- a/src/main/java/com/example/codebase/util/SecurityUtil.java +++ b/src/main/java/com/example/codebase/util/SecurityUtil.java @@ -9,6 +9,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; +import javax.annotation.Nullable; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -19,27 +20,31 @@ public class SecurityUtil { public SecurityUtil() { } - public static Optional getCurrentUsername() { + @Nullable + public static String getLoginUsername() { final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || isAnonymous()) { log.debug("Security Context์— ์ธ์ฆ ์ •๋ณด ์—†์Šต๋‹ˆ๋‹ค."); - return Optional.empty(); + return null; } String username = null; - if (authentication.getPrincipal() instanceof UserDetails) { - UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal(); + if (authentication.getPrincipal() instanceof UserDetails springSecurityUser) { username = springSecurityUser.getUsername(); } else if (authentication.getPrincipal() instanceof String) { username = (String) authentication.getPrincipal(); } - return Optional.ofNullable(username); + return username; + } + + public static Optional getCurrentUsername() { + return Optional.ofNullable(getLoginUsername()); } public static String getCurrentUsernameValue() { - return getCurrentUsername().orElseThrow(LoginRequiredException::new); + return Optional.ofNullable(getLoginUsername()).orElseThrow(LoginRequiredException::new); } public static boolean isAnonymous() { @@ -76,6 +81,7 @@ public static Boolean isAdmin() { } public static Boolean isSameUser(String username1, String username2) { + if (username2 == null) return false; return username1.equals(username2); } @@ -83,7 +89,7 @@ public static Boolean isSameUser(String username1, String username2) { Username์„ ๊ฐ€์ง„ ์‚ฌ๋žŒ์ด ๊ด€๋ฆฌ์ž ์ด๊ฑฐ๋‚˜, ํ˜„์žฌ ์Šคํ”„๋ง ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋œ ์œ ์ €์™€ ๊ฐ™์€ ์‚ฌ๋žŒ์ธ๊ฐ€(๊ฐ™์€ ์Šค๋ ˆ๋“œ ์š”์ฒญ์ธ์ง€) */ public static Boolean isAdminOrSameUser(String username) { - return isAdmin() || isSameUser(username, getCurrentUsername().get()); + return isAdmin() || isSameUser(username, getLoginUsername()); } public static String getCookieAccessTokenValue(TokenResponseDTO tokenDto) { From 6244ce206999d0199cc2dcd1b1f5e052e57d3244 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 9 Apr 2024 16:54:27 +0900 Subject: [PATCH 063/103] =?UTF-8?q?feat:=20MagazineWithIsLiked=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20MagazineR?= =?UTF-8?q?epository=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=97=AC=EB=B6=80?= =?UTF-8?q?=EB=A5=BC=20=ED=8F=AC=ED=95=A8=ED=95=98=EB=8A=94=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ์ธํ„ฐํŽ˜์ด์Šค default ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•œ ์ข‹์•„์š” ์—ฌ๋ถ€ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ์ž‘์„ฑ - ๋‹จ์ผ ์กฐํšŒ, ์ „์ฒด ์กฐํšŒ ๋ฉ”์„œ๋“œ ๊ตฌํ˜„ --- .../magazine/entity/MagazineWithIsLiked.java | 8 +++++ .../repository/MagazineRepository.java | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/java/com/example/codebase/domain/magazine/entity/MagazineWithIsLiked.java diff --git a/src/main/java/com/example/codebase/domain/magazine/entity/MagazineWithIsLiked.java b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineWithIsLiked.java new file mode 100644 index 00000000..96b40598 --- /dev/null +++ b/src/main/java/com/example/codebase/domain/magazine/entity/MagazineWithIsLiked.java @@ -0,0 +1,8 @@ +package com.example.codebase.domain.magazine.entity; + +public interface MagazineWithIsLiked { + + Magazine getMagazine(); + + Boolean getIsLiked(); +} diff --git a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java index dbb14ff1..34468ebe 100644 --- a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java +++ b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java @@ -1,6 +1,7 @@ package com.example.codebase.domain.magazine.repository; import com.example.codebase.domain.magazine.entity.Magazine; +import com.example.codebase.domain.magazine.entity.MagazineWithIsLiked; import com.example.codebase.domain.member.entity.Member; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -19,4 +20,33 @@ public interface MagazineRepository extends JpaRepository { @Query("SELECT m FROM Magazine m LEFT JOIN Follow f ON (f.follower= :member) WHERE f.followingMember = m.member") Page findByMemberToFollowing(Member member, PageRequest pageRequest); + + @Query("SELECT m AS magazine, false AS isLiked FROM Magazine m WHERE m.id = :id AND m.isDeleted = false") + Optional findMagazineWithIsLikedById(Long id); + + @Query("SELECT m AS magazine, CASE WHEN ml.member.username = :username THEN true ELSE false END As isLiked " + + "FROM Magazine m LEFT JOIN MagazineLike ml ON m = ml.magazine AND ml.member.username = :username " + + "WHERE m.id = :id") + Optional findMagazineWithLikeeByIdAndMember(Long id, String username); + + @Query("SELECT m AS magazine, false AS isLiked FROM Magazine m WHERE m.isDeleted = false") + Page findAllMagazineWithIsLiked(PageRequest pageRequest); + + @Query("SELECT m AS magazine, CASE WHEN ml.member.username = :username THEN true ELSE false END As isLiked " + + "FROM Magazine m LEFT JOIN MagazineLike ml ON m = ml.magazine AND ml.member.username = :username") + Page findAllMagazineWithIsLikedByUsername(String username, PageRequest pageRequest); + + default Optional findMagazineWithIsLiked(Long id, String username) { + if (username != null) { + return findMagazineWithLikeeByIdAndMember(id, username); + } + return findMagazineWithIsLikedById(id); + } + + default Page findAllMagazineWithIsLiked(PageRequest pageRequest, String username) { + if (username != null) { + return findAllMagazineWithIsLikedByUsername(username, pageRequest); + } + return findAllMagazineWithIsLiked(pageRequest); + } } \ No newline at end of file From 20c42af44998004e0aea8d356f88845f9befbb48 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 9 Apr 2024 16:56:44 +0900 Subject: [PATCH 064/103] =?UTF-8?q?feat:=20MagazineService=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20=EB=B0=8F=20=EB=8B=A8=EC=9D=BC?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=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 - findMagazineWithIsLiked (๋‹จ์ผ ์กฐํšŒ), findAllMagazineWithIsLiked (์ „์ฒด ์กฐํšŒ) ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋กœ์ง ๋ณ€๊ฒฝ - SecurityUtil ๋กœ ๋ถ€ํ„ฐ ํ˜„์žฌ ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€, ํ•ด๋‹น ๋ฐ˜ํ™˜๊ฐ’ (String,Nullable)์„ ์ด์šฉํ•ด ์ข‹์•„์š” ์—ฌ๋ถ€ ์กฐํšŒ๋ฅผ Repository ๊ณ„์ธต์—์„œ ๊ตฌ๋ถ„ํ•˜๋„๋ก ๋กœ์ง ์ž‘์„ฑ --- .../domain/magazine/dto/MagazineResponse.java | 20 +++++++++++++++++++ .../magazine/service/MagazineService.java | 16 +++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java index 46244bc2..01c7eb10 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineResponse.java @@ -3,6 +3,7 @@ import com.example.codebase.controller.dto.PageInfo; import com.example.codebase.domain.magazine.entity.Magazine; import com.example.codebase.domain.magazine.entity.MagazineMedia; +import com.example.codebase.domain.magazine.entity.MagazineWithIsLiked; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; @@ -45,6 +46,8 @@ public static class Get { private AuthorResponse author; + private Boolean isLiked = false; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime createdTime; @@ -82,6 +85,12 @@ public static Get from(Magazine magazine) { return response; } + public static Get from(MagazineWithIsLiked magazineWithIsLiked) { + Get response = from(magazineWithIsLiked.getMagazine()); + response.isLiked = magazineWithIsLiked.getIsLiked(); + return response; + } + @JsonIgnore public Long getFirstCommentId() { if (magazineComments.isEmpty()) { @@ -118,5 +127,16 @@ public static GetAll from(Page magazines) { response.pageInfo = PageInfo.from(magazines); return response; } + + public static GetAll withLike(Page magazines) { + GetAll response = new GetAll(); + + response.magazines = magazines.stream() + .map(Get::from) + .toList(); + response.pageInfo = PageInfo.from(magazines); + return response; + } + } } diff --git a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java index c6d24aee..fe16ce9c 100644 --- a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java +++ b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java @@ -3,16 +3,14 @@ import com.example.codebase.domain.magazine.dto.MagazineCommentRequest; import com.example.codebase.domain.magazine.dto.MagazineRequest; import com.example.codebase.domain.magazine.dto.MagazineResponse; -import com.example.codebase.domain.magazine.entity.Magazine; -import com.example.codebase.domain.magazine.entity.MagazineCategory; -import com.example.codebase.domain.magazine.entity.MagazineComment; -import com.example.codebase.domain.magazine.entity.MagazineMedia; +import com.example.codebase.domain.magazine.entity.*; import com.example.codebase.domain.magazine.repository.MagazineCategoryRepository; import com.example.codebase.domain.magazine.repository.MagazineCommentRepository; import com.example.codebase.domain.magazine.repository.MagazineMediaRepository; import com.example.codebase.domain.magazine.repository.MagazineRepository; import com.example.codebase.domain.member.entity.Member; import com.example.codebase.exception.NotFoundException; +import com.example.codebase.util.SecurityUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -54,18 +52,18 @@ public MagazineResponse.Get create(MagazineRequest.Create magazineRequest, Membe @Transactional public MagazineResponse.Get get(Long id) { - Magazine magazine = magazineRepository.findById(id) + MagazineWithIsLiked magazine = magazineRepository.findMagazineWithIsLiked(id, SecurityUtil.getLoginUsername()) .orElseThrow(() -> new NotFoundException("ํ•ด๋‹น ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); - magazine.incressView(); + magazine.getMagazine().incressView(); - magazineRepository.save(magazine); + magazineRepository.save(magazine.getMagazine()); return MagazineResponse.Get.from(magazine); } public MagazineResponse.GetAll getAll(PageRequest pageRequest) { - Page magazines = magazineRepository.findAll(pageRequest); - return MagazineResponse.GetAll.from(magazines); + Page magazines = magazineRepository.findAllMagazineWithIsLiked(pageRequest, SecurityUtil.getLoginUsername()); + return MagazineResponse.GetAll.withLike(magazines); } @Transactional From 37c2e4a363dc7e0b691a6ab399ee41fa1720ff81 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Tue, 9 Apr 2024 16:57:09 +0900 Subject: [PATCH 065/103] =?UTF-8?q?test:=20=EB=A7=A4=EA=B1=B0=EC=A7=84=20?= =?UTF-8?q?=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=97=AC=EB=B6=80=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EC=A1=B0=ED=9A=8C=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=9C=A0=EC=8A=A4=EC=BC=80=EC=9D=B4=EC=8A=A4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MagazineLikeControllerTest.java | 117 ++++++++++++++---- 1 file changed, 94 insertions(+), 23 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java index e0d5e000..f75c9c3f 100644 --- a/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineLikeControllerTest.java @@ -3,8 +3,6 @@ import com.example.codebase.domain.auth.WithMockCustomUser; import com.example.codebase.domain.magazine.dto.MagazineCategoryRequest; import com.example.codebase.domain.magazine.dto.MagazineCategoryResponse; -import com.example.codebase.domain.magazine.dto.MagazineRequest; -import com.example.codebase.domain.magazine.dto.MagazineResponse; import com.example.codebase.domain.magazine.entity.Magazine; import com.example.codebase.domain.magazine.entity.MagazineCategory; import com.example.codebase.domain.magazine.repository.MagazineCategoryRepository; @@ -13,49 +11,30 @@ import com.example.codebase.domain.magazine.service.MagazineLikeService; import com.example.codebase.domain.magazine.service.MagazineService; import com.example.codebase.domain.member.dto.CreateMemberDTO; -import com.example.codebase.domain.member.entity.Authority; import com.example.codebase.domain.member.entity.Member; -import com.example.codebase.domain.member.entity.MemberAuthority; import com.example.codebase.domain.member.repository.MemberRepository; import com.example.codebase.domain.member.service.MemberService; -import com.example.codebase.jwt.TokenProvider; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import jakarta.persistence.EntityManager; -import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; import org.springframework.web.context.WebApplicationContext; import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; import java.util.Random; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -150,7 +129,7 @@ public Magazine createMagaizne(Member member) { public MagazineCategory createCategory() { Random random = new Random(System.currentTimeMillis()); - String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(300); + String categoryName = "์นดํ…Œ๊ณ ๋ฆฌ" + random.nextInt(10000); char randomChar1 = (char) ('a' + random.nextInt(26)); char randomChar2 = (char) ('a' + random.nextInt(26)); @@ -179,6 +158,54 @@ public MagazineCategory createCategory() { assertEquals(1, magazineService.get(magazine.getId()).getLikes()); } + @WithMockCustomUser(username = "testid") + @DisplayName("๋งค๊ฑฐ์ง„ ์ข‹์•„์š” ํ›„ ์ „์ฒด ์กฐํšŒ ์‹œ") + @Test + void ๋งค๊ฑฐ์ง„_์ข‹์•„์š”2() throws Exception { + createMagaizne(member); + createMagaizne(member); + + // when + mockMvc.perform( + post("/api/magazines/{magazineId}/like", magazine.getId()) + ) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertEquals(1, magazineService.get(magazine.getId()).getLikes()); + + // then + mockMvc.perform( + get("/api/magazines") + ) + .andDo(print()) + .andExpect(status().isOk()); + } + + @WithMockCustomUser(username = "testid") + @DisplayName("๋งค๊ฑฐ์ง„ ์ข‹์•„์š” ํ›„ ์ƒ์„ธ ์กฐํšŒ ์‹œ") + @Test + void ๋งค๊ฑฐ์ง„_์ข‹์•„์š”3() throws Exception { + + // when + mockMvc.perform( + post("/api/magazines/{magazineId}/like", magazine.getId()) + ) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertEquals(1, magazineService.get(magazine.getId()).getLikes()); + + // then + mockMvc.perform( + get("/api/magazines/{magazineId}", magazine.getId()) + ) + .andDo(print()) + .andExpect(status().isOk()); + } + + + @WithMockCustomUser(username = "testid") @DisplayName("๋งค๊ฑฐ์ง„ ์ข‹์•„์š” ์ทจ์†Œ ์‹œ") @Test @@ -198,6 +225,50 @@ public MagazineCategory createCategory() { assertEquals(0, magazineService.get(magazine.getId()).getLikes()); } + @WithMockCustomUser(username = "testid") + @DisplayName("๋งค๊ฑฐ์ง„ ์ข‹์•„์š” ์ทจ์†Œ ํ›„ ์ „์ฒด ์กฐํšŒ ์‹œ") + @Test + void ๋งค๊ฑฐ์ง„_์ข‹์•„์š”_์ทจ์†Œ2() throws Exception { + // given + createMagaizne(member); + createMagaizne(member); + magazineLikeService.like(magazine.getId(), member); + + // when + mockMvc.perform( + post("/api/magazines/{magazineId}/unlike", magazine.getId()) + ) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + assertEquals(0, magazineService.get(magazine.getId()).getLikes()); + + // then + mockMvc.perform( + get("/api/magazines") + ) + .andDo(print()) + .andExpect(status().isOk()); + } + + @WithMockCustomUser(username = "testid2") + @DisplayName("๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ข‹์•„์š”ํ•œ ๊ฒŒ์‹œ๋ฌผ์„ ํฌํ•จํ•ด ์ „์ฒด ์กฐํšŒ ์‹œ") + @Test + void ์ข‹์•„์š”3() throws Exception { + // given + createMagaizne(member); + createMagaizne(member); + magazineLikeService.like(magazine.getId(), member); + + // when then + mockMvc.perform( + get("/api/magazines") + ) + .andDo(print()) + .andExpect(status().isOk()); + } + + @WithMockCustomUser(username = "testid") @DisplayName("๋งค๊ฑฐ์ง„ 2๋ฒˆ ์ข‹์•„์š” ์‹œ") @Test From 7915c85081d5dc2d1fac022b7bde5efa17926c84 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 9 Apr 2024 23:54:33 +0900 Subject: [PATCH 066/103] =?UTF-8?q?feat:=20@CheckDuplicatedRequest?= =?UTF-8?q?=EC=97=90=20=EA=B8=B0=EB=B3=B8=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/codebase/annotation/CheckDuplicatedRequest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/annotation/CheckDuplicatedRequest.java b/src/main/java/com/example/codebase/annotation/CheckDuplicatedRequest.java index 713a7b48..d3d1107a 100644 --- a/src/main/java/com/example/codebase/annotation/CheckDuplicatedRequest.java +++ b/src/main/java/com/example/codebase/annotation/CheckDuplicatedRequest.java @@ -8,5 +8,7 @@ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CheckDuplicatedRequest { - String target(); + String target() default ""; + + int aliveMillisecondTime() default 3000; } From 50177dd49c8e45a37c1bb2f4c5a6295b0cfa10ef Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 9 Apr 2024 23:58:54 +0900 Subject: [PATCH 067/103] =?UTF-8?q?feat:=20CheckDuplicatedRequestAspect=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?-=20@CheckDuplicatedRequest=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=82=AC=EC=9A=A9=EC=8B=9C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EA=B4=80=EB=A0=A8=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=EC=9D=B4=20=EB=8B=AC=EB=A0=A4?= =?UTF-8?q?=EC=9E=88=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8=20-=20@CheckDu?= =?UTF-8?q?plicatedRequest=20target=20=EB=AF=B8=20=EC=A0=95=EC=9D=98?= =?UTF-8?q?=EC=8B=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=AA=85=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aop/CheckDuplicatedRequestAspect.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java b/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java index 0a3eb652..e582c830 100644 --- a/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java +++ b/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java @@ -1,7 +1,11 @@ package com.example.codebase.aop; import com.example.codebase.annotation.CheckDuplicatedRequest; +import com.example.codebase.annotation.LoginOnly; +import com.example.codebase.annotation.UserAdminOnly; +import com.example.codebase.annotation.UserOnly; import com.example.codebase.exception.DuplicatedRequestException; +import com.example.codebase.exception.LoginRequiredException; import com.example.codebase.util.RedisUtil; import com.example.codebase.util.SecurityUtil; import org.aspectj.lang.JoinPoint; @@ -9,6 +13,7 @@ import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @@ -16,6 +21,7 @@ @Aspect @Component +@Profile("!test") public class CheckDuplicatedRequestAspect { private final RedisUtil redisUtil; @@ -27,25 +33,28 @@ private CheckDuplicatedRequestAspect(RedisUtil redisUtil) { @Before("@annotation(com.example.codebase.annotation.CheckDuplicatedRequest)") public void checkDuplicateRequest(JoinPoint joinPoint) { - CheckDuplicatedRequest checkDuplicatedRequest = getAnnotation(joinPoint); - String username = String.valueOf(SecurityUtil.getCurrentUsername()); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + CheckDuplicatedRequest annotation = method.getAnnotation(CheckDuplicatedRequest.class); - if(username.isEmpty()){ - return; - } + if (!isValidAnnotation(method)) return; + + String username = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); - String target = checkDuplicatedRequest.target(); + if (username.isEmpty()) return; + + String target = annotation.target().isEmpty() ? String.valueOf(method) : annotation.target(); String uniqueKey = username + "->" + target; if (redisUtil.getData(uniqueKey).isPresent()) { throw new DuplicatedRequestException(); } - redisUtil.setDataAndExpire(uniqueKey, String.valueOf(LocalDateTime.now()), 3000); + redisUtil.setDataAndExpire(uniqueKey, String.valueOf(LocalDateTime.now()), annotation.aliveMillisecondTime()); } - private CheckDuplicatedRequest getAnnotation(JoinPoint joinPoint) { - MethodSignature signature = (MethodSignature) joinPoint.getSignature(); - Method method = signature.getMethod(); - return method.getAnnotation(CheckDuplicatedRequest.class); + private boolean isValidAnnotation(Method method) { + return method.isAnnotationPresent(LoginOnly.class) + || method.isAnnotationPresent(UserOnly.class) + || method.isAnnotationPresent(UserAdminOnly.class); } -} +} \ No newline at end of file From ca6b2161b0908f543b8c84eb70ef6aa49c73cbd6 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Wed, 10 Apr 2024 00:02:48 +0900 Subject: [PATCH 068/103] =?UTF-8?q?refactor:=20=EB=A7=A4=EA=B1=B0=EC=A7=84?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1,=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20api=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20-=20=EB=A7=A4=EA=B1=B0=EC=A7=84=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=8B=9C=20=EA=B8=B0=EC=A1=B4=EC=9D=98=20api?= =?UTF-8?q?=EC=99=80=20=EB=8C=80=EC=9D=91=EB=90=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EB=90=98=EC=97=88=EC=8A=B5=EB=8B=88=EB=8B=A4?= =?UTF-8?q?.=20-=20=EB=A7=A4=EA=B1=B0=EC=A7=84=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EC=8B=9C=20api=EB=A5=BC=20=EC=9D=B4=EC=A0=84=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=A1=A4=EB=B0=B1=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MagazineController.java | 59 ++++++++++--------- .../domain/magazine/dto/MagazineRequest.java | 40 ++++++++++--- .../domain/magazine/entity/Magazine.java | 20 ++++++- .../magazine/service/MagazineService.java | 20 ++----- .../codebase/domain/team/entity/Team.java | 1 + 5 files changed, 83 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/MagazineController.java b/src/main/java/com/example/codebase/controller/MagazineController.java index 3f9e439e..106b038f 100644 --- a/src/main/java/com/example/codebase/controller/MagazineController.java +++ b/src/main/java/com/example/codebase/controller/MagazineController.java @@ -62,7 +62,6 @@ public MagazineController(MagazineService magazineService, MagazineCategoryServi } @Operation(summary = "๋งค๊ฑฐ์ง„ ์ƒ์„ฑ", description = "๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋งŒ ๋งค๊ฑฐ์ง„์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ๋ฏธ๋””์–ด ์ฒจ๋ถ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ JSON ํ˜•์‹์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.", - parameters = @Parameter(name = "action", description = "member: ๊ฐœ์ธ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ, team: ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ", required = true, example = "team"), requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( description = "๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์ž URN", required = true, content = @io.swagger.v3.oas.annotations.media.Content( schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = MagazineRequest.Create.class), @@ -77,15 +76,19 @@ public MagazineController(MagazineService magazineService, MagazineCategoryServi ) @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "๋งค๊ฑฐ์ง„ ์ž‘์„ฑ ์„ฑ๊ณต"), - @ApiResponse(responseCode = "200", description = "๋งค๊ฑฐ์ง„ ์ž‘์„ฑ ์‹คํŒจ") + @ApiResponse(responseCode = "404", description = "๋งค๊ฑฐ์ง„ ์ž‘์„ฑ ์‹คํŒจ") }) @UserOnly @PostMapping - @CheckDuplicatedRequest(target = "magazine") + @CheckDuplicatedRequest() public ResponseEntity createMagazine( @RequestBody @Valid MagazineRequest.Create magazineRequest) { String loginUsername = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); + if (magazineRequest.isDefaultUrn()) { + magazineRequest.addUsernameUrn(loginUsername); + } + MagazineRequest.MagazineEntityUrn entityUrn = MagazineRequest.MagazineEntityUrn.from(magazineRequest.getUrn()); MagazineCategory category = magazineCategoryService.getEntity(magazineRequest.getCategorySlug()); @@ -93,16 +96,19 @@ public ResponseEntity createMagazine( return createMagazine(magazineRequest, loginUsername, category, entityUrn); } - private ResponseEntity createMagazine(MagazineRequest.Create magazineRequest, String username, MagazineCategory category, MagazineRequest.MagazineEntityUrn entityUrn) { + private ResponseEntity createMagazine(MagazineRequest.Create magazineRequest, String loginUsername, MagazineCategory category, MagazineRequest.MagazineEntityUrn entityUrn) { MagazineResponse.Get magazine = null; switch (entityUrn) { case MEMBER -> { - Member member = memberService.getEntity(username); - magazine = magazineService.createMemberMagazine(magazineRequest, member, category); + if (!loginUsername.equals(entityUrn.getId())) { + throw new RuntimeException("์š”์ฒญํ•œ urn๊ณผ ๋กœ๊ทธ์ธํ•œ ์œ ์ €์˜ ์ •๋ณด๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค."); + } + Member member = memberService.getEntity(entityUrn.getId()); + magazine = magazineService.createMagazine(magazineRequest, member, category, null); } case TEAM -> { - TeamUser teamUser = teamUserService.findByTeamIdAndUsername(Long.valueOf(entityUrn.getId()), username); - magazine = magazineService.createTeamMagazine(magazineRequest, teamUser, category); + TeamUser teamUser = teamUserService.findByTeamIdAndUsername(Long.valueOf(entityUrn.getId()), loginUsername); + magazine = magazineService.createMagazine(magazineRequest, teamUser.getMember(), category, teamUser.getTeam()); } } return new ResponseEntity(magazine, HttpStatus.CREATED); @@ -118,37 +124,32 @@ public ResponseEntity getMagazine( return new ResponseEntity(magazine, HttpStatus.OK); } - @Operation(summary = "๋งค๊ฑฐ์ง„ ์ „์ฒด ์กฐํšŒ", description = "ํ•ด๋‹น ์‚ฌ์šฉ์ž ๋˜๋Š” ํŒ€์ด ์ž‘์„ฑํ•œ ๋งค๊ฑฐ์ง„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค( ๋งŒ์•ฝ action, name์ด ์—†๋‹ค๋ฉด ์ „์ฒด ๋งค๊ฑฐ์ง„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค). ํŽ˜์ด์ง€๋„ค์ด์…˜") - @GetMapping - public ResponseEntity getAllMagazines( - @RequestParam("action") Optional<@Pattern(regexp = "team|member", message = "์ž˜๋ชป๋œ action ๊ฐ’์ž…๋‹ˆ๋‹ค.") String> action, - @RequestParam("name") Optional name, + @Operation(summary = "ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ๋งค๊ฑฐ์ง„ ์ „์ฒด ์กฐํšŒ", description = "ํ•ด๋‹น ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ๋งค๊ฑฐ์ง„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€๋„ค์ด์…˜") + @GetMapping("/members/{username}") + public ResponseEntity getMyMagazines( + @PathVariable String username, @PositiveOrZero @RequestParam(value = "page", defaultValue = "0") int page, @PositiveOrZero @RequestParam(value = "size", defaultValue = "10") int size, @RequestParam(defaultValue = "DESC", required = false) String sortDirection ) { PageRequest pageRequest = PageRequest.of(page, size, Sort.by(Sort.Direction.fromString(sortDirection), "createdTime")); + Member member = memberService.getEntity(username); - if (action.isPresent() && name.isPresent()) { - return getAllMagazines(action.get(), name.get(), pageRequest); - } + MagazineResponse.GetAll allMagazines = magazineService.getMemberMagazines(member, pageRequest); - MagazineResponse.GetAll allMagazines = magazineService.getAll(pageRequest); return new ResponseEntity(allMagazines, HttpStatus.OK); } - private ResponseEntity getAllMagazines(String action, String name, PageRequest pageRequest) { - MagazineResponse.GetAll allMagazines = null; - switch (action) { - case ("member") -> { - Member member = memberService.getEntity(name); - allMagazines = magazineService.getMemberMagazines(member, pageRequest); - } - case ("team") -> { - Team team = teamService.getEntity(name); - allMagazines = magazineService.getTeamMagazines(team, pageRequest); - } - } + @Operation(summary = "๋งค๊ฑฐ์ง„ ์ „์ฒด ์กฐํšŒ", description = "ํ•ด๋‹น ์‚ฌ์šฉ์ž ๋˜๋Š” ํŒ€์ด ์ž‘์„ฑํ•œ ๋งค๊ฑฐ์ง„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค( ๋งŒ์•ฝ action, name์ด ์—†๋‹ค๋ฉด ์ „์ฒด ๋งค๊ฑฐ์ง„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค). ํŽ˜์ด์ง€๋„ค์ด์…˜") + @GetMapping + public ResponseEntity getAllMagazines( + @PositiveOrZero @RequestParam(value = "page", defaultValue = "0") int page, + @PositiveOrZero @RequestParam(value = "size", defaultValue = "10") int size, + @RequestParam(defaultValue = "DESC", required = false) String sortDirection + ) { + PageRequest pageRequest = PageRequest.of(page, size, Sort.by(Sort.Direction.fromString(sortDirection), "createdTime")); + + MagazineResponse.GetAll allMagazines = magazineService.getAll(pageRequest); return new ResponseEntity(allMagazines, HttpStatus.OK); } diff --git a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java index b5d58747..0808db07 100644 --- a/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java +++ b/src/main/java/com/example/codebase/domain/magazine/dto/MagazineRequest.java @@ -1,5 +1,7 @@ package com.example.codebase.domain.magazine.dto; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.*; import lombok.Getter; @@ -34,9 +36,19 @@ public static class Create { private List<@URL(message = "์˜ฌ๋ฐ”๋ฅธ URL ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.") String> mediaUrls = Collections.emptyList(); @NotBlank(message = "URN์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.") - @Pattern(regexp = "^urn:(member|team:\\w+)$", message = "์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.") - private String urn; + @JsonIgnoreProperties(ignoreUnknown = true) + @Pattern(regexp = "^urn:[a-z]+:.*$", message = "์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.") + private String urn = "urn:member:"; + @JsonIgnore + public boolean isDefaultUrn() { + return this.getUrn().equals("urn:member:"); + } + + @JsonIgnore + public void addUsernameUrn(String username) { + this.urn = new StringBuilder(this.urn).append(username).toString(); + } } @Getter @@ -65,14 +77,15 @@ public enum MagazineEntityUrn { private final String resource; - @Getter String id; + @Getter + String id; - MagazineEntityUrn(String resource){ + MagazineEntityUrn(String resource) { this.resource = resource; this.id = null; } - public static MagazineEntityUrn from (String urn){ + public static MagazineEntityUrn from(String urn) { try { String[] checkSplit = urn.split(":"); @@ -84,15 +97,24 @@ public static MagazineEntityUrn from (String urn){ MagazineEntityUrn magazineEntityUrn = MagazineEntityUrn.valueOf(resource.toUpperCase()); magazineEntityUrn.id = id; + + if (magazineEntityUrn == TEAM) { + magazineEntityUrn.validTeamId(); + } + return magazineEntityUrn; - } - else{ + } else { throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š์€ EntityUrn ์ž…๋‹ˆ๋‹ค."); } - } - catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { throw new RuntimeException("์œ ํšจํ•˜์ง€ ์•Š์€ EntityUrn ์ž…๋‹ˆ๋‹ค."); } } + + public void validTeamId() { + if (!this.id.matches("\\d+")) { + throw new RuntimeException("Team URN์˜ ID๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } } } diff --git a/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java b/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java index b8868fd2..e13697b9 100644 --- a/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java +++ b/src/main/java/com/example/codebase/domain/magazine/entity/Magazine.java @@ -90,9 +90,10 @@ public class Magazine { @OneToMany(mappedBy = "magazine", cascade = CascadeType.ALL) private List magazineMedias = new ArrayList<>(); + @Builder.Default @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn( name = "team_id") - private Team team; + @JoinColumn(name = "team_id") + private Team team = null; public static Magazine toEntity(MagazineRequest.Create magazineRequest, Member member, MagazineCategory category) { return Magazine.builder() @@ -106,7 +107,7 @@ public static Magazine toEntity(MagazineRequest.Create magazineRequest, Member m .build(); } - public static Magazine toEntity(MagazineRequest.Create magazineRequest, TeamUser teamUser, MagazineCategory category){ + public static Magazine toEntity(MagazineRequest.Create magazineRequest, TeamUser teamUser, MagazineCategory category) { return Magazine.builder() .title(magazineRequest.getTitle()) .content(magazineRequest.getContent()) @@ -119,6 +120,19 @@ public static Magazine toEntity(MagazineRequest.Create magazineRequest, TeamUser .build(); } + public static Magazine toEntity(MagazineRequest.Create magazineRequest, Member member, MagazineCategory category, Team team) { + return Magazine.builder() + .title(magazineRequest.getTitle()) + .content(magazineRequest.getContent()) + .metadata(magazineRequest.getMetadata()) + .member(member) + .category(category) + .team(team) + .createdTime(LocalDateTime.now()) + .updatedTime(LocalDateTime.now()) + .build(); + } + public boolean isOwner(String loginUsername) { return member.getUsername().equals(loginUsername); diff --git a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java index 40833ed1..d799660d 100644 --- a/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java +++ b/src/main/java/com/example/codebase/domain/magazine/service/MagazineService.java @@ -13,6 +13,7 @@ import com.example.codebase.domain.member.entity.Member; import com.example.codebase.domain.team.entity.Team; import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.repository.TeamRepository; import com.example.codebase.exception.NotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -20,6 +21,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Nullable; import java.util.List; @Service @@ -41,18 +43,9 @@ public MagazineService(MagazineRepository magazineRepository, MagazineCommentRep } @Transactional - public MagazineResponse.Get createMemberMagazine(MagazineRequest.Create magazineRequest, Member member, MagazineCategory category) { - Magazine newMagazine = Magazine.toEntity(magazineRequest, member, category); - magazineRepository.save(newMagazine); - - List magazineMedias = MagazineMedia.toList(magazineRequest.getMediaUrls(), newMagazine); - magazineMediaRepository.saveAll(magazineMedias); - return MagazineResponse.Get.from(newMagazine); - } + public MagazineResponse.Get createMagazine(MagazineRequest.Create magazineRequest, Member member, MagazineCategory category, @Nullable Team team) { + Magazine newMagazine = Magazine.toEntity(magazineRequest, member, category, team); - @Transactional - public MagazineResponse.Get createTeamMagazine(MagazineRequest.Create magazineRequest, TeamUser teamMember, MagazineCategory category) { - Magazine newMagazine = Magazine.toEntity(magazineRequest, teamMember, category); magazineRepository.save(newMagazine); List magazineMedias = MagazineMedia.toList(magazineRequest.getMediaUrls(), newMagazine); @@ -182,11 +175,6 @@ public MagazineResponse.GetAll getMemberMagazines(Member member, PageRequest pag return MagazineResponse.GetAll.from(magazines); } - public MagazineResponse.GetAll getTeamMagazines(Team team, PageRequest pageRequest) { - Page magazines = magazineRepository.findByTeam(team, pageRequest); - return MagazineResponse.GetAll.from(magazines); - } - public MagazineResponse.GetAll getFollowingMagazine(Member member, PageRequest pageRequest) { Page magazines = magazineRepository.findByMemberToFollowing(member, pageRequest); return MagazineResponse.GetAll.from(magazines); diff --git a/src/main/java/com/example/codebase/domain/team/entity/Team.java b/src/main/java/com/example/codebase/domain/team/entity/Team.java index c1125554..59f92d7d 100644 --- a/src/main/java/com/example/codebase/domain/team/entity/Team.java +++ b/src/main/java/com/example/codebase/domain/team/entity/Team.java @@ -1,5 +1,6 @@ package com.example.codebase.domain.team.entity; +import com.example.codebase.domain.magazine.entity.Magazine; import com.example.codebase.domain.team.dto.TeamRequest; import jakarta.persistence.*; import lombok.*; From c0a5ffe71648cb401902dd2f0a09c1293176ac25 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Wed, 10 Apr 2024 00:04:54 +0900 Subject: [PATCH 069/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=8B=9C=20Check?= =?UTF-8?q?DuplicatedRequestAspectMock=20=EC=9D=84=20=EB=91=90=EC=96=B4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A4=91=20testid=20username?= =?UTF-8?q?=EC=9D=84=20=EC=82=AC=EC=9A=A9=EC=8B=9C=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=EC=9D=84=20=ED=86=B5=EA=B3=BC=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aop/CheckDuplicatedRequestAspectMock.java | 67 ++++++ .../controller/CurationControllerTest.java | 4 +- .../MagazineCategoryControllerTest.java | 6 +- .../controller/MagazineControllerTest.java | 204 ++++++++++-------- 4 files changed, 190 insertions(+), 91 deletions(-) create mode 100644 src/test/java/com/example/codebase/aop/CheckDuplicatedRequestAspectMock.java diff --git a/src/test/java/com/example/codebase/aop/CheckDuplicatedRequestAspectMock.java b/src/test/java/com/example/codebase/aop/CheckDuplicatedRequestAspectMock.java new file mode 100644 index 00000000..4a3db6f7 --- /dev/null +++ b/src/test/java/com/example/codebase/aop/CheckDuplicatedRequestAspectMock.java @@ -0,0 +1,67 @@ +package com.example.codebase.aop; + +import com.example.codebase.annotation.CheckDuplicatedRequest; +import com.example.codebase.annotation.LoginOnly; +import com.example.codebase.annotation.UserAdminOnly; +import com.example.codebase.annotation.UserOnly; +import com.example.codebase.exception.DuplicatedRequestException; +import com.example.codebase.exception.LoginRequiredException; +import com.example.codebase.util.RedisUtil; +import com.example.codebase.util.SecurityUtil; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.time.LocalDateTime; + +@Profile("test") +@Aspect +@Component +public class CheckDuplicatedRequestAspectMock { + + private final RedisUtil redisUtil; + + @Autowired + private CheckDuplicatedRequestAspectMock(RedisUtil redisUtil) { + this.redisUtil = redisUtil; + } + + @Before("@annotation(com.example.codebase.annotation.CheckDuplicatedRequest)") + public void checkDuplicateRequest(JoinPoint joinPoint) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + CheckDuplicatedRequest annotation = method.getAnnotation(CheckDuplicatedRequest.class); + + if (!isValidAnnotation(method)) return; + + String username = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); + + // ํ…Œ์ŠคํŠธ ์‹œ ํ…Œ์ŠคํŠธ ์œ ์ €๋Š” ์ œ์™ธ + if (username.isEmpty() || username.equals("testid")) { + return; + } + + String target = annotation.target().isEmpty() ? String.valueOf(method) : annotation.target(); + + String uniqueKey = username + "->" + target; + if (redisUtil.getData(uniqueKey).isPresent()) { + throw new DuplicatedRequestException(); + } + redisUtil.setDataAndExpire(uniqueKey, String.valueOf(LocalDateTime.now()), annotation.aliveMillisecondTime()); + } + + private boolean isValidAnnotation(Method method) { + return method.isAnnotationPresent(LoginOnly.class) + || method.isAnnotationPresent(UserOnly.class) + || method.isAnnotationPresent(UserAdminOnly.class); + } + + private boolean isLoginUser() { + return SecurityUtil.getCurrentUsername().isEmpty(); + } +} diff --git a/src/test/java/com/example/codebase/controller/CurationControllerTest.java b/src/test/java/com/example/codebase/controller/CurationControllerTest.java index b008e6de..e1821a9b 100644 --- a/src/test/java/com/example/codebase/controller/CurationControllerTest.java +++ b/src/test/java/com/example/codebase/controller/CurationControllerTest.java @@ -172,7 +172,7 @@ public MagazineResponse.Get createMagazine(Member member) { magazineRequest.setContent("๋‚ด์šฉ"); magazineRequest.setCategorySlug(category.getSlug()); - return magazineService.createMemberMagazine(magazineRequest, member, category); + return magazineService.createMagazine(magazineRequest, member, category, null); } MagazineResponse.Get createMagazine(Member member, MagazineCategory category) { @@ -181,7 +181,7 @@ MagazineResponse.Get createMagazine(Member member, MagazineCategory category) { magazineRequest.setContent("๋‚ด์šฉ"); magazineRequest.setCategorySlug(category.getSlug()); - return magazineService.createMemberMagazine(magazineRequest, member, category); + return magazineService.createMagazine(magazineRequest, member, category, null); } diff --git a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java index 3fcfe856..6e5d96eb 100644 --- a/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineCategoryControllerTest.java @@ -101,7 +101,7 @@ public MagazineResponse.Get createMagaizne(Member member, MagazineCategory categ "https://cdn.artscope.kr/local/2.jpg" )); - return magazineService.createMemberMagazine(magazineRequest, member, category); + return magazineService.createMagazine(magazineRequest, member, category, null); } public Member createMember(String username) { @@ -268,7 +268,7 @@ public void updateCategory() throws Exception { .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); // then - MagazineCategoryResponse.Get updatedCategory =objectMapper.readValue(response, MagazineCategoryResponse.Get.class); + MagazineCategoryResponse.Get updatedCategory = objectMapper.readValue(response, MagazineCategoryResponse.Get.class); assertEquals(updateRequest.getName(), updatedCategory.getName()); assertEquals(updateRequest.getSlug(), updatedCategory.getSlug()); assertEquals(updateRequest.getParentId(), updatedCategory.getParentCategory().getId()); @@ -393,7 +393,7 @@ public void updateCategory() throws Exception { // given MagazineCategory parentCategory = createCategoryAndLoad(); - createMagaizne(createMember("admin"),parentCategory); + createMagaizne(createMember("admin"), parentCategory); // when mockMvc.perform( diff --git a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java index d109a3c8..209c0e1a 100644 --- a/src/test/java/com/example/codebase/controller/MagazineControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MagazineControllerTest.java @@ -112,7 +112,7 @@ public MagazineResponse.Get createMagaizne(Member member) { "https://cdn.artscope.kr/local/2.jpg" )); - return magazineService.createMemberMagazine(magazineRequest, member, category); + return magazineService.createMagazine(magazineRequest, member, category, null); } public MagazineResponse.Get createMagaizne(TeamUser teamUser) { @@ -131,7 +131,7 @@ public MagazineResponse.Get createMagaizne(TeamUser teamUser) { "https://cdn.artscope.kr/local/2.jpg" )); - return magazineService.createTeamMagazine(magazineRequest, teamUser, category); + return magazineService.createMagazine(magazineRequest, teamUser.getMember(), category, teamUser.getTeam()); } public MagazineCategory createCategory() { @@ -187,12 +187,12 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { } - @WithMockCustomUser(username = "testid1", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์ด ๋œ๋‹ค.") @Test void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ() throws Exception { // given - createMember("testid1"); + createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -200,7 +200,6 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); magazineRequest.setCategorySlug(category.getSlug()); - magazineRequest.setUrn("urn:member"); // when String response = mockMvc.perform( @@ -220,17 +219,16 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertEquals(magazine.getCategoryId(), category.getId()); } - @WithMockCustomUser(username = "testid2", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด 400.") @Test void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ_์—๋Ÿฌ() throws Exception { // given - createMember("testid2"); + createMember("testid"); MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); magazineRequest.setTitle("์ œ๋ชฉ"); magazineRequest.setContent("๋‚ด์šฉ"); magazineRequest.setCategorySlug("slug"); - magazineRequest.setUrn("urn:member"); // when String content = mockMvc.perform( @@ -307,12 +305,12 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertTrue(content.contains("ํ•ด๋‹น ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); } - @WithMockCustomUser(username = "testid3", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ˆ˜์ •์ด ๋œ๋‹ค.") @Test void ๋งค๊ฑฐ์ง„_์ˆ˜์ •() throws Exception { // given - Member author = createMember("testid3"); + Member author = createMember("testid"); MagazineResponse.Get magazine = createMagaizne(author); MagazineRequest.Update magazineRequest = new MagazineRequest.Update(); @@ -338,7 +336,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertEquals(magazineRequest.getCategorySlug(), magazineResponse.getCategorySlug()); } - @WithMockCustomUser(username = "testid4", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ˆ˜์ •์‹œ ์—†๋Š” ๋งค๊ฑฐ์ง„์ด๋ฉด 404.") @Test void ๋งค๊ฑฐ์ง„_์ˆ˜์ •_์—๋Ÿฌ() throws Exception { @@ -363,12 +361,12 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertTrue(content.contains("ํ•ด๋‹น ๋งค๊ฑฐ์ง„์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); } - @WithMockCustomUser(username = "testid5", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์‚ญ์ œ๊ฐ€ ๋œ๋‹ค.") @Test void ๋งค๊ฑฐ์ง„_์‚ญ์ œ() throws Exception { // given - Member author = createMember("testid5"); + Member author = createMember("testid"); MagazineResponse.Get magazine = createMagaizne(author); // when @@ -386,7 +384,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { .andExpect(status().isNotFound()); } - @WithMockCustomUser(username = "testid6", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์‚ญ์ œ์‹œ ์—†๋Š” ๋งค๊ฑฐ์ง„์ด๋ฉด 404.") @Test void ๋งค๊ฑฐ์ง„_์‚ญ์ œ_์—๋Ÿฌ() throws Exception { @@ -534,12 +532,12 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { .andExpect(status().isNotFound()); } - @WithMockCustomUser(username = "testid7", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ๋ฏธ๋””์–ด ์ฒจ๋ถ€๊ฐ€ ๋œ๋‹ค.") @Test void ๋งค๊ฑฐ์ง„_๋ฏธ๋””์–ด_์ƒ์„ฑ() throws Exception { // given - createMember("testid7"); + createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -551,7 +549,6 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { "https://cdn.artscope.kr/local/1.jpg", "https://cdn.artscope.kr/local/2.jpg" )); - magazineRequest.setUrn("urn:member"); // when String response = mockMvc.perform( @@ -571,12 +568,12 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertEquals(magazine.getMediaUrls().size(), 2); } - @WithMockCustomUser(username = "testid8", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ์ž˜๋ชป๋œ ๋ฏธ๋””์–ด URL์ด๋ฉด 400.") @Test void ๋งค๊ฑฐ์ง„_๋ฏธ๋””์–ด_์ž˜๋ชป๋œ_์ƒ์„ฑ() throws Exception { // given - createMember("testid8"); + createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -600,12 +597,12 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { .andExpect(status().isBadRequest()); } - @WithMockCustomUser(username = "testid9", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ๋ฏธ๋””์–ด ์ตœ๋Œ€ ๊ฐœ์ˆ˜ ์ด์ƒ ์ฒจ๋ถ€ ์‹œ 400.") @Test void ๋งค๊ฑฐ์ง„_๋ฏธ๋””์–ด_์ž˜๋ชป๋œ_์ƒ์„ฑ2() throws Exception { // given - createMember("testid9"); + createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -638,12 +635,12 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { .andExpect(status().isBadRequest()); } - @WithMockCustomUser(username = "testid10", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ฒจ๋ถ€ํ•œ๋‹ค.") @Test void ๋งค๊ฑฐ์ง„_๋ฉ”ํƒ€๋ฐ์ดํ„ฐ_์ƒ์„ฑ() throws Exception { // given - createMember("testid10"); + createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -655,7 +652,6 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { "color", "blue", "font", "godic" )); - magazineRequest.setUrn("urn:member"); // when mockMvc.perform( @@ -705,7 +701,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertEquals(magazineResponse.getMetadata().get("font"), "๋‹ค๋ฅธ ํฐํŠธ"); } - @DisplayName("ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ๋งค๊ฑฐ์ง„ ์ „์ฒด ์กฐํšŒ ์‹œ") + @DisplayName("๋งค๊ฑฐ์ง„ ์ „์ฒด ์กฐํšŒ ์‹œ") @Test void ํ•ด๋‹น_์‚ฌ์šฉ์ž์˜_๋งค๊ฑฐ์ง„_์ „์ฒด_์กฐํšŒ() throws Exception { // given @@ -718,8 +714,6 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { // when String response = mockMvc.perform( get("/api/magazines", member.getUsername()) - .param("action", "member") - .param("name", member.getUsername()) .param("page", "0") .param("size", "10") ) @@ -764,41 +758,13 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertEquals(magazineList.getMagazines().size(), 3); } - @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ „์ฒด ์กฐํšŒ ์‹œ") - @Test - void ํ•ด๋‹น_ํŒ€_๋งค๊ฑฐ์ง„_์ „์ฒด_์กฐํšŒ() throws Exception { - // given - Member member = createMember("testid"); - TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); - TeamUser teamUser = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); - - createMagaizne(teamUser); - createMagaizne(teamUser); - createMagaizne(teamUser); - - // when - String response = mockMvc.perform( - get("/api/magazines", member.getUsername()) - .param("action", "team") - .param("name", teamUser.getTeam().getName()) - .param("page", "0") - .param("size", "10") - ) - .andDo(print()) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - - // then - MagazineResponse.GetAll magazineList = objectMapper.readValue(response, MagazineResponse.GetAll.class); - assertEquals(magazineList.getMagazines().size(), 3); - } - - @WithMockCustomUser(username = "testid11", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ ์‹œ ์ž˜๋ชป๋œ urn ํ˜•์‹์œผ๋กœ ์š”์ฒญ์‹œ ์‹คํŒจ") @Test void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ_urn_๋ฌธ์ œ_์‹คํŒจ() throws Exception { // given - createMember("testid10"); + createMember("admin"); + Member loginMember = createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -810,10 +776,10 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { "color", "blue", "font", "godic" )); - magazineRequest.setUrn("urn:member:"); - // when1 urn:member: - String response1 =mockMvc.perform( + // when1 urn์— urn:์ด์ƒํ•œ๊ฐ’:id + magazineRequest.setUrn("urn:์ด์ƒํ•œ๊ฐ’:1"); + String response1 = mockMvc.perform( post("/api/magazines") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(magazineRequest)) @@ -823,8 +789,9 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertTrue(response1.contains("์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")); - // when2 urn:member:member - magazineRequest.setUrn("urn:member:member"); + // when2 urn:member urn์„ ์™„์„ฑํ•˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ + createTeam(loginMember, "name"); + magazineRequest.setUrn("urn:member"); String response2 = mockMvc.perform( post("/api/magazines") .contentType(MediaType.APPLICATION_JSON) @@ -833,28 +800,46 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { .andDo(print()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - assertTrue(response1.contains("์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")); + assertTrue(response2.contains("์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")); + } - //when3 urn:asdf - magazineRequest.setUrn("urn:member:asdf"); + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ ์š”์ฒญํ•œ ์œ ์ €์™€ urn ์œ ์ €์ •๋ณด๊ฐ€ ์ผ์น˜ ํ•˜์ง€ ์•Š์„๊ฒฝ์šฐ") + @Test + void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ์‹œ_์š”์ฒญํ•œ_์œ ์ €์™€_urn_์œ ์ €์ •๋ณด๊ฐ€_์ผ์น˜_ํ•˜์ง€_์•Š์„๊ฒฝ์šฐ() throws Exception { + // given + createMember("testid"); + createMember("notMe"); + MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); + MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); - String response3 = mockMvc.perform( + MagazineRequest.Create magazineRequest = new MagazineRequest.Create(); + magazineRequest.setTitle("์ œ๋ชฉ"); + magazineRequest.setContent("๋‚ด์šฉ"); + magazineRequest.setCategorySlug(category.getSlug()); + magazineRequest.setUrn("urn:member:notMe"); + + // when + String response = mockMvc.perform( post("/api/magazines") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(magazineRequest)) ) .andDo(print()) + .andExpect(status().isBadRequest()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - assertTrue(response1.contains("์˜ฌ๋ฐ”๋ฅธ URN ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")); + + //then + assertTrue(response.contains("์š”์ฒญํ•œ urn๊ณผ ๋กœ๊ทธ์ธํ•œ ์œ ์ €์˜ ์ •๋ณด๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.")); } - @WithMockCustomUser(username = "testid12", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ") @Test void ํŒ€_๋งค๊ฑฐ์ง„_์ƒ์„ฑ() throws Exception { // given - Member member = createMember("testid12"); + Member member = createMember("testid"); TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); TeamUser teamUser = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); @@ -889,7 +874,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertEquals(magazine.getCategoryId(), category.getId()); } - @WithMockCustomUser(username = "testid13", role = "USER") + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ ํŒ€์— ์†ํ•ด์žˆ์ง€ ์•Š์€๊ฒฝ์šฐ ์‹คํŒจ") @Test void ํŒ€_๋งค๊ฑฐ์ง„_์ƒ์„ฑ์‹œ_ํ•ด๋‹น_ํŒ€_์œ ์ €๊ฐ€_์•„๋‹Œ๊ฒฝ์šฐ_์‹คํŒจ() throws Exception { @@ -898,7 +883,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); TeamUser teamUser = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); - createMember("testid13"); + createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -923,12 +908,16 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { .andExpect(result -> assertEquals("ํ•ด๋‹น ํŒ€์— ์†ํ•ด์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); } - @WithMockCustomUser(username = "testid14", role = "USER") - @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ ํ•ด๋‹นํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์‹คํŒจ") + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ urn์— string์„ ๊ธฐ์ž… ํ•  ๊ฒฝ์šฐ ์‹คํŒจ") @Test - void ํŒ€_๋งค๊ฑฐ์ง„_์ƒ์„ฑ์‹œ_ํ•ด๋‹นํŒ€์ด_์กด์žฌํ•˜์ง€_์•Š์„_๊ฒฝ์šฐ_์‹คํŒจ() throws Exception { + void ํŒ€_๋งค๊ฑฐ์ง„_์ƒ์„ฑ์‹œ_urn์—_string์„_๊ธฐ์ž…_ํ• _๊ฒฝ์šฐ_์‹คํŒจ() throws Exception { // given - createMember("testid14"); + Member member = createMember("admin"); + TeamResponse.Get team = createTeam(member, "ํŒ€์ด๋ฆ„"); + TeamUser teamUser = teamUserService.findByTeamIdAndUsername(team.getId(), member.getUsername()); + + createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -940,7 +929,7 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { "color", "blue", "font", "godic" )); - magazineRequest.setUrn("urn:team:" + 14); + magazineRequest.setUrn("urn:team:" + team.getName()); // when mockMvc.perform( @@ -949,16 +938,16 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { .content(objectMapper.writeValueAsString(magazineRequest)) ) .andDo(print()) - .andExpect(status().isNotFound()) - .andExpect(result -> assertEquals("ํ•ด๋‹น ํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + .andExpect(status().isBadRequest()) + .andExpect(result -> assertEquals("Team URN์˜ ID๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); } - @WithMockCustomUser(username = "testid15", role = "USER") - @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ api๋ฅผ ์—ฐ์†์œผ๋กœ ํ˜ธ์ถœํ•  ์‹œ ์‹คํŒจ ") + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("ํŒ€ ๋งค๊ฑฐ์ง„ ์ƒ์„ฑ์‹œ ํ•ด๋‹นํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์‹คํŒจ") @Test - void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ_api๋ฅผ_์—ฐ์†์œผ๋กœ_ํ˜ธ์ถœํ• _์‹œ_์‹คํŒจ() throws Exception { + void ํŒ€_๋งค๊ฑฐ์ง„_์ƒ์„ฑ์‹œ_ํ•ด๋‹นํŒ€์ด_์กด์žฌํ•˜์ง€_์•Š์„_๊ฒฝ์šฐ_์‹คํŒจ() throws Exception { // given - createMember("testid15"); + createMember("testid"); MagazineCategoryRequest.Create request = new MagazineCategoryRequest.Create("๊ธ€", "slug", null); MagazineCategoryResponse.Create category = magazineCategoryService.createCategory(request); @@ -970,21 +959,64 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { "color", "blue", "font", "godic" )); - magazineRequest.setUrn("urn:member"); + magazineRequest.setUrn("urn:team:" + 14); + // when mockMvc.perform( post("/api/magazines") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(magazineRequest)) ) .andDo(print()) + .andExpect(status().isNotFound()) + .andExpect(result -> assertEquals("ํ•ด๋‹น ํŒ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.", result.getResolvedException().getMessage())); + } + + @WithMockCustomUser(username = "admin", role = "USER") + @DisplayName("๋งค๊ฑฐ์ง„ ์ƒ์„ฑ api๋ฅผ ์—ฐ์†์œผ๋กœ ํ˜ธ์ถœํ•  ์‹œ ์‹คํŒจ ") + @Test + void ๋งค๊ฑฐ์ง„_์ƒ์„ฑ_api๋ฅผ_์—ฐ์†์œผ๋กœ_ํ˜ธ์ถœํ• _์‹œ_์‹คํŒจ() throws Exception { + // given + createMember("admin"); + MagazineCategoryRequest.Create request1 = new MagazineCategoryRequest.Create("๊ธ€1", "slug1", null); + MagazineCategoryResponse.Create category1 = magazineCategoryService.createCategory(request1); + + MagazineRequest.Create magazineRequest1 = new MagazineRequest.Create(); + magazineRequest1.setTitle("์ œ๋ชฉ"); + magazineRequest1.setContent("๋‚ด์šฉ"); + magazineRequest1.setCategorySlug(category1.getSlug()); + magazineRequest1.setMetadata(Map.of( + "color", "blue", + "font", "godic" + )); + + MagazineCategoryRequest.Create request2 = new MagazineCategoryRequest.Create("๊ธ€2", "slug2", category1.getId()); + MagazineCategoryResponse.Create category2 = magazineCategoryService.createCategory(request2); + + MagazineRequest.Create magazineRequest2 = new MagazineRequest.Create(); + magazineRequest2.setTitle("์ œ๋ชฉ"); + magazineRequest2.setContent("๋‚ด์šฉ"); + magazineRequest2.setCategorySlug(category2.getSlug()); + magazineRequest2.setMetadata(Map.of( + "color", "blue", + "font", "godic" + )); + + //then + // 1์ฐจ ์š”์ฒญ + mockMvc.perform( + post("/api/magazines") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(magazineRequest1)) + ) + .andDo(print()) .andExpect(status().isCreated()); - // when + // 2์ฐจ ์š”์ฒญ String response = mockMvc.perform( post("/api/magazines") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(magazineRequest)) + .content(objectMapper.writeValueAsString(magazineRequest2)) ) .andDo(print()) .andExpect(status().isBadRequest()) From 2e277211cd3b101958c46d2cf0c4f0211933a354 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Wed, 10 Apr 2024 19:34:28 +0900 Subject: [PATCH 070/103] =?UTF-8?q?refactor:=20CheckDuplicatedRequestAspec?= =?UTF-8?q?tMock=20=EA=B4=80=EB=A0=A8=20=EB=B3=80=EA=B2=BD=20-=20CheckDupl?= =?UTF-8?q?icatedRequestAspectMock=EC=97=90=20=EB=8C=80=ED=95=98=EC=97=AC?= =?UTF-8?q?=20=EA=B8=B0=EC=A1=B4=20=EB=A1=9C=EC=A7=81=EC=9D=B8=20CheckDupl?= =?UTF-8?q?icatedRequestAspect=EB=A5=BC=20=EC=83=81=EC=86=8D=EB=B0=9B?= =?UTF-8?q?=EC=95=84=20test=20=EA=B3=84=EC=A0=95=EC=9E=84=EC=9D=84=20?= =?UTF-8?q?=EA=B5=AC=EB=B3=84=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aop/CheckDuplicatedRequestAspect.java | 8 +-- .../aop/CheckDuplicatedRequestAspectMock.java | 56 +++---------------- 2 files changed, 13 insertions(+), 51 deletions(-) diff --git a/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java b/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java index e582c830..99480894 100644 --- a/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java +++ b/src/main/java/com/example/codebase/aop/CheckDuplicatedRequestAspect.java @@ -27,7 +27,7 @@ public class CheckDuplicatedRequestAspect { private final RedisUtil redisUtil; @Autowired - private CheckDuplicatedRequestAspect(RedisUtil redisUtil) { + protected CheckDuplicatedRequestAspect(RedisUtil redisUtil) { this.redisUtil = redisUtil; } @@ -39,13 +39,13 @@ public void checkDuplicateRequest(JoinPoint joinPoint) { if (!isValidAnnotation(method)) return; - String username = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); + String key = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); - if (username.isEmpty()) return; + if (key.isEmpty()) return; String target = annotation.target().isEmpty() ? String.valueOf(method) : annotation.target(); - String uniqueKey = username + "->" + target; + String uniqueKey = key + "->" + target; if (redisUtil.getData(uniqueKey).isPresent()) { throw new DuplicatedRequestException(); } diff --git a/src/test/java/com/example/codebase/aop/CheckDuplicatedRequestAspectMock.java b/src/test/java/com/example/codebase/aop/CheckDuplicatedRequestAspectMock.java index 4a3db6f7..0fbd75c6 100644 --- a/src/test/java/com/example/codebase/aop/CheckDuplicatedRequestAspectMock.java +++ b/src/test/java/com/example/codebase/aop/CheckDuplicatedRequestAspectMock.java @@ -1,67 +1,29 @@ package com.example.codebase.aop; -import com.example.codebase.annotation.CheckDuplicatedRequest; -import com.example.codebase.annotation.LoginOnly; -import com.example.codebase.annotation.UserAdminOnly; -import com.example.codebase.annotation.UserOnly; -import com.example.codebase.exception.DuplicatedRequestException; + import com.example.codebase.exception.LoginRequiredException; import com.example.codebase.util.RedisUtil; import com.example.codebase.util.SecurityUtil; import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; -import java.lang.reflect.Method; -import java.time.LocalDateTime; -@Profile("test") -@Aspect @Component -public class CheckDuplicatedRequestAspectMock { - - private final RedisUtil redisUtil; +@Profile("test") +public class CheckDuplicatedRequestAspectMock extends CheckDuplicatedRequestAspect { @Autowired - private CheckDuplicatedRequestAspectMock(RedisUtil redisUtil) { - this.redisUtil = redisUtil; + public CheckDuplicatedRequestAspectMock(RedisUtil redisUtil) { + super(redisUtil); } - @Before("@annotation(com.example.codebase.annotation.CheckDuplicatedRequest)") + @Override public void checkDuplicateRequest(JoinPoint joinPoint) { - MethodSignature signature = (MethodSignature) joinPoint.getSignature(); - Method method = signature.getMethod(); - CheckDuplicatedRequest annotation = method.getAnnotation(CheckDuplicatedRequest.class); - - if (!isValidAnnotation(method)) return; - String username = SecurityUtil.getCurrentUsername().orElseThrow(LoginRequiredException::new); - - // ํ…Œ์ŠคํŠธ ์‹œ ํ…Œ์ŠคํŠธ ์œ ์ €๋Š” ์ œ์™ธ - if (username.isEmpty() || username.equals("testid")) { - return; + if (!username.equals("testid")) { + super.checkDuplicateRequest(joinPoint); } - - String target = annotation.target().isEmpty() ? String.valueOf(method) : annotation.target(); - - String uniqueKey = username + "->" + target; - if (redisUtil.getData(uniqueKey).isPresent()) { - throw new DuplicatedRequestException(); - } - redisUtil.setDataAndExpire(uniqueKey, String.valueOf(LocalDateTime.now()), annotation.aliveMillisecondTime()); - } - - private boolean isValidAnnotation(Method method) { - return method.isAnnotationPresent(LoginOnly.class) - || method.isAnnotationPresent(UserOnly.class) - || method.isAnnotationPresent(UserAdminOnly.class); - } - - private boolean isLoginUser() { - return SecurityUtil.getCurrentUsername().isEmpty(); } -} +} \ No newline at end of file From bb07c701b63f1ad873d54739e3d5223151b7dbec Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Wed, 10 Apr 2024 19:57:44 +0900 Subject: [PATCH 071/103] =?UTF-8?q?chore:=20MagazineRepository=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=9E=98=EB=AA=BB=EB=90=9C=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=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 --- .../domain/magazine/repository/MagazineRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java index 34468ebe..99547641 100644 --- a/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java +++ b/src/main/java/com/example/codebase/domain/magazine/repository/MagazineRepository.java @@ -27,7 +27,7 @@ public interface MagazineRepository extends JpaRepository { @Query("SELECT m AS magazine, CASE WHEN ml.member.username = :username THEN true ELSE false END As isLiked " + "FROM Magazine m LEFT JOIN MagazineLike ml ON m = ml.magazine AND ml.member.username = :username " + "WHERE m.id = :id") - Optional findMagazineWithLikeeByIdAndMember(Long id, String username); + Optional findMagazineWithLikedByIdAndMember(Long id, String username); @Query("SELECT m AS magazine, false AS isLiked FROM Magazine m WHERE m.isDeleted = false") Page findAllMagazineWithIsLiked(PageRequest pageRequest); @@ -38,7 +38,7 @@ public interface MagazineRepository extends JpaRepository { default Optional findMagazineWithIsLiked(Long id, String username) { if (username != null) { - return findMagazineWithLikeeByIdAndMember(id, username); + return findMagazineWithLikedByIdAndMember(id, username); } return findMagazineWithIsLikedById(id); } From 33d3c31c5b8ab0ebe49f5ac525ae0e5a7206f142 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Fri, 12 Apr 2024 22:50:51 +0900 Subject: [PATCH 072/103] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EC=86=8D=ED=95=9C=20=ED=8C=80=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=ED=99=98=20api=20=EC=B6=94=EA=B0=80=20-?= =?UTF-8?q?=20@GetMapping=20api/teams/members/{username}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/codebase/controller/TeamController.java | 8 ++++++++ .../domain/team/repository/TeamUserRepository.java | 2 ++ .../codebase/domain/team/service/TeamUserService.java | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/src/main/java/com/example/codebase/controller/TeamController.java b/src/main/java/com/example/codebase/controller/TeamController.java index e8c32949..14dadd95 100644 --- a/src/main/java/com/example/codebase/controller/TeamController.java +++ b/src/main/java/com/example/codebase/controller/TeamController.java @@ -88,4 +88,12 @@ public ResponseEntity getTeamUsers(@PathVariable Long teamId) { TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); return new ResponseEntity(response, HttpStatus.OK); } + + @GetMapping("/members/{username}") + @Operation(summary = "์œ ์ €๊ฐ€ ์†ํ•œ ํŒ€ ๋ชฉ๋ก ์กฐํšŒ", description = "์œ ์ €๊ฐ€ ์†ํ•œ ๋ชจ๋“  ํŒ€๋“ค์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity getTeamsByUsername(@PathVariable String username) { + Member member = memberService.getEntity(username); + TeamUserResponse.GetAll response = teamUserService.getTeamsByUsername(member); + return new ResponseEntity(response, HttpStatus.OK); + } } diff --git a/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java index 06495456..1af166a6 100644 --- a/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java +++ b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java @@ -18,4 +18,6 @@ public interface TeamUserRepository extends JpaRepository { List findAllByTeamOrderByRole(Team team); boolean existsByTeamAndMember(Team team, Member member); + + List findByMember(Member member); } diff --git a/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java index d147a559..0726413d 100644 --- a/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java +++ b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java @@ -13,6 +13,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; import java.util.Objects; @Service @@ -79,4 +80,9 @@ public void updateTeamUser(TeamUser member, TeamUser changeMember, TeamUserReque teamUserRepository.save(member); } + @Transactional(readOnly = true) + public TeamUserResponse.GetAll getTeamsByUsername(Member member) { + List teams = teamUserRepository.findByMember(member); + return TeamUserResponse.GetAll.from(teams); + } } From 0c40e52e46bc49a7dffa3d96f0ed71e2d071abed Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Fri, 12 Apr 2024 23:11:11 +0900 Subject: [PATCH 073/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20-=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EC=9C=A0=EC=A0=80=EC=9D=98=20=ED=8C=80=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TeamControllerTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/java/com/example/codebase/controller/TeamControllerTest.java b/src/test/java/com/example/codebase/controller/TeamControllerTest.java index 28a6858a..dd83163f 100644 --- a/src/test/java/com/example/codebase/controller/TeamControllerTest.java +++ b/src/test/java/com/example/codebase/controller/TeamControllerTest.java @@ -6,8 +6,11 @@ import com.example.codebase.domain.member.service.MemberService; import com.example.codebase.domain.team.dto.TeamRequest; import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.dto.TeamUserRequest; import com.example.codebase.domain.team.dto.TeamUserResponse; +import com.example.codebase.domain.team.entity.TeamUser; import com.example.codebase.domain.team.service.TeamService; +import com.example.codebase.domain.team.service.TeamUserService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import lombok.extern.slf4j.Slf4j; @@ -54,6 +57,9 @@ class TeamControllerTest { @Autowired private TeamService teamService; + @Autowired + private TeamUserService teamUserService; + private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach @@ -93,6 +99,13 @@ public TeamResponse.Get createTeam(Member member, String name) { return teamService.createTeam(createTeamRequest(name), member); } + public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { + TeamUserRequest.Create request = new TeamUserRequest.Create( + "ํŒ€์›" + ); + teamUserService.addTeamUser(loginUser, inviteMember, request); + } + @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("ํŒ€ ์ƒ์„ฑ ํ…Œ์ŠคํŠธ") @Test @@ -280,4 +293,30 @@ public TeamResponse.Get createTeam(Member member, String name) { assertEquals(1, teamUserResponse.getTeamUsers().size()); assertEquals("testid", teamUserResponse.getTeamUsers().get(0).getUsername()); } + + @DisplayName("ํ•ด๋‹น ์œ ์ €์˜ ํŒ€ ๋ชฉ๋ก ์กฐํšŒ ") + @Test + void ์œ ์ €๊ฐ€_์†ํ•œ_ํŒ€_์กฐํšŒ() throws Exception { + //given + Member member = createMember("testid"); + Member member2 = createMember("testid2"); + TeamResponse.Get createTeam1 = createTeam(member, "ownerTeam1"); + TeamResponse.Get createTeam2 = createTeam(member, "ownerTeam2"); + TeamResponse.Get inviteTeam = createTeam(member2, "memberTeam1"); + TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(inviteTeam.getId(),member2.getUsername()); + createAndInviteMember(teamOwner, member); + + //when + String response = mockMvc.perform(get("/api/teams/members/" + member.getUsername()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + //then + TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); + assertEquals(3, teamUserResponse.getTeamUsers().size()); + assertEquals("OWNER", teamUserResponse.getTeamUsers().get(0).getRole().name()); + assertEquals("OWNER", teamUserResponse.getTeamUsers().get(1).getRole().name()); + assertEquals("MEMBER", teamUserResponse.getTeamUsers().get(2).getRole().name()); + } } From ab83a5e0b245d60cbc52e394ec3d3530f2136b33 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 16 Apr 2024 15:53:47 +0900 Subject: [PATCH 074/103] =?UTF-8?q?feat:=20=ED=8C=80=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=A1=B0=ED=9A=8C=20dto=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 --- .../domain/team/dto/TeamResponse.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java b/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java index 5dc15572..d8d15ce6 100644 --- a/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java @@ -46,4 +46,23 @@ public static TeamResponse.Get from(Team team) { return get; } } + + @Getter + @Setter + @Schema(name = "TeamResponse.ProfileGet", description = "ํŒ€ ํ”„๋กœํŒŒ์ผ ์กฐํšŒ DTO") + public static class ProfileGet{ + private Long id; + + private String profileImage; + + private String name; + + public static TeamResponse.ProfileGet from(Team team){ + ProfileGet profileGet = new ProfileGet(); + profileGet.setId(team.getId()); + profileGet.setProfileImage(team.getProfileImage()); + profileGet.setName(team.getName()); + return profileGet; + } + } } From 0f281cf18a153a024fd0283d63c46c16686cdca8 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 16 Apr 2024 15:54:21 +0900 Subject: [PATCH 075/103] =?UTF-8?q?feat:=20TeamUserResponse.Get=EC=97=90?= =?UTF-8?q?=20teamId=20=EB=B0=98=ED=99=98=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/codebase/domain/team/dto/TeamUserResponse.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java b/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java index c1ff646d..44a8cfb0 100644 --- a/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java @@ -30,6 +30,8 @@ public static class Get{ private TeamUserRole role; + private Long teamId; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createdTime; @@ -45,6 +47,7 @@ public static TeamUserResponse.Get from(TeamUser teamUser) { get.setRole(teamUser.getRole()); get.setCreatedTime(teamUser.getCreatedTime()); get.setUpdatedTime(teamUser.getUpdatedTime()); + get.setTeamId(teamUser.getId()); return get; } } From 383fb0dd28d273d748ef22fa5d563ffa6c01e447 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 16 Apr 2024 15:55:03 +0900 Subject: [PATCH 076/103] =?UTF-8?q?refactor:=20@Transactional=20springfame?= =?UTF-8?q?work=EC=97=90=EC=84=9C=20=EC=A7=80=EC=9B=90=ED=95=98=EB=8A=94?= =?UTF-8?q?=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=EC=9C=BC?= =?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 --- .../example/codebase/domain/member/service/MemberService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/member/service/MemberService.java b/src/main/java/com/example/codebase/domain/member/service/MemberService.java index 95742bfc..af0469dc 100644 --- a/src/main/java/com/example/codebase/domain/member/service/MemberService.java +++ b/src/main/java/com/example/codebase/domain/member/service/MemberService.java @@ -19,7 +19,7 @@ import com.example.codebase.domain.notification.repository.NotificationSettingRepository; import com.example.codebase.s3.S3Service; import com.example.codebase.util.RedisUtil; -import jakarta.transaction.Transactional; +import org.springframework.transaction.annotation.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -164,6 +164,7 @@ public MemberResponseDTO createCurator(CreateCuratorMemberDTO createCuratorMembe return MemberResponseDTO.from(member); } + @Transactional(readOnly = true) public MemberResponseDTO getProfile(String username) { Member member = memberRepository.findByUsername(username).orElseThrow(NotFoundMemberException::new); From de1f0f54ea2499fbc71d573180334ac6c9ccceef Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 16 Apr 2024 15:55:57 +0900 Subject: [PATCH 077/103] =?UTF-8?q?feat:=20MemberResponseDTO=EC=97=90=20?= =?UTF-8?q?=ED=8C=80=20=EC=A0=95=EB=B3=B4=EB=8F=84=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/dto/MemberResponseDTO.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java index 1c45d703..325f919b 100644 --- a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java @@ -1,11 +1,16 @@ package com.example.codebase.domain.member.dto; import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.entity.TeamUser; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Getter; import lombok.Setter; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + @Getter @Setter @@ -50,6 +55,8 @@ public class MemberResponseDTO { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime updatedTime; + private List teams = new ArrayList<>(); + public static MemberResponseDTO from(Member member) { MemberResponseDTO dto = new MemberResponseDTO(); dto.setUsername(member.getUsername()); @@ -83,6 +90,12 @@ public static MemberResponseDTO from(Member member) { dto.setAllowEmailReceive(member.isAllowEmailReceive()); dto.setAllowEmailReceiveDateTime(member.getAllowEmailReceiveDatetime()); + if (member.getTeamUserRoles() != null) { + for (TeamUser teamUser : member.getTeamUserRoles()) { + dto.teams.add(TeamResponse.ProfileGet.from(teamUser.getTeam())); + } + } + return dto; } From 7b51598af88bd3ba0b6a0a5d3e399f0ca66f4dde Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 16 Apr 2024 15:57:03 +0900 Subject: [PATCH 078/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20-=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=20=ED=94=84=EB=A1=9C=ED=95=84=EC=9D=84=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=ED=95=A0=EB=96=84=20=ED=95=B4=EB=8B=B9=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=EC=9D=98=20=ED=8C=80=20=EB=AA=A9=EB=A1=9D=EB=8F=84=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4=20-=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EC=9C=A0=EC=A0=80=EC=9D=98=20=ED=8C=80=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=EC=9D=84=20=EC=A1=B0=ED=9A=8C=ED=95=A0=20=EB=95=8C=20?= =?UTF-8?q?=ED=8C=80=20teamId=EB=8F=84=20=ED=95=A8=EA=BB=98=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MemberControllerTest.java | 89 +++++++++++++++++-- .../controller/TeamControllerTest.java | 3 + 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/MemberControllerTest.java b/src/test/java/com/example/codebase/controller/MemberControllerTest.java index 7b0aa903..92605157 100644 --- a/src/test/java/com/example/codebase/controller/MemberControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MemberControllerTest.java @@ -3,17 +3,22 @@ import com.amazonaws.services.s3.AmazonS3; import com.example.codebase.config.S3MockConfig; import com.example.codebase.domain.auth.WithMockCustomUser; -import com.example.codebase.domain.member.dto.CreateArtistMemberDTO; -import com.example.codebase.domain.member.dto.CreateCuratorMemberDTO; -import com.example.codebase.domain.member.dto.CreateMemberDTO; -import com.example.codebase.domain.member.dto.UpdateMemberDTO; +import com.example.codebase.domain.member.dto.*; import com.example.codebase.domain.member.entity.Authority; import com.example.codebase.domain.member.entity.Member; import com.example.codebase.domain.member.entity.MemberAuthority; import com.example.codebase.domain.member.entity.RoleStatus; import com.example.codebase.domain.member.repository.MemberAuthorityRepository; import com.example.codebase.domain.member.repository.MemberRepository; +import com.example.codebase.domain.team.dto.TeamRequest; +import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.dto.TeamUserResponse; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.repository.TeamUserRepository; +import com.example.codebase.domain.team.service.TeamService; +import com.example.codebase.domain.team.service.TeamUserService; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.findify.s3mock.S3Mock; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.*; @@ -36,10 +41,12 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.time.LocalDateTime; import java.util.Optional; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -73,6 +80,9 @@ class MemberControllerTest { @Autowired private MemberRepository memberRepository; + @Autowired + private TeamService teamService; + @BeforeAll static void setUp(@Autowired S3Mock s3Mock, @Autowired AmazonS3 amazonS3, @@ -91,7 +101,11 @@ static void tearDown(@Autowired S3Mock s3Mock, @Autowired AmazonS3 amazonS3) { @BeforeEach public void setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build(); + mockMvc = MockMvcBuilders + .webAppContextSetup(context) + .apply(springSecurity()) + .build(); + objectMapper.registerModule(new JavaTimeModule()); } public Member createOrLoadMember() { @@ -135,6 +149,20 @@ private byte[] createImageFile() throws IOException { return Files.readAllBytes(file.toPath()); } + public TeamResponse.Get createTeam(Member member, String name) { + return teamService.createTeam(createTeamRequest(name), member); + } + + public TeamRequest.Create createTeamRequest(String name) { + return new TeamRequest.Create( + name, + "ํŒ€ ์ฃผ์†Œ", + "http://test.com/profile.jpg", + "http://test.com/background.jpg", + "ํŒ€์†Œ๊ฐœ", + "์ž์‹ ์˜ ํฌ์ง€์…˜, ์ง๊ธ‰" + ); + } @DisplayName("ํšŒ์›๊ฐ€์ž… API๊ฐ€ ์ž‘๋™ํ•œ๋‹ค") @Test @@ -572,7 +600,7 @@ void create_curator() throws Exception { mockMvc .perform( - put("/api/members/" + member.getUsername() +"/email-receive") + put("/api/members/" + member.getUsername() + "/email-receive") .param("emailReceive", "true") ) .andDo(print()) @@ -602,4 +630,53 @@ void create_curator() throws Exception { .andExpect(status().isOk()); } + @DisplayName("ํšŒ์› ํ”„๋กœํ•„ ์กฐํšŒ์‹œ ํŒ€ ๋ชฉ๋ก ๋ฐ˜ํ™˜ ์„ฑ๊ณต") + @WithMockCustomUser(username = "testid1", role = "USER") + @Test + void ํšŒ์›_ํ”„๋กœํ•„_ํŒ€_์กฐํšŒ์‹œ_ํŒ€_๋ฐ˜ํ™˜_ํ™•์ธ() throws Exception { + // given + Member member = createOrLoadMember(); + TeamResponse.Get team1 = createTeam(member, "ํŒ€์ด๋ฆ„1"); + TeamResponse.Get team2 = createTeam(member, "ํŒ€์ด๋ฆ„2"); + + // when + String response = mockMvc.perform( + get("/api/members/profile") + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + // then + MemberResponseDTO memberResponse = objectMapper.readValue(response, MemberResponseDTO.class); + assertEquals(2, memberResponse.getTeams().size()); + assertEquals(team1.getName(), memberResponse.getTeams().get(0).getName()); + assertEquals(team2.getName(), memberResponse.getTeams().get(1).getName()); + } + + @DisplayName("์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์กฐํšŒ ์‹œ ํŒ€ ๋ฐ˜ํ™˜ ์„ฑ๊ณต") + @Test + void ์‚ฌ์šฉ์ž_ํ”„๋กœํ•„_์กฐํšŒ์‹œ_ํŒ€_๋ฐ˜ํ™˜_ํ™•์ธ() throws Exception { + // given + Member member = createOrLoadMember(); + TeamResponse.Get team1 = createTeam(member, "ํŒ€์ด๋ฆ„1"); + TeamResponse.Get team2 = createTeam(member, "ํŒ€์ด๋ฆ„2"); + + // when + String response = mockMvc.perform( + get(String.format("/api/members/%s", member.getUsername())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); + + + // then + MemberResponseDTO memberResponse = objectMapper.readValue(response, MemberResponseDTO.class); + assertEquals(2, memberResponse.getTeams().size()); + assertEquals(team1.getName(), memberResponse.getTeams().get(0).getName()); + assertEquals(team2.getName(), memberResponse.getTeams().get(1).getName()); + } } diff --git a/src/test/java/com/example/codebase/controller/TeamControllerTest.java b/src/test/java/com/example/codebase/controller/TeamControllerTest.java index dd83163f..8a4d1cc7 100644 --- a/src/test/java/com/example/codebase/controller/TeamControllerTest.java +++ b/src/test/java/com/example/codebase/controller/TeamControllerTest.java @@ -316,7 +316,10 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); assertEquals(3, teamUserResponse.getTeamUsers().size()); assertEquals("OWNER", teamUserResponse.getTeamUsers().get(0).getRole().name()); + assertEquals(createTeam1.getId(),teamUserResponse.getTeamUsers().get(0).getTeamId()); assertEquals("OWNER", teamUserResponse.getTeamUsers().get(1).getRole().name()); + assertEquals(createTeam2.getId(),teamUserResponse.getTeamUsers().get(1).getTeamId()); assertEquals("MEMBER", teamUserResponse.getTeamUsers().get(2).getRole().name()); + assertEquals(inviteTeam.getId(),teamUserResponse.getTeamUsers().get(2).getTeamId()); } } From 9384045a1d16a25c311bec2f355a553a76e3fb13 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 16 Apr 2024 19:33:12 +0900 Subject: [PATCH 079/103] =?UTF-8?q?fix:=20TeamUserResponse=EA=B0=80=20?= =?UTF-8?q?=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EB=90=9C=20teamId=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/codebase/domain/team/dto/TeamUserResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java b/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java index 44a8cfb0..c46bf339 100644 --- a/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamUserResponse.java @@ -47,7 +47,7 @@ public static TeamUserResponse.Get from(TeamUser teamUser) { get.setRole(teamUser.getRole()); get.setCreatedTime(teamUser.getCreatedTime()); get.setUpdatedTime(teamUser.getUpdatedTime()); - get.setTeamId(teamUser.getId()); + get.setTeamId(teamUser.getTeam().getId()); return get; } } From 52f4229906d1c1f8f4d4fc85e4808a5a1fdc11a4 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 16 Apr 2024 19:33:53 +0900 Subject: [PATCH 080/103] =?UTF-8?q?refactor:=20member=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=9D=98=20teamUserRoles=EB=A5=BC=20teamUser=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/domain/member/dto/MemberResponseDTO.java | 4 ++-- .../com/example/codebase/domain/member/entity/Member.java | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java index 325f919b..6fcfaa14 100644 --- a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java @@ -90,8 +90,8 @@ public static MemberResponseDTO from(Member member) { dto.setAllowEmailReceive(member.isAllowEmailReceive()); dto.setAllowEmailReceiveDateTime(member.getAllowEmailReceiveDatetime()); - if (member.getTeamUserRoles() != null) { - for (TeamUser teamUser : member.getTeamUserRoles()) { + if (member.getTeamUser() != null) { + for (TeamUser teamUser : member.getTeamUser()) { dto.teams.add(TeamResponse.ProfileGet.from(teamUser.getTeam())); } } diff --git a/src/main/java/com/example/codebase/domain/member/entity/Member.java b/src/main/java/com/example/codebase/domain/member/entity/Member.java index 0b4b2f40..d9135260 100644 --- a/src/main/java/com/example/codebase/domain/member/entity/Member.java +++ b/src/main/java/com/example/codebase/domain/member/entity/Member.java @@ -125,7 +125,7 @@ public class Member { @Builder.Default @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE, orphanRemoval = true) - private List teamUserRoles = new ArrayList<>(); + private List teamUser = new ArrayList<>(); public static User toUser(Member member) { return new User(member.getUsername(), member.getPassword(), member.getAuthorities().stream() @@ -326,11 +326,14 @@ public void setNotificationSetting(NotificationSetting notificationSetting) { @PreRemove private void preRemove() { - for(TeamUser teamUser : teamUserRoles) { + for(TeamUser teamUser : teamUser) { if(teamUser.isOwner()) { throw new RuntimeException("ํŒ€์žฅ์ธ ํŒ€์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ํŒ€์žฅ์„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ํŒ€์„ ์‚ญ์ œํ•œ ํ›„์— ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."); } } } + public void addTeamUser(TeamUser teamUser) { + this.teamUser.add(teamUser); + } } From c33020a56a91c071274f1604666fcda30dd1fb93 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 16 Apr 2024 19:35:30 +0900 Subject: [PATCH 081/103] =?UTF-8?q?refactor:=20teamUser=EA=B3=BC=20member?= =?UTF-8?q?=EA=B0=84=EC=9D=98=20=EC=96=91=EB=B0=A9=ED=96=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/codebase/domain/team/entity/TeamUser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java b/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java index aa1b4573..2e5ac5ea 100644 --- a/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java +++ b/src/main/java/com/example/codebase/domain/team/entity/TeamUser.java @@ -49,12 +49,14 @@ public class TeamUser { private LocalDateTime updatedTime = LocalDateTime.now(); public static TeamUser toEntity(String position, Member member, Team team, TeamUserRole role) { - return TeamUser.builder() + TeamUser teamUser = TeamUser.builder() .member(member) .team(team) .role(role) .position(position) .build(); + member.addTeamUser(teamUser); + return teamUser; } public void validOwner() { From af22f2c1786457ee4490bbf1f2586c3acd164eb4 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Tue, 16 Apr 2024 20:20:02 +0900 Subject: [PATCH 082/103] =?UTF-8?q?refactor:=20MemberResponseDTO=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20-=20member.getTeamUser()=EC=97=90=EC=84=9C?= =?UTF-8?q?=20null=20=EC=B2=B4=ED=81=AC=EB=A5=BC=20isEmpty()=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 --- .../example/codebase/domain/member/dto/MemberResponseDTO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java index 6fcfaa14..cc5bbc8d 100644 --- a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java @@ -90,7 +90,7 @@ public static MemberResponseDTO from(Member member) { dto.setAllowEmailReceive(member.isAllowEmailReceive()); dto.setAllowEmailReceiveDateTime(member.getAllowEmailReceiveDatetime()); - if (member.getTeamUser() != null) { + if (!member.getTeamUser().isEmpty()) { for (TeamUser teamUser : member.getTeamUser()) { dto.teams.add(TeamResponse.ProfileGet.from(teamUser.getTeam())); } From a1cb764a4642dada11c1149887cae756cb53afb1 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Thu, 18 Apr 2024 19:33:59 +0900 Subject: [PATCH 083/103] =?UTF-8?q?feat:=20=EB=82=B4=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20API=20->=20AuthController=20=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EA=B4=80=20=EB=B0=8F=20URL=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ๊ธฐ์กด GET /api/members/profile - ์ดํ›„ GET /api/auth/me - ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ˆ˜์ • - ์ฝ”๋“œ ํฌ๋งทํŒ… --- .../codebase/controller/AuthController.java | 55 +++-- .../codebase/controller/MemberController.java | 45 +--- .../controller/AuthControllerTest.java | 64 +++--- .../controller/MemberControllerTest.java | 197 +++++++----------- 4 files changed, 162 insertions(+), 199 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/AuthController.java b/src/main/java/com/example/codebase/controller/AuthController.java index f17e74a8..a7e9027f 100644 --- a/src/main/java/com/example/codebase/controller/AuthController.java +++ b/src/main/java/com/example/codebase/controller/AuthController.java @@ -1,23 +1,32 @@ package com.example.codebase.controller; +import static com.example.codebase.util.SecurityUtil.getCookieAccessTokenValue; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.context.SecurityContextHolder; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + import com.example.codebase.domain.auth.dto.LoginDTO; import com.example.codebase.domain.auth.dto.TokenResponseDTO; import com.example.codebase.domain.auth.service.AuthService; +import com.example.codebase.domain.member.dto.MemberResponseDTO; +import com.example.codebase.domain.member.service.MemberService; import com.example.codebase.jwt.TokenProvider; +import com.example.codebase.util.SecurityUtil; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.*; - import jakarta.validation.Valid; -import static com.example.codebase.util.SecurityUtil.getCookieAccessTokenValue; - @Tag(name = "Auth", description = "์ธ์ฆ API") @RestController @RequestMapping("/api") @@ -27,9 +36,12 @@ public class AuthController { private final AuthService authService; - public AuthController(TokenProvider tokenProvider, AuthService authService) { + private final MemberService memberService; + + public AuthController(TokenProvider tokenProvider, AuthService authService, MemberService memberService) { this.tokenProvider = tokenProvider; this.authService = authService; + this.memberService = memberService; } @Operation(summary = "๋กœ๊ทธ์ธ", description = "๋กœ๊ทธ์ธ") @@ -39,8 +51,8 @@ public ResponseEntity login(@Valid @RequestBody LoginDTO loginDTO) { // Set Cookie return ResponseEntity.ok() - .header("Set-Cookie", getCookieAccessTokenValue(responseDTO)) - .body(responseDTO); + .header("Set-Cookie", getCookieAccessTokenValue(responseDTO)) + .body(responseDTO); } @Operation(summary = "๋กœ๊ทธ์•„์›ƒ", description = "๋กœ๊ทธ์•„์›ƒ") @@ -57,20 +69,29 @@ public ResponseEntity logout() { public ResponseEntity refresh(@RequestBody String refreshToken) { TokenResponseDTO responseDTO = tokenProvider.regenerateToken(refreshToken); return ResponseEntity.ok() - .header("Set-Cookie", getCookieAccessTokenValue(responseDTO)) - .body(responseDTO); + .header("Set-Cookie", getCookieAccessTokenValue(responseDTO)) + .body(responseDTO); } @Operation(summary = "์ด๋ฉ”์ผ ์ธ์ฆ", description = "์ด๋ฉ”์ผ ์ธ์ฆ") @ApiResponses({ - @ApiResponse(responseCode = "200", description = "์ด๋ฉ”์ผ ์ธ์ฆ ์„ฑ๊ณต"), - @ApiResponse(responseCode = "400", description = "์ด๋ฉ”์ผ ์ธ์ฆ ์‹คํŒจ") + @ApiResponse(responseCode = "200", description = "์ด๋ฉ”์ผ ์ธ์ฆ ์„ฑ๊ณต"), + @ApiResponse(responseCode = "400", description = "์ด๋ฉ”์ผ ์ธ์ฆ ์‹คํŒจ") }) @PreAuthorize("permitAll()") @GetMapping("/mail/authenticate") public ResponseEntity authenticateMailLink( - @RequestParam String code) { + @RequestParam String code) { authService.authenticateMail(code); return new ResponseEntity("์ด๋ฉ”์ผ ์ธ์ฆ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", HttpStatus.OK); } -} \ No newline at end of file + + @Operation(summary = "๋‚ด ํ”„๋กœํ•„ ์กฐํšŒ", description = "[USER] ๋‚ด ํ”„๋กœํ•„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") + @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER')") + @GetMapping("/auth/me") + public ResponseEntity getProfile() { + String username = SecurityUtil.getCurrentUsernameValue(); + MemberResponseDTO member = memberService.getProfile(username); + return new ResponseEntity(member, HttpStatus.OK); + } +} diff --git a/src/main/java/com/example/codebase/controller/MemberController.java b/src/main/java/com/example/codebase/controller/MemberController.java index 5041a8ca..0ae7106d 100644 --- a/src/main/java/com/example/codebase/controller/MemberController.java +++ b/src/main/java/com/example/codebase/controller/MemberController.java @@ -34,7 +34,6 @@ public class MemberController { private final MemberService memberService; - @Autowired public MemberController(MemberService memberService) { this.memberService = memberService; @@ -78,7 +77,7 @@ public ResponseEntity createCurator(@Valid @RequestBody CreateCuratorMemberDTO c @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER')") @PutMapping("/{uesrname}") public ResponseEntity updateMember(@PathVariable String uesrname, - @Valid @RequestBody UpdateMemberDTO updateMemberDTO) { + @Valid @RequestBody UpdateMemberDTO updateMemberDTO) { String loginUsername = SecurityUtil.getCurrentUsername() .orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); @@ -95,8 +94,7 @@ public ResponseEntity updateMember(@PathVariable String uesrname, @PutMapping("/{username}/picture") public ResponseEntity updateProfile( @PathVariable String username, - @RequestPart MultipartFile profile - ) throws Exception { + @RequestPart MultipartFile profile) throws Exception { String currentUsername = SecurityUtil.getCurrentUsername() .orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); if (!currentUsername.equals(username)) { @@ -121,8 +119,7 @@ public ResponseEntity updateProfile( public ResponseEntity getAllMember( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, - @RequestParam(defaultValue = "DESC", required = false) String sortDirection - ) { + @RequestParam(defaultValue = "DESC", required = false) String sortDirection) { Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), "createdTime"); PageRequest pageRequest = PageRequest.of(page, size, sort); @@ -130,35 +127,20 @@ public ResponseEntity getAllMember( return new ResponseEntity(members, HttpStatus.OK); } - @Operation(summary = "์—ญํ•  ์ƒํƒœ๋กœ ํšŒ์› ์ „์ฒด ์กฐํšŒ", description = "[ADMIN] ์—ญํ•  ์ƒํƒœ๋กœ ํšŒ์› ์ „์ฒด ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.", - parameters = @Parameter( - name = "roleStatus", - description = "์—ญํ•  ์ƒํƒœ", - example = "NONE | PENDING | REJECTED | ARTIST | CURATOR") - ) + @Operation(summary = "์—ญํ•  ์ƒํƒœ๋กœ ํšŒ์› ์ „์ฒด ์กฐํšŒ", description = "[ADMIN] ์—ญํ•  ์ƒํƒœ๋กœ ํšŒ์› ์ „์ฒด ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.", parameters = @Parameter(name = "roleStatus", description = "์—ญํ•  ์ƒํƒœ", example = "NONE | PENDING | REJECTED | ARTIST | CURATOR")) @PreAuthorize("hasAnyRole('ROLE_ADMIN')") @GetMapping("/role-status") public ResponseEntity getAllRoleStatusMember( @RequestParam(defaultValue = "NONE") @NotBlank(message = "์—ญํ•  ์ƒํƒœ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") String roleStatus, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, - @RequestParam(defaultValue = "DESC", required = false) String sortDirection - ) { + @RequestParam(defaultValue = "DESC", required = false) String sortDirection) { Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), "createdTime"); PageRequest pageRequest = PageRequest.of(page, size, sort); MembersResponseDTO members = memberService.getAllRoleStatusMember(roleStatus, pageRequest); return new ResponseEntity(members, HttpStatus.OK); } - @Operation(summary = "๋‚ด ํ”„๋กœํ•„ ์กฐํšŒ", description = "[USER] ๋‚ด ํ”„๋กœํ•„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") - @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER')") - @GetMapping("/profile") - public ResponseEntity getProfile() { - String username = SecurityUtil.getCurrentUsername().orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); - MemberResponseDTO member = memberService.getProfile(username); - return new ResponseEntity(member, HttpStatus.OK); - } - @Operation(summary = "ํšŒ์› ํ”„๋กœํ•„ ์กฐํšŒ", description = "[USER] ํšŒ์›์˜ ํ”„๋กœํ•„์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") @GetMapping("/{username}") public ResponseEntity getProfile(@PathVariable String username) { @@ -199,16 +181,11 @@ public ResponseEntity checkUsername(@Valid @NotBlank @PathVariable String userna return new ResponseEntity("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์•„์ด๋”” ์ž…๋‹ˆ๋‹ค.", HttpStatus.OK); } - @Operation(summary = "ํšŒ์› ์—ญํ•  ์ƒํƒœ ๋ณ€๊ฒฝ", description = "[ADMIN] ํ•ด๋‹น ํšŒ์›์˜ ์—ญํ•  ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.", - parameters = @Parameter( - name = "roleStatus", - description = "์—ญํ•  ์ƒํƒœ", - example = "NONE | ARTIST_PENDING | ARTIST_REJECTED | ARTIST | CURATOR_PENDING | CURATOR_REJECTED | CURATOR") - ) + @Operation(summary = "ํšŒ์› ์—ญํ•  ์ƒํƒœ ๋ณ€๊ฒฝ", description = "[ADMIN] ํ•ด๋‹น ํšŒ์›์˜ ์—ญํ•  ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.", parameters = @Parameter(name = "roleStatus", description = "์—ญํ•  ์ƒํƒœ", example = "NONE | ARTIST_PENDING | ARTIST_REJECTED | ARTIST | CURATOR_PENDING | CURATOR_REJECTED | CURATOR")) @PreAuthorize("hasAnyRole('ROLE_ADMIN')") @PutMapping("/{username}/role-status") public ResponseEntity updateRoleStatus(@PathVariable @Valid @NotBlank String username, - @RequestParam @Valid @NotBlank(message = "์—ญํ•  ์ƒํƒœ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") String roleStatus) { + @RequestParam @Valid @NotBlank(message = "์—ญํ•  ์ƒํƒœ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") String roleStatus) { MemberResponseDTO member = memberService.updateRoleStatus(username, RoleStatus.create(roleStatus)); return new ResponseEntity(member, HttpStatus.OK); } @@ -217,7 +194,7 @@ public ResponseEntity updateRoleStatus(@PathVariable @Valid @NotBlank String use @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER')") @PutMapping("/{username}/username") public ResponseEntity updateUsername(@PathVariable String username, - @Valid @RequestParam UsernameDTO newUsername) { + @Valid @RequestParam UsernameDTO newUsername) { String currentUsername = SecurityUtil.getCurrentUsername() .orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); if (!SecurityUtil.isAdmin() && !currentUsername.equals(username)) { // ๊ด€๋ฆฌ์ž๊ฐ€ ์•„๋‹ˆ๊ณ , ๋ณธ์ธ์˜ ์•„์ด๋””๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ @@ -231,7 +208,7 @@ public ResponseEntity updateUsername(@PathVariable String username, @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER', 'ROLE_ADMIN')") @PutMapping("/{username}/password") public ResponseEntity updatePassword(@PathVariable String username, - @NotBlank @RequestParam(value = "newPassword") String newPassword) { + @NotBlank @RequestParam(value = "newPassword") String newPassword) { String currentUsername = SecurityUtil.getCurrentUsername() .orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); @@ -262,7 +239,7 @@ public ResponseEntity searchMember(@PathVariable String username) { @Operation(summary = "๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •", description = "๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ํ•ฉ๋‹ˆ๋‹ค.") @PostMapping("/reset-password") public ResponseEntity resetPassword(@RequestParam @NotBlank String code, - @RequestBody PasswordResetRequestDTO password) { + @RequestBody PasswordResetRequestDTO password) { memberService.resetPassword(code, password); @@ -273,7 +250,7 @@ public ResponseEntity resetPassword(@RequestParam @NotBlank String code, @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER')") @PutMapping("/{username}/email-receive") public ResponseEntity updateEmailReceive(@PathVariable String username, - @RequestParam boolean emailReceive) { + @RequestParam boolean emailReceive) { String currentUsername = SecurityUtil.getCurrentUsername() .orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); diff --git a/src/test/java/com/example/codebase/controller/AuthControllerTest.java b/src/test/java/com/example/codebase/controller/AuthControllerTest.java index 851ab13e..0dea2b76 100644 --- a/src/test/java/com/example/codebase/controller/AuthControllerTest.java +++ b/src/test/java/com/example/codebase/controller/AuthControllerTest.java @@ -67,9 +67,9 @@ class AuthControllerTest { @BeforeEach public void setUp() { mockMvc = MockMvcBuilders - .webAppContextSetup(context) - .apply(springSecurity()) - .build(); + .webAppContextSetup(context) + .apply(springSecurity()) + .build(); } public Member createOrLoadMember() { @@ -83,13 +83,13 @@ public Member createOrLoadMember(boolean activated) { } Member dummy = Member.builder() - .username("testid") - .password(passwordEncoder.encode("1234")) - .email("email") - .name("test") - .activated(activated) - .createdTime(LocalDateTime.now()) - .build(); + .username("testid") + .password(passwordEncoder.encode("1234")) + .email("email") + .name("test") + .activated(activated) + .createdTime(LocalDateTime.now()) + .build(); MemberAuthority memberAuthority = new MemberAuthority(); memberAuthority.setAuthority(Authority.of("ROLE_USER")); @@ -111,11 +111,10 @@ public void test1() throws Exception { mockMvc.perform( post("/api/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginDTO)) - ) - .andDo(print()) - .andExpect(status().isOk()); + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(loginDTO))) + .andDo(print()) + .andExpect(status().isOk()); } @DisplayName("ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ ์‹œ") @@ -131,11 +130,10 @@ public void test2() throws Exception { mockMvc.perform( post("/api/refresh") - .contentType(MediaType.APPLICATION_JSON) - .content(refreshToken) - ) - .andDo(print()) - .andExpect(status().isOk()); + .contentType(MediaType.APPLICATION_JSON) + .content(refreshToken)) + .andDo(print()) + .andExpect(status().isOk()); } @WithMockCustomUser(username = "testid") @@ -151,10 +149,9 @@ public void test3() throws Exception { tokenProvider.generateToken(loginDTO); mockMvc.perform( - post("/api/logout") - ) - .andDo(print()) - .andExpect(status().isOk()); + post("/api/logout")) + .andDo(print()) + .andExpect(status().isOk()); } @DisplayName("์‚ฌ์šฉ์ž ์ด๋ฉ”์ผ ์ฝ”๋“œ ์ž…๋ ฅ ์‹œ ") @@ -167,11 +164,22 @@ public void test3() throws Exception { mockMvc.perform( get("/api/mail/authenticate") - .param("code", code) - ) - .andDo(print()) - .andExpect(status().isOk()); + .param("code", code)) + .andDo(print()) + .andExpect(status().isOk()); } + @WithMockCustomUser(username = "testid", role = "USER") + @DisplayName("๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์กฐํšŒ ์‹œ") + @Test + void test4() throws Exception { + createOrLoadMember(); + + mockMvc.perform( + get("/api/auth/me") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(print()); + } } diff --git a/src/test/java/com/example/codebase/controller/MemberControllerTest.java b/src/test/java/com/example/codebase/controller/MemberControllerTest.java index 92605157..6fd7a45e 100644 --- a/src/test/java/com/example/codebase/controller/MemberControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MemberControllerTest.java @@ -85,8 +85,8 @@ class MemberControllerTest { @BeforeAll static void setUp(@Autowired S3Mock s3Mock, - @Autowired AmazonS3 amazonS3, - @Autowired MockMvc mockMvc) { + @Autowired AmazonS3 amazonS3, + @Autowired MockMvc mockMvc) { log.info("s3Mock start"); s3Mock.start(); amazonS3.createBucket("media-xi-art-storage"); @@ -122,19 +122,18 @@ public Member createOrLoadMember(int index, RoleStatus role) { return testMember.get(); } - Member dummy = - Member.builder() - .username("testid" + index) - .password(passwordEncoder.encode("1234")) - .email("email" + index + "@test.com") - .name("test" + index) - .companyName("company" + index) - .roleStatus(role) - .activated(true) - .allowEmailReceive(true) - .allowEmailReceiveDatetime(LocalDateTime.now()) - .createdTime(LocalDateTime.now().plusSeconds(index)) - .build(); + Member dummy = Member.builder() + .username("testid" + index) + .password(passwordEncoder.encode("1234")) + .email("email" + index + "@test.com") + .name("test" + index) + .companyName("company" + index) + .roleStatus(role) + .activated(true) + .allowEmailReceive(true) + .allowEmailReceiveDatetime(LocalDateTime.now()) + .createdTime(LocalDateTime.now().plusSeconds(index)) + .build(); MemberAuthority memberAuthority = new MemberAuthority(); memberAuthority.setAuthority(Authority.of("ROLE_USER")); @@ -160,8 +159,7 @@ public TeamRequest.Create createTeamRequest(String name) { "http://test.com/profile.jpg", "http://test.com/background.jpg", "ํŒ€์†Œ๊ฐœ", - "์ž์‹ ์˜ ํฌ์ง€์…˜, ์ง๊ธ‰" - ); + "์ž์‹ ์˜ ํฌ์ง€์…˜, ์ง๊ธ‰"); } @DisplayName("ํšŒ์›๊ฐ€์ž… API๊ฐ€ ์ž‘๋™ํ•œ๋‹ค") @@ -175,10 +173,9 @@ void test1() throws Exception { dto.setAllowEmailReceive(true); mockMvc.perform( - post("/api/members") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto)) - ) + post("/api/members") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andExpect(status().isCreated()) .andDo(print()); } @@ -195,10 +192,9 @@ void test1() throws Exception { dto.setAllowEmailReceive(true); mockMvc.perform( - post("/api/members/admin") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto)) - ) + post("/api/members/admin") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andExpect(status().isCreated()) .andDo(print()); } @@ -216,10 +212,9 @@ void test2() throws Exception { dto.setHistory("์—ฐํ˜"); mockMvc.perform( - post("/api/members/artist") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto)) - ) + post("/api/members/artist") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andExpect(status().isCreated()) .andDo(print()); } @@ -233,25 +228,10 @@ void test3() throws Exception { createOrLoadMember(3); mockMvc.perform( - get("/api/members") - .param("page", "0") - .param("size", "10") - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isOk()) - .andDo(print()); - } - - @WithMockCustomUser(username = "testid1", role = "USER") - @DisplayName("๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์กฐํšŒ ์‹œ") - @Test - void test4() throws Exception { - createOrLoadMember(); - - mockMvc.perform( - get("/api/members/profile") - .contentType(MediaType.APPLICATION_JSON) - ) + get("/api/members") + .param("page", "0") + .param("size", "10") + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(print()); } @@ -262,9 +242,8 @@ void test5() throws Exception { createOrLoadMember(); mockMvc.perform( - get(String.format("/api/members/%s", "testid1")) - .contentType(MediaType.APPLICATION_JSON) - ) + get(String.format("/api/members/%s", "testid1")) + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(print()); } @@ -282,10 +261,9 @@ void test5() throws Exception { dto.setHistory("history ์ˆ˜์ •"); mockMvc.perform( - put("/api/members/testid1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto)) - ) + put("/api/members/testid1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andExpect(status().isOk()) .andDo(print()); } @@ -299,14 +277,14 @@ void test5() throws Exception { MockMultipartFile file = new MockMultipartFile("profile", "test.jpg", "image/jpg", createImageFile()); mockMvc.perform( - multipart("/api/members/testid1/picture") - .file(file) - .with(request -> { - request.setMethod("PUT"); - return request; - }) - - ) + multipart("/api/members/testid1/picture") + .file(file) + .with(request -> { + request.setMethod("PUT"); + return request; + }) + + ) .andExpect(status().isOk()) .andDo(print()); } @@ -320,14 +298,14 @@ void test5() throws Exception { MockMultipartFile file = new MockMultipartFile("profile", "test.mp3", "audio/mp3", "asd".getBytes()); mockMvc.perform( - multipart("/api/members/testid1/picture") - .file(file) - .with(request -> { - request.setMethod("PUT"); - return request; - }) - - ) + multipart("/api/members/testid1/picture") + .file(file) + .with(request -> { + request.setMethod("PUT"); + return request; + }) + + ) .andExpect(status().isUnsupportedMediaType()) .andDo(print()); } @@ -339,9 +317,8 @@ void test5() throws Exception { createOrLoadMember(); mockMvc.perform( - delete("/api/members/testid1") - .contentType(MediaType.APPLICATION_JSON) - ) + delete("/api/members/testid1") + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(print()); } @@ -352,8 +329,7 @@ void test5() throws Exception { createOrLoadMember(); mockMvc.perform( - get(String.format("/api/members/email/%s", "email1")) - ) + get(String.format("/api/members/email/%s", "email1"))) .andExpect(status().isBadRequest()) .andDo(print()); } @@ -364,8 +340,7 @@ void test5() throws Exception { Member member = createOrLoadMember(); mockMvc.perform( - get(String.format("/api/members/username/%s", member.getUsername())) - ) + get(String.format("/api/members/username/%s", member.getUsername()))) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -379,8 +354,7 @@ void test5() throws Exception { String status = "ARTIST"; mockMvc.perform( - put(String.format("/api/members/%s/role-status?roleStatus=%s", "testid1", status)) - ) + put(String.format("/api/members/%s/role-status?roleStatus=%s", "testid1", status))) .andDo(print()) .andExpect(status().isOk()); } @@ -394,9 +368,8 @@ void test5() throws Exception { String status = "asd"; mockMvc.perform( - put("/api/members/testid1/role-status") - .param("roleStatus", status) - ) + put("/api/members/testid1/role-status") + .param("roleStatus", status)) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -408,8 +381,7 @@ void test5() throws Exception { Member loadMember = createOrLoadMember(); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember.getUsername(), "newid")) - ) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember.getUsername(), "newid"))) .andDo(print()) .andExpect(status().isOk()); } @@ -422,9 +394,8 @@ void test5() throws Exception { Member loadMember2 = createOrLoadMember(2); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), - loadMember2.getUsername())) - ) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), + loadMember2.getUsername()))) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -436,26 +407,22 @@ void test5() throws Exception { Member loadMember1 = createOrLoadMember(); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), "1")) - ) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), "1"))) .andDo(print()) .andExpect(status().isBadRequest()); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), "ํ•œ๊ธ€")) - ) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), "ํ•œ๊ธ€"))) .andDo(print()) .andExpect(status().isBadRequest()); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), - "asdasdasdsadasdasdasdsa")) - ) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), + "asdasdasdsadasdasdasdsa"))) .andDo(print()) .andExpect(status().isBadRequest()); } - @WithMockCustomUser(username = "testid1", role = "USER") @DisplayName("๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ์‹œ") @Test @@ -463,9 +430,8 @@ void test5() throws Exception { Member loadMember = createOrLoadMember(); mockMvc.perform( - put(String.format("/api/members/%s/password", loadMember.getUsername())) - .param("newPassword", "newpassword123!") - ) + put(String.format("/api/members/%s/password", loadMember.getUsername())) + .param("newPassword", "newpassword123!")) .andDo(print()) .andExpect(status().isOk()); } @@ -480,10 +446,9 @@ void non_email_create_mebmer() throws Exception { dto.setPassword("1234"); mockMvc.perform( - post("/api/members") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto)) - ) + post("/api/members") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -564,8 +529,7 @@ void create_curator() throws Exception { mockMvc .perform( get("/api/members/role-status") - .param("roleStatus", "PENDING") - ) + .param("roleStatus", "PENDING")) .andDo(print()) .andExpect(status().isOk()); } @@ -586,8 +550,7 @@ void create_curator() throws Exception { mockMvc .perform( get("/api/members/role-status") - .param("roleStatus", "asd") - ) + .param("roleStatus", "asd")) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -601,15 +564,13 @@ void create_curator() throws Exception { mockMvc .perform( put("/api/members/" + member.getUsername() + "/email-receive") - .param("emailReceive", "true") - ) + .param("emailReceive", "true")) .andDo(print()) .andExpect(status().isOk()); mockMvc .perform( - get("/api/members/" + member.getUsername()) - ) + get("/api/members/" + member.getUsername())) .andDo(print()) .andExpect(status().isOk()); } @@ -624,8 +585,7 @@ void create_curator() throws Exception { mockMvc .perform( - get("/api/members/username") - ) + get("/api/members/username")) .andDo(print()) .andExpect(status().isOk()); } @@ -641,9 +601,8 @@ void create_curator() throws Exception { // when String response = mockMvc.perform( - get("/api/members/profile") - .contentType(MediaType.APPLICATION_JSON) - ) + get("/api/members/profile") + .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); @@ -665,14 +624,12 @@ void create_curator() throws Exception { // when String response = mockMvc.perform( - get(String.format("/api/members/%s", member.getUsername())) - .contentType(MediaType.APPLICATION_JSON) - ) + get(String.format("/api/members/%s", member.getUsername())) + .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - // then MemberResponseDTO memberResponse = objectMapper.readValue(response, MemberResponseDTO.class); assertEquals(2, memberResponse.getTeams().size()); From db4cdd16bf941e6a67dd843e3f50742228b7b388 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Thu, 18 Apr 2024 19:36:41 +0900 Subject: [PATCH 084/103] =?UTF-8?q?chore:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20API=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/controller/MemberController.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/MemberController.java b/src/main/java/com/example/codebase/controller/MemberController.java index 0ae7106d..daed3257 100644 --- a/src/main/java/com/example/codebase/controller/MemberController.java +++ b/src/main/java/com/example/codebase/controller/MemberController.java @@ -262,10 +262,11 @@ public ResponseEntity updateEmailReceive(@PathVariable String username, return new ResponseEntity(message, HttpStatus.OK); } - @Operation(summary = "ํšŒ์› ์•„์ด๋”” ์ „์ฒด ์กฐํšŒ", description = "๊ฒ€์ƒ‰์—”์ง„ ๋…ธ์ถœ ์šฉ ํšŒ์› ์•„์ด๋”” ๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") - @GetMapping("/username") - public ResponseEntity getAllUsername() { - List usernameList = memberService.getAllUsername(); - return new ResponseEntity(usernameList, HttpStatus.OK); - } + // @Operation(summary = "ํšŒ์› ์•„์ด๋”” ์ „์ฒด ์กฐํšŒ", description = "๊ฒ€์ƒ‰์—”์ง„ ๋…ธ์ถœ ์šฉ ํšŒ์› ์•„์ด๋”” ๋ฆฌ์ŠคํŠธ๋ฅผ + // ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") + // @GetMapping("/username") + // public ResponseEntity getAllUsername() { + // List usernameList = memberService.getAllUsername(); + // return new ResponseEntity(usernameList, HttpStatus.OK); + // } } From 14ffdb751be395d2de38a24c3d717dc0479ae27e Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Thu, 18 Apr 2024 20:05:52 +0900 Subject: [PATCH 085/103] =?UTF-8?q?refactor:=20MemberResponseDTO=20?= =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=86=8D=EC=84=B1=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 - ๋ฏผ๊ฐํ•œ ์ •๋ณด ์ทจ๊ธ‰ ๋ฐฉ์ง€ --- .../example/codebase/domain/member/dto/MemberResponseDTO.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java index cc5bbc8d..0db2cb6c 100644 --- a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java @@ -11,7 +11,6 @@ import java.util.ArrayList; import java.util.List; - @Getter @Setter public class MemberResponseDTO { @@ -20,8 +19,6 @@ public class MemberResponseDTO { private String name; - private String email; - private String picture; private String oauthProvider; @@ -61,7 +58,6 @@ public static MemberResponseDTO from(Member member) { MemberResponseDTO dto = new MemberResponseDTO(); dto.setUsername(member.getUsername()); dto.setName(member.getName()); - dto.setEmail(member.getEmail()); dto.setPicture(member.getPicture()); dto.setOauthProvider(String.valueOf(member.getOauthProvider())); // dto.setOauthProviderId(Optional.ofNullable(member.getOauthProviderId())); From 957fe77bbec2d25978533a5e841fb800fcbf39a8 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Thu, 18 Apr 2024 20:07:44 +0900 Subject: [PATCH 086/103] =?UTF-8?q?chore:=20MemberControllerTest=20API=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=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 --- .../controller/MemberControllerTest.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/MemberControllerTest.java b/src/test/java/com/example/codebase/controller/MemberControllerTest.java index 6fd7a45e..56f3d02e 100644 --- a/src/test/java/com/example/codebase/controller/MemberControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MemberControllerTest.java @@ -513,7 +513,7 @@ void create_curator() throws Exception { .andExpect(status().isOk()); } - @DisplayName("์—ญ ์ƒํƒœ ๋ณ„ ํšŒ์› ์ „์ฒด ์กฐํšŒ") + @DisplayName("์—ญํ™œ ๋ณ„ ํšŒ์› ์ „์ฒด ์กฐํšŒ") @WithMockCustomUser(username = "admin", role = "ADMIN") @Test void ์Šน์ธ_๋Œ€๊ธฐ_ํšŒ์›_์ „์ฒด_์กฐํšŒ() throws Exception { @@ -575,20 +575,20 @@ void create_curator() throws Exception { .andExpect(status().isOk()); } - @DisplayName("ํšŒ์› ์•„์ด๋”” ์ „์ฒด ์กฐํšŒ ์‹œ") - @Test - void ํšŒ์›_์•„์ด๋””_์ „์ฒด_์กฐํšŒ() throws Exception { - createOrLoadMember(1); - createOrLoadMember(2); - createOrLoadMember(3); - createOrLoadMember(4); - - mockMvc - .perform( - get("/api/members/username")) - .andDo(print()) - .andExpect(status().isOk()); - } +// @DisplayName("ํšŒ์› ์•„์ด๋”” ์ „์ฒด ์กฐํšŒ ์‹œ") +// @Test +// void ํšŒ์›_์•„์ด๋””_์ „์ฒด_์กฐํšŒ() throws Exception { +// createOrLoadMember(1); +// createOrLoadMember(2); +// createOrLoadMember(3); +// createOrLoadMember(4); +// +// mockMvc +// .perform( +// get("/api/members/username")) +// .andDo(print()) +// .andExpect(status().isOk()); +// } @DisplayName("ํšŒ์› ํ”„๋กœํ•„ ์กฐํšŒ์‹œ ํŒ€ ๋ชฉ๋ก ๋ฐ˜ํ™˜ ์„ฑ๊ณต") @WithMockCustomUser(username = "testid1", role = "USER") @@ -601,7 +601,7 @@ void create_curator() throws Exception { // when String response = mockMvc.perform( - get("/api/members/profile") + get("/api/auth/me") .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) From e4ec1e20bb0d61f6fd6397cd612e285bec11e200 Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Thu, 18 Apr 2024 20:14:40 +0900 Subject: [PATCH 087/103] chore: code formatting --- .../codebase/controller/MemberController.java | 14 +- .../controller/AuthControllerTest.java | 24 ++-- .../controller/MemberControllerTest.java | 123 +++++++++--------- 3 files changed, 77 insertions(+), 84 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/MemberController.java b/src/main/java/com/example/codebase/controller/MemberController.java index daed3257..2300ee95 100644 --- a/src/main/java/com/example/codebase/controller/MemberController.java +++ b/src/main/java/com/example/codebase/controller/MemberController.java @@ -1,10 +1,8 @@ package com.example.codebase.controller; -import com.example.codebase.domain.mail.service.MailService; import com.example.codebase.domain.member.dto.*; import com.example.codebase.domain.member.entity.RoleStatus; import com.example.codebase.domain.member.service.MemberService; -import com.example.codebase.domain.notification.service.NotificationSettingService; import com.example.codebase.exception.NotAcceptTypeException; import com.example.codebase.util.FileUtil; import com.example.codebase.util.SecurityUtil; @@ -77,7 +75,7 @@ public ResponseEntity createCurator(@Valid @RequestBody CreateCuratorMemberDTO c @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER')") @PutMapping("/{uesrname}") public ResponseEntity updateMember(@PathVariable String uesrname, - @Valid @RequestBody UpdateMemberDTO updateMemberDTO) { + @Valid @RequestBody UpdateMemberDTO updateMemberDTO) { String loginUsername = SecurityUtil.getCurrentUsername() .orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); @@ -185,7 +183,7 @@ public ResponseEntity checkUsername(@Valid @NotBlank @PathVariable String userna @PreAuthorize("hasAnyRole('ROLE_ADMIN')") @PutMapping("/{username}/role-status") public ResponseEntity updateRoleStatus(@PathVariable @Valid @NotBlank String username, - @RequestParam @Valid @NotBlank(message = "์—ญํ•  ์ƒํƒœ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") String roleStatus) { + @RequestParam @Valid @NotBlank(message = "์—ญํ•  ์ƒํƒœ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.") String roleStatus) { MemberResponseDTO member = memberService.updateRoleStatus(username, RoleStatus.create(roleStatus)); return new ResponseEntity(member, HttpStatus.OK); } @@ -194,7 +192,7 @@ public ResponseEntity updateRoleStatus(@PathVariable @Valid @NotBlank String use @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER')") @PutMapping("/{username}/username") public ResponseEntity updateUsername(@PathVariable String username, - @Valid @RequestParam UsernameDTO newUsername) { + @Valid @RequestParam UsernameDTO newUsername) { String currentUsername = SecurityUtil.getCurrentUsername() .orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); if (!SecurityUtil.isAdmin() && !currentUsername.equals(username)) { // ๊ด€๋ฆฌ์ž๊ฐ€ ์•„๋‹ˆ๊ณ , ๋ณธ์ธ์˜ ์•„์ด๋””๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ @@ -208,7 +206,7 @@ public ResponseEntity updateUsername(@PathVariable String username, @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER', 'ROLE_ADMIN')") @PutMapping("/{username}/password") public ResponseEntity updatePassword(@PathVariable String username, - @NotBlank @RequestParam(value = "newPassword") String newPassword) { + @NotBlank @RequestParam(value = "newPassword") String newPassword) { String currentUsername = SecurityUtil.getCurrentUsername() .orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); @@ -239,7 +237,7 @@ public ResponseEntity searchMember(@PathVariable String username) { @Operation(summary = "๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •", description = "๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ํ•ฉ๋‹ˆ๋‹ค.") @PostMapping("/reset-password") public ResponseEntity resetPassword(@RequestParam @NotBlank String code, - @RequestBody PasswordResetRequestDTO password) { + @RequestBody PasswordResetRequestDTO password) { memberService.resetPassword(code, password); @@ -250,7 +248,7 @@ public ResponseEntity resetPassword(@RequestParam @NotBlank String code, @PreAuthorize("isAuthenticated() and hasAnyRole('ROLE_USER')") @PutMapping("/{username}/email-receive") public ResponseEntity updateEmailReceive(@PathVariable String username, - @RequestParam boolean emailReceive) { + @RequestParam boolean emailReceive) { String currentUsername = SecurityUtil.getCurrentUsername() .orElseThrow(() -> new RuntimeException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")); diff --git a/src/test/java/com/example/codebase/controller/AuthControllerTest.java b/src/test/java/com/example/codebase/controller/AuthControllerTest.java index 0dea2b76..41664410 100644 --- a/src/test/java/com/example/codebase/controller/AuthControllerTest.java +++ b/src/test/java/com/example/codebase/controller/AuthControllerTest.java @@ -10,6 +10,7 @@ import com.example.codebase.jwt.TokenProvider; import com.example.codebase.util.RedisUtil; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.transaction.Transactional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,7 +26,6 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import jakarta.transaction.Transactional; import java.time.LocalDateTime; import java.util.Optional; @@ -110,9 +110,9 @@ public void test1() throws Exception { loginDTO.setPassword("1234"); mockMvc.perform( - post("/api/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginDTO))) + post("/api/login") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(loginDTO))) .andDo(print()) .andExpect(status().isOk()); } @@ -129,9 +129,9 @@ public void test2() throws Exception { String refreshToken = tokenProvider.generateToken(loginDTO).getRefreshToken(); mockMvc.perform( - post("/api/refresh") - .contentType(MediaType.APPLICATION_JSON) - .content(refreshToken)) + post("/api/refresh") + .contentType(MediaType.APPLICATION_JSON) + .content(refreshToken)) .andDo(print()) .andExpect(status().isOk()); } @@ -149,7 +149,7 @@ public void test3() throws Exception { tokenProvider.generateToken(loginDTO); mockMvc.perform( - post("/api/logout")) + post("/api/logout")) .andDo(print()) .andExpect(status().isOk()); } @@ -163,8 +163,8 @@ public void test3() throws Exception { redisUtil.setDataAndExpire(code, member.getEmail(), 60 * 5 * 1000); mockMvc.perform( - get("/api/mail/authenticate") - .param("code", code)) + get("/api/mail/authenticate") + .param("code", code)) .andDo(print()) .andExpect(status().isOk()); @@ -177,8 +177,8 @@ void test4() throws Exception { createOrLoadMember(); mockMvc.perform( - get("/api/auth/me") - .contentType(MediaType.APPLICATION_JSON)) + get("/api/auth/me") + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(print()); } diff --git a/src/test/java/com/example/codebase/controller/MemberControllerTest.java b/src/test/java/com/example/codebase/controller/MemberControllerTest.java index 56f3d02e..1d309fe0 100644 --- a/src/test/java/com/example/codebase/controller/MemberControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MemberControllerTest.java @@ -12,11 +12,7 @@ import com.example.codebase.domain.member.repository.MemberRepository; import com.example.codebase.domain.team.dto.TeamRequest; import com.example.codebase.domain.team.dto.TeamResponse; -import com.example.codebase.domain.team.dto.TeamUserResponse; -import com.example.codebase.domain.team.entity.TeamUser; -import com.example.codebase.domain.team.repository.TeamUserRepository; import com.example.codebase.domain.team.service.TeamService; -import com.example.codebase.domain.team.service.TeamUserService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.findify.s3mock.S3Mock; @@ -27,7 +23,6 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Role; import org.springframework.core.io.ResourceLoader; import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; @@ -85,8 +80,8 @@ class MemberControllerTest { @BeforeAll static void setUp(@Autowired S3Mock s3Mock, - @Autowired AmazonS3 amazonS3, - @Autowired MockMvc mockMvc) { + @Autowired AmazonS3 amazonS3, + @Autowired MockMvc mockMvc) { log.info("s3Mock start"); s3Mock.start(); amazonS3.createBucket("media-xi-art-storage"); @@ -173,9 +168,9 @@ void test1() throws Exception { dto.setAllowEmailReceive(true); mockMvc.perform( - post("/api/members") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + post("/api/members") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andExpect(status().isCreated()) .andDo(print()); } @@ -192,9 +187,9 @@ void test1() throws Exception { dto.setAllowEmailReceive(true); mockMvc.perform( - post("/api/members/admin") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + post("/api/members/admin") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andExpect(status().isCreated()) .andDo(print()); } @@ -212,9 +207,9 @@ void test2() throws Exception { dto.setHistory("์—ฐํ˜"); mockMvc.perform( - post("/api/members/artist") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + post("/api/members/artist") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andExpect(status().isCreated()) .andDo(print()); } @@ -228,10 +223,10 @@ void test3() throws Exception { createOrLoadMember(3); mockMvc.perform( - get("/api/members") - .param("page", "0") - .param("size", "10") - .contentType(MediaType.APPLICATION_JSON)) + get("/api/members") + .param("page", "0") + .param("size", "10") + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(print()); } @@ -242,8 +237,8 @@ void test5() throws Exception { createOrLoadMember(); mockMvc.perform( - get(String.format("/api/members/%s", "testid1")) - .contentType(MediaType.APPLICATION_JSON)) + get(String.format("/api/members/%s", "testid1")) + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(print()); } @@ -261,9 +256,9 @@ void test5() throws Exception { dto.setHistory("history ์ˆ˜์ •"); mockMvc.perform( - put("/api/members/testid1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + put("/api/members/testid1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andExpect(status().isOk()) .andDo(print()); } @@ -277,14 +272,14 @@ void test5() throws Exception { MockMultipartFile file = new MockMultipartFile("profile", "test.jpg", "image/jpg", createImageFile()); mockMvc.perform( - multipart("/api/members/testid1/picture") - .file(file) - .with(request -> { - request.setMethod("PUT"); - return request; - }) - - ) + multipart("/api/members/testid1/picture") + .file(file) + .with(request -> { + request.setMethod("PUT"); + return request; + }) + + ) .andExpect(status().isOk()) .andDo(print()); } @@ -298,14 +293,14 @@ void test5() throws Exception { MockMultipartFile file = new MockMultipartFile("profile", "test.mp3", "audio/mp3", "asd".getBytes()); mockMvc.perform( - multipart("/api/members/testid1/picture") - .file(file) - .with(request -> { - request.setMethod("PUT"); - return request; - }) - - ) + multipart("/api/members/testid1/picture") + .file(file) + .with(request -> { + request.setMethod("PUT"); + return request; + }) + + ) .andExpect(status().isUnsupportedMediaType()) .andDo(print()); } @@ -317,8 +312,8 @@ void test5() throws Exception { createOrLoadMember(); mockMvc.perform( - delete("/api/members/testid1") - .contentType(MediaType.APPLICATION_JSON)) + delete("/api/members/testid1") + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(print()); } @@ -329,7 +324,7 @@ void test5() throws Exception { createOrLoadMember(); mockMvc.perform( - get(String.format("/api/members/email/%s", "email1"))) + get(String.format("/api/members/email/%s", "email1"))) .andExpect(status().isBadRequest()) .andDo(print()); } @@ -340,7 +335,7 @@ void test5() throws Exception { Member member = createOrLoadMember(); mockMvc.perform( - get(String.format("/api/members/username/%s", member.getUsername()))) + get(String.format("/api/members/username/%s", member.getUsername()))) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -354,7 +349,7 @@ void test5() throws Exception { String status = "ARTIST"; mockMvc.perform( - put(String.format("/api/members/%s/role-status?roleStatus=%s", "testid1", status))) + put(String.format("/api/members/%s/role-status?roleStatus=%s", "testid1", status))) .andDo(print()) .andExpect(status().isOk()); } @@ -368,8 +363,8 @@ void test5() throws Exception { String status = "asd"; mockMvc.perform( - put("/api/members/testid1/role-status") - .param("roleStatus", status)) + put("/api/members/testid1/role-status") + .param("roleStatus", status)) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -381,7 +376,7 @@ void test5() throws Exception { Member loadMember = createOrLoadMember(); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember.getUsername(), "newid"))) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember.getUsername(), "newid"))) .andDo(print()) .andExpect(status().isOk()); } @@ -394,8 +389,8 @@ void test5() throws Exception { Member loadMember2 = createOrLoadMember(2); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), - loadMember2.getUsername()))) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), + loadMember2.getUsername()))) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -407,18 +402,18 @@ void test5() throws Exception { Member loadMember1 = createOrLoadMember(); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), "1"))) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), "1"))) .andDo(print()) .andExpect(status().isBadRequest()); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), "ํ•œ๊ธ€"))) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), "ํ•œ๊ธ€"))) .andDo(print()) .andExpect(status().isBadRequest()); mockMvc.perform( - put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), - "asdasdasdsadasdasdasdsa"))) + put(String.format("/api/members/%s/username?newUsername=%s", loadMember1.getUsername(), + "asdasdasdsadasdasdasdsa"))) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -430,8 +425,8 @@ void test5() throws Exception { Member loadMember = createOrLoadMember(); mockMvc.perform( - put(String.format("/api/members/%s/password", loadMember.getUsername())) - .param("newPassword", "newpassword123!")) + put(String.format("/api/members/%s/password", loadMember.getUsername())) + .param("newPassword", "newpassword123!")) .andDo(print()) .andExpect(status().isOk()); } @@ -446,9 +441,9 @@ void non_email_create_mebmer() throws Exception { dto.setPassword("1234"); mockMvc.perform( - post("/api/members") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + post("/api/members") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) .andDo(print()) .andExpect(status().isBadRequest()); } @@ -601,8 +596,8 @@ void create_curator() throws Exception { // when String response = mockMvc.perform( - get("/api/auth/me") - .contentType(MediaType.APPLICATION_JSON)) + get("/api/auth/me") + .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); @@ -624,8 +619,8 @@ void create_curator() throws Exception { // when String response = mockMvc.perform( - get(String.format("/api/members/%s", member.getUsername())) - .contentType(MediaType.APPLICATION_JSON)) + get(String.format("/api/members/%s", member.getUsername())) + .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); From d7c96e090e03a5df3939acf55516a0c1040eeffc Mon Sep 17 00:00:00 2001 From: Hoon9901 Date: Thu, 18 Apr 2024 20:22:01 +0900 Subject: [PATCH 088/103] feat: git action test.yaml added codecov token --- .github/workflows/test.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 24a2a97a..cb03e8bc 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -55,6 +55,8 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v3.1.1 + with: + token: ${{ secrets.CODECOV_TOKEN }} - name: Cleanup Gradle Cache if: ${{ always() }} From 83382defae7e35686008ed0d62d654b3c604dcbd Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sat, 20 Apr 2024 09:37:17 +0900 Subject: [PATCH 089/103] =?UTF-8?q?feat:=20TeamResponse.ProfileGet=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20-=20TeamUserRole=EC=9D=84=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=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 --- .../codebase/domain/team/dto/TeamResponse.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java b/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java index d8d15ce6..5a4181ad 100644 --- a/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java @@ -1,6 +1,8 @@ package com.example.codebase.domain.team.dto; import com.example.codebase.domain.team.entity.Team; +import com.example.codebase.domain.team.entity.TeamUser; +import com.example.codebase.domain.team.entity.TeamUserRole; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -57,11 +59,14 @@ public static class ProfileGet{ private String name; - public static TeamResponse.ProfileGet from(Team team){ + private TeamUserRole role; + + public static TeamResponse.ProfileGet from(TeamUser teamUser){ ProfileGet profileGet = new ProfileGet(); - profileGet.setId(team.getId()); - profileGet.setProfileImage(team.getProfileImage()); - profileGet.setName(team.getName()); + profileGet.setId(teamUser.getTeam().getId()); + profileGet.setProfileImage(teamUser.getTeam().getProfileImage()); + profileGet.setName(teamUser.getTeam().getName()); + profileGet.setRole(teamUser.getRole()); return profileGet; } } From 96162f05d158bc582b14d4a7424bbaaaaa78be61 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sat, 20 Apr 2024 09:47:32 +0900 Subject: [PATCH 090/103] =?UTF-8?q?refactor:=20=ED=8C=80=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EC=97=90=20=EC=A0=95=EB=A0=AC=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EB=B3=80=EA=B2=BD=20-=20=EA=B0=80=EC=9E=A5=20?= =?UTF-8?q?=EC=98=A4=EB=9E=98=EB=90=9C=20=EC=88=9C=EC=84=9C=EB=8C=80?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=98=ED=99=98=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 --- .../java/com/example/codebase/domain/member/entity/Member.java | 1 + .../codebase/domain/team/repository/TeamUserRepository.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/member/entity/Member.java b/src/main/java/com/example/codebase/domain/member/entity/Member.java index d9135260..c161628d 100644 --- a/src/main/java/com/example/codebase/domain/member/entity/Member.java +++ b/src/main/java/com/example/codebase/domain/member/entity/Member.java @@ -124,6 +124,7 @@ public class Member { private NotificationSetting notificationSettings; @Builder.Default + @OrderBy("createdTime ASC") @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE, orphanRemoval = true) private List teamUser = new ArrayList<>(); diff --git a/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java index 1af166a6..a0b0ac6f 100644 --- a/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java +++ b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java @@ -19,5 +19,5 @@ public interface TeamUserRepository extends JpaRepository { boolean existsByTeamAndMember(Team team, Member member); - List findByMember(Member member); + List findByMemberOrderByCreatedTimeAsc(Member member); } From 7efdeedf73bf33599aea01d2562309b77c24f0bd Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sat, 20 Apr 2024 09:48:30 +0900 Subject: [PATCH 091/103] =?UTF-8?q?refactor:=20=EB=B3=80=EA=B2=BD=EB=90=9C?= =?UTF-8?q?=20dto=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 --- .../example/codebase/domain/member/dto/MemberResponseDTO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java index cc5bbc8d..344d6985 100644 --- a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java @@ -92,7 +92,7 @@ public static MemberResponseDTO from(Member member) { if (!member.getTeamUser().isEmpty()) { for (TeamUser teamUser : member.getTeamUser()) { - dto.teams.add(TeamResponse.ProfileGet.from(teamUser.getTeam())); + dto.getTeams().add(TeamResponse.ProfileGet.from(teamUser)); } } From fa4003d0870510ef8204b80c1a9ed37e818afbc5 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sat, 20 Apr 2024 09:50:10 +0900 Subject: [PATCH 092/103] =?UTF-8?q?refactor:=20=EB=B3=80=EA=B2=BD=EB=90=9C?= =?UTF-8?q?=20=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EA=B2=8C=20get=20teams/{username}=20=20response=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-=20List=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codebase/controller/TeamController.java | 4 +++- .../domain/team/service/TeamUserService.java | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/TeamController.java b/src/main/java/com/example/codebase/controller/TeamController.java index 14dadd95..ea348683 100644 --- a/src/main/java/com/example/codebase/controller/TeamController.java +++ b/src/main/java/com/example/codebase/controller/TeamController.java @@ -18,6 +18,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @Tag(name = "ํŒ€ API", description = "ํŒ€๊ณผ ๊ด€๋ จ๋œ API") @RequestMapping("/api/teams") @@ -93,7 +95,7 @@ public ResponseEntity getTeamUsers(@PathVariable Long teamId) { @Operation(summary = "์œ ์ €๊ฐ€ ์†ํ•œ ํŒ€ ๋ชฉ๋ก ์กฐํšŒ", description = "์œ ์ €๊ฐ€ ์†ํ•œ ๋ชจ๋“  ํŒ€๋“ค์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") public ResponseEntity getTeamsByUsername(@PathVariable String username) { Member member = memberService.getEntity(username); - TeamUserResponse.GetAll response = teamUserService.getTeamsByUsername(member); + List response = teamUserService.getTeamsByUsername(member); return new ResponseEntity(response, HttpStatus.OK); } } diff --git a/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java index 0726413d..decb2d49 100644 --- a/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java +++ b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java @@ -1,6 +1,7 @@ package com.example.codebase.domain.team.service; import com.example.codebase.domain.member.entity.Member; +import com.example.codebase.domain.team.dto.TeamResponse; import com.example.codebase.domain.team.dto.TeamUserRequest; import com.example.codebase.domain.team.dto.TeamUserResponse; import com.example.codebase.domain.team.entity.Team; @@ -13,6 +14,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -58,7 +60,7 @@ public void addTeamUser(TeamUser member, Member inviteUser, TeamUserRequest.Crea } public void deleteTeamUser(TeamUser loginUser, TeamUser deleteUser) { - if(loginUser.isOwner() && deleteUser.isOwner()) { + if (loginUser.isOwner() && deleteUser.isOwner()) { throw new RuntimeException("ํŒ€์žฅ์€ ์ž์‹ ์„ ์ถ”๋ฐฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); } teamUserRepository.delete(deleteUser); @@ -72,7 +74,7 @@ public void transferToOwner(TeamUser loginUser, TeamUser transferUser) { } public void updateTeamUser(TeamUser member, TeamUser changeMember, TeamUserRequest.Update request) { - if(!(Objects.equals(member.getId(), changeMember.getId())) && !member.isOwner()) { + if (!(Objects.equals(member.getId(), changeMember.getId())) && !member.isOwner()) { throw new RuntimeException("๋ณธ์ธ ๋˜๋Š” ํŒ€์žฅ๋งŒ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."); } @@ -81,8 +83,15 @@ public void updateTeamUser(TeamUser member, TeamUser changeMember, TeamUserReque } @Transactional(readOnly = true) - public TeamUserResponse.GetAll getTeamsByUsername(Member member) { - List teams = teamUserRepository.findByMember(member); - return TeamUserResponse.GetAll.from(teams); + public List getTeamsByUsername(Member member) { + List teamUsers = teamUserRepository.findByMemberOrderByCreatedTimeAsc(member); + + List response = new ArrayList<>(); + for (TeamUser teamUser : teamUsers) { + TeamResponse.ProfileGet profileGet = TeamResponse.ProfileGet.from(teamUser); + response.add(profileGet); + } + + return response; } } From a3a7fd993c8aeb28da8f8bb28edfcbc4a152c973 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sat, 20 Apr 2024 09:51:13 +0900 Subject: [PATCH 093/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=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 --- .../controller/TeamControllerTest.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/TeamControllerTest.java b/src/test/java/com/example/codebase/controller/TeamControllerTest.java index 8a4d1cc7..9f01e054 100644 --- a/src/test/java/com/example/codebase/controller/TeamControllerTest.java +++ b/src/test/java/com/example/codebase/controller/TeamControllerTest.java @@ -11,6 +11,7 @@ import com.example.codebase.domain.team.entity.TeamUser; import com.example.codebase.domain.team.service.TeamService; import com.example.codebase.domain.team.service.TeamUserService; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import lombok.extern.slf4j.Slf4j; @@ -28,6 +29,7 @@ import org.springframework.web.context.WebApplicationContext; import java.nio.charset.StandardCharsets; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; @@ -313,13 +315,13 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); //then - TeamUserResponse.GetAll teamUserResponse = objectMapper.readValue(response, TeamUserResponse.GetAll.class); - assertEquals(3, teamUserResponse.getTeamUsers().size()); - assertEquals("OWNER", teamUserResponse.getTeamUsers().get(0).getRole().name()); - assertEquals(createTeam1.getId(),teamUserResponse.getTeamUsers().get(0).getTeamId()); - assertEquals("OWNER", teamUserResponse.getTeamUsers().get(1).getRole().name()); - assertEquals(createTeam2.getId(),teamUserResponse.getTeamUsers().get(1).getTeamId()); - assertEquals("MEMBER", teamUserResponse.getTeamUsers().get(2).getRole().name()); - assertEquals(inviteTeam.getId(),teamUserResponse.getTeamUsers().get(2).getTeamId()); + List teamUserResponse = objectMapper.readValue(response, new TypeReference<>() {}); + assertEquals(3, teamUserResponse.size()); + assertEquals("OWNER", teamUserResponse.get(0).getRole().name()); + assertEquals(createTeam1.getId(),teamUserResponse.get(0).getId()); + assertEquals("OWNER", teamUserResponse.get(1).getRole().name()); + assertEquals(createTeam2.getId(),teamUserResponse.get(1).getId()); + assertEquals("MEMBER", teamUserResponse.get(2).getRole().name()); + assertEquals(inviteTeam.getId(),teamUserResponse.get(2).getId()); } } From 1d02de161717a5760f9cd9f9ffbabf84005ab1e5 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 21 Apr 2024 23:38:50 +0900 Subject: [PATCH 094/103] =?UTF-8?q?refactor:=20Member=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=97=B0=EA=B4=80=EA=B4=80=EA=B3=84=20teamUser=20OrderBy=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC=20=EC=A1=B0=EA=B1=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/codebase/domain/member/entity/Member.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/member/entity/Member.java b/src/main/java/com/example/codebase/domain/member/entity/Member.java index c161628d..d9135260 100644 --- a/src/main/java/com/example/codebase/domain/member/entity/Member.java +++ b/src/main/java/com/example/codebase/domain/member/entity/Member.java @@ -124,7 +124,6 @@ public class Member { private NotificationSetting notificationSettings; @Builder.Default - @OrderBy("createdTime ASC") @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE, orphanRemoval = true) private List teamUser = new ArrayList<>(); From 3571b4ae842a6767193f8301fa50b2a8230952a6 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 21 Apr 2024 23:42:19 +0900 Subject: [PATCH 095/103] =?UTF-8?q?refactor:=20=EB=B3=B4=EB=8B=A4=20?= =?UTF-8?q?=EB=AA=85=ED=99=95=ED=95=9C=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20api=20=EC=9D=B4=EA=B4=80=20->=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=EA=B0=80=20=EC=B0=B8=EC=97=AC=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=ED=8C=80=EA=B3=BC=20=EA=B6=8C=ED=95=9C=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?->=20teams=EC=97=90=EC=84=9C=20members=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/codebase/controller/MemberController.java | 7 +++++++ .../com/example/codebase/controller/TeamController.java | 8 -------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/MemberController.java b/src/main/java/com/example/codebase/controller/MemberController.java index 5041a8ca..692709e1 100644 --- a/src/main/java/com/example/codebase/controller/MemberController.java +++ b/src/main/java/com/example/codebase/controller/MemberController.java @@ -291,4 +291,11 @@ public ResponseEntity getAllUsername() { List usernameList = memberService.getAllUsername(); return new ResponseEntity(usernameList, HttpStatus.OK); } + + @GetMapping("/{username}/teams") + @Operation(summary = "ํšŒ์›์ด ์†ํ•œ ํŒ€ ๋ชฉ๋ก ์กฐํšŒ", description = "ํšŒ์›์ด ์†ํ•œ ๋ชจ๋“  ํŒ€๋“ค์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") + public ResponseEntity getTeamsByUsername(@PathVariable String username) { + MemberResponseDTO.TeamProfiles response = memberService.getTeamProfiles(username); + return new ResponseEntity(response, HttpStatus.OK); + } } diff --git a/src/main/java/com/example/codebase/controller/TeamController.java b/src/main/java/com/example/codebase/controller/TeamController.java index ea348683..c8ff2a6b 100644 --- a/src/main/java/com/example/codebase/controller/TeamController.java +++ b/src/main/java/com/example/codebase/controller/TeamController.java @@ -90,12 +90,4 @@ public ResponseEntity getTeamUsers(@PathVariable Long teamId) { TeamUserResponse.GetAll response = teamUserService.getTeamUsers(teamId); return new ResponseEntity(response, HttpStatus.OK); } - - @GetMapping("/members/{username}") - @Operation(summary = "์œ ์ €๊ฐ€ ์†ํ•œ ํŒ€ ๋ชฉ๋ก ์กฐํšŒ", description = "์œ ์ €๊ฐ€ ์†ํ•œ ๋ชจ๋“  ํŒ€๋“ค์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.") - public ResponseEntity getTeamsByUsername(@PathVariable String username) { - Member member = memberService.getEntity(username); - List response = teamUserService.getTeamsByUsername(member); - return new ResponseEntity(response, HttpStatus.OK); - } } From d7542355f048a1faa55fae89aef531f83304e05c Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 21 Apr 2024 23:42:58 +0900 Subject: [PATCH 096/103] =?UTF-8?q?refactor:=20dto=EA=B0=80=20key=20value?= =?UTF-8?q?=EC=9D=98=20=ED=98=95=ED=83=9C=EB=A5=BC=20=EC=B7=A8=ED=95=A0?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?-=20=EB=B3=B4=EB=8B=A4=20=EC=9D=98=EB=AF=B8=EA=B0=80=20?= =?UTF-8?q?=EB=AA=85=ED=99=95=ED=95=98=EA=B2=8C=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=AA=85=EC=9D=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/dto/MemberResponseDTO.java | 60 +++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java index 344d6985..45280b7c 100644 --- a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java @@ -1,15 +1,18 @@ package com.example.codebase.domain.member.dto; +import com.example.codebase.domain.magazine.dto.MagazineCategoryResponse; +import com.example.codebase.domain.magazine.entity.MagazineCategory; import com.example.codebase.domain.member.entity.Member; -import com.example.codebase.domain.team.dto.TeamResponse; import com.example.codebase.domain.team.entity.TeamUser; import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Getter @@ -55,7 +58,7 @@ public class MemberResponseDTO { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime updatedTime; - private List teams = new ArrayList<>(); + private List teams = new ArrayList<>(); public static MemberResponseDTO from(Member member) { MemberResponseDTO dto = new MemberResponseDTO(); @@ -84,7 +87,7 @@ public static MemberResponseDTO from(Member member) { // TODO : ๊ธฐํš์ž๊ฐ€ ์•„๋‹ˆ๋ฉด NPE ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ ์žˆ์Œ ํ•ด๊ฒฐํ•˜๊ธฐ dto.setCompanyName( - member.getCompanyName() != null ? member.getCompanyName() : null); + member.getCompanyName() != null ? member.getCompanyName() : null); dto.setCompanyRole(member.getCompanyRole() != null ? member.getCompanyRole() : null); dto.setAllowEmailReceive(member.isAllowEmailReceive()); @@ -92,11 +95,60 @@ public static MemberResponseDTO from(Member member) { if (!member.getTeamUser().isEmpty()) { for (TeamUser teamUser : member.getTeamUser()) { - dto.getTeams().add(TeamResponse.ProfileGet.from(teamUser)); + dto.getTeams().add(TeamProfile.from(teamUser)); } } return dto; } + @Getter + @Setter + public static class TeamProfile { + + private Long id; + + private String profileImage; + + private String name; + + public static TeamProfile from(TeamUser teamUser) { + TeamProfile teamProfile = new TeamProfile(); + teamProfile.setId(teamUser.getTeam().getId()); + teamProfile.setName(teamUser.getTeam().getName()); + teamProfile.setProfileImage(teamUser.getTeam().getProfileImage()); + return teamProfile; + } + } + + @Getter + @Setter + public static class TeamProfileWithRole extends TeamProfile { + private String role; + + public static TeamProfileWithRole from(TeamUser teamUser) { + TeamProfileWithRole profileWithRole = new TeamProfileWithRole(); + profileWithRole.setId(teamUser.getTeam().getId()); + profileWithRole.setName(teamUser.getTeam().getName()); + profileWithRole.setProfileImage(teamUser.getTeam().getProfileImage()); + profileWithRole.setRole(teamUser.getRole().toString()); + return profileWithRole; + } + } + + @Getter + @Setter + @Schema(name = "MemberResponseDTO.TeamProfiles", description = "ํ•ด๋‹น ์‚ฌ์šฉ์ž๊ฐ€ ์†Œ์†๋œ ๋ชจ๋“  ํŒ€๊ณผ ๊ถŒํ•œ ์กฐํšŒ DTO") + public static class TeamProfiles { + private List profiles; + + public static TeamProfiles from(List teamUsers) { + TeamProfiles teamProfiles = new TeamProfiles(); + teamProfiles.setProfiles(teamUsers.stream() + .map(TeamProfileWithRole::from) + .collect(Collectors.toList())); + return teamProfiles; + } + } + } From 05abc123f578aded85b93d842d0de671f9810311 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 21 Apr 2024 23:44:06 +0900 Subject: [PATCH 097/103] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20dto=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/team/dto/TeamResponse.java | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java b/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java index 5a4181ad..70bc703e 100644 --- a/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java +++ b/src/main/java/com/example/codebase/domain/team/dto/TeamResponse.java @@ -1,8 +1,6 @@ package com.example.codebase.domain.team.dto; import com.example.codebase.domain.team.entity.Team; -import com.example.codebase.domain.team.entity.TeamUser; -import com.example.codebase.domain.team.entity.TeamUserRole; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -15,7 +13,7 @@ public class TeamResponse { @Getter @Setter @Schema(name = "TeamResponse.Get", description = "ํŒ€ ์กฐํšŒ DTO") - public static class Get{ + public static class Get { private Long id; @@ -48,26 +46,4 @@ public static TeamResponse.Get from(Team team) { return get; } } - - @Getter - @Setter - @Schema(name = "TeamResponse.ProfileGet", description = "ํŒ€ ํ”„๋กœํŒŒ์ผ ์กฐํšŒ DTO") - public static class ProfileGet{ - private Long id; - - private String profileImage; - - private String name; - - private TeamUserRole role; - - public static TeamResponse.ProfileGet from(TeamUser teamUser){ - ProfileGet profileGet = new ProfileGet(); - profileGet.setId(teamUser.getTeam().getId()); - profileGet.setProfileImage(teamUser.getTeam().getProfileImage()); - profileGet.setName(teamUser.getTeam().getName()); - profileGet.setRole(teamUser.getRole()); - return profileGet; - } - } -} +} \ No newline at end of file From c9f15a0e9e340246c030d4937e8e72df3e43f758 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 21 Apr 2024 23:45:03 +0900 Subject: [PATCH 098/103] =?UTF-8?q?refactor:=20api=EC=9C=84=EC=B9=98?= =?UTF-8?q?=EA=B0=80=20=EB=B3=80=EA=B2=BD=EB=90=A8=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20=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 --- .../codebase/controller/TeamUserController.java | 2 +- .../domain/member/service/MemberService.java | 8 ++++++++ .../domain/team/repository/TeamUserRepository.java | 1 - .../domain/team/service/TeamUserService.java | 13 ------------- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/example/codebase/controller/TeamUserController.java b/src/main/java/com/example/codebase/controller/TeamUserController.java index c0b4e2aa..a56d53cd 100644 --- a/src/main/java/com/example/codebase/controller/TeamUserController.java +++ b/src/main/java/com/example/codebase/controller/TeamUserController.java @@ -76,7 +76,7 @@ public ResponseEntity transferToOwner(@PathVariable String username, @RequestPar return new ResponseEntity(response, HttpStatus.OK); } - @PatchMapping("{username}") + @PatchMapping("/{username}") @LoginOnly @Operation(summary = "ํŒ€ ์œ ์ € ์ง์ฑ… ๋ณ€๊ฒฝ", description = "ํŒ€ ์œ ์ €์˜ ์ง์ฑ…์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.") public ResponseEntity updateTeamUser(@PathVariable String username, @RequestParam Long teamId, @RequestBody @Valid TeamUserRequest.Update request) { diff --git a/src/main/java/com/example/codebase/domain/member/service/MemberService.java b/src/main/java/com/example/codebase/domain/member/service/MemberService.java index af0469dc..b9aeab6f 100644 --- a/src/main/java/com/example/codebase/domain/member/service/MemberService.java +++ b/src/main/java/com/example/codebase/domain/member/service/MemberService.java @@ -376,4 +376,12 @@ public Member getEntity(String username) { return memberRepository.findByUsername(username) .orElseThrow(NotFoundMemberException::new); } + + @Transactional(readOnly = true) + public MemberResponseDTO.TeamProfiles getTeamProfiles(String username) { + Member member = memberRepository.findByUsername(username) + .orElseThrow(NotFoundMemberException::new); + + return MemberResponseDTO.TeamProfiles.from(member.getTeamUser()); + } } diff --git a/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java index a0b0ac6f..3b1d1875 100644 --- a/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java +++ b/src/main/java/com/example/codebase/domain/team/repository/TeamUserRepository.java @@ -19,5 +19,4 @@ public interface TeamUserRepository extends JpaRepository { boolean existsByTeamAndMember(Team team, Member member); - List findByMemberOrderByCreatedTimeAsc(Member member); } diff --git a/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java index decb2d49..d588a8ec 100644 --- a/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java +++ b/src/main/java/com/example/codebase/domain/team/service/TeamUserService.java @@ -81,17 +81,4 @@ public void updateTeamUser(TeamUser member, TeamUser changeMember, TeamUserReque member.update(request); teamUserRepository.save(member); } - - @Transactional(readOnly = true) - public List getTeamsByUsername(Member member) { - List teamUsers = teamUserRepository.findByMemberOrderByCreatedTimeAsc(member); - - List response = new ArrayList<>(); - for (TeamUser teamUser : teamUsers) { - TeamResponse.ProfileGet profileGet = TeamResponse.ProfileGet.from(teamUser); - response.add(profileGet); - } - - return response; - } } From 82ff5f76122e6c6dd81c92b7b5be65d95ed583be Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 21 Apr 2024 23:45:17 +0900 Subject: [PATCH 099/103] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MemberControllerTest.java | 51 +++++++++++++++++-- .../controller/TeamControllerTest.java | 36 ------------- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/test/java/com/example/codebase/controller/MemberControllerTest.java b/src/test/java/com/example/codebase/controller/MemberControllerTest.java index 92605157..74ef9420 100644 --- a/src/test/java/com/example/codebase/controller/MemberControllerTest.java +++ b/src/test/java/com/example/codebase/controller/MemberControllerTest.java @@ -12,11 +12,13 @@ import com.example.codebase.domain.member.repository.MemberRepository; import com.example.codebase.domain.team.dto.TeamRequest; import com.example.codebase.domain.team.dto.TeamResponse; +import com.example.codebase.domain.team.dto.TeamUserRequest; import com.example.codebase.domain.team.dto.TeamUserResponse; import com.example.codebase.domain.team.entity.TeamUser; import com.example.codebase.domain.team.repository.TeamUserRepository; import com.example.codebase.domain.team.service.TeamService; import com.example.codebase.domain.team.service.TeamUserService; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.findify.s3mock.S3Mock; @@ -44,6 +46,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -83,6 +86,9 @@ class MemberControllerTest { @Autowired private TeamService teamService; + @Autowired + private TeamUserService teamUserService; + @BeforeAll static void setUp(@Autowired S3Mock s3Mock, @Autowired AmazonS3 amazonS3, @@ -164,6 +170,13 @@ public TeamRequest.Create createTeamRequest(String name) { ); } + public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { + TeamUserRequest.Create request = new TeamUserRequest.Create( + "ํŒ€์›" + ); + teamUserService.addTeamUser(loginUser, inviteMember, request); + } + @DisplayName("ํšŒ์›๊ฐ€์ž… API๊ฐ€ ์ž‘๋™ํ•œ๋‹ค") @Test void test1() throws Exception { @@ -651,8 +664,8 @@ void create_curator() throws Exception { // then MemberResponseDTO memberResponse = objectMapper.readValue(response, MemberResponseDTO.class); assertEquals(2, memberResponse.getTeams().size()); - assertEquals(team1.getName(), memberResponse.getTeams().get(0).getName()); - assertEquals(team2.getName(), memberResponse.getTeams().get(1).getName()); + assertEquals("ํŒ€์ด๋ฆ„1", memberResponse.getTeams().get(0).getName()); + assertEquals("ํŒ€์ด๋ฆ„2", memberResponse.getTeams().get(1).getName()); } @DisplayName("์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์กฐํšŒ ์‹œ ํŒ€ ๋ฐ˜ํ™˜ ์„ฑ๊ณต") @@ -676,7 +689,37 @@ void create_curator() throws Exception { // then MemberResponseDTO memberResponse = objectMapper.readValue(response, MemberResponseDTO.class); assertEquals(2, memberResponse.getTeams().size()); - assertEquals(team1.getName(), memberResponse.getTeams().get(0).getName()); - assertEquals(team2.getName(), memberResponse.getTeams().get(1).getName()); + assertEquals("ํŒ€์ด๋ฆ„1", memberResponse.getTeams().get(0).getName()); + assertEquals("ํŒ€์ด๋ฆ„2", memberResponse.getTeams().get(1).getName()); + } + + @DisplayName("ํ•ด๋‹น ์œ ์ €์˜ ํŒ€ ๋ชฉ๋ก ์กฐํšŒ ") + @Test + void ์œ ์ €๊ฐ€_์†ํ•œ_ํŒ€_์กฐํšŒ() throws Exception { + //given + Member member = createOrLoadMember(); + Member member2 = createOrLoadMember(2, RoleStatus.ARTIST); + TeamResponse.Get createTeam1 = createTeam(member, "ownerTeam1"); + TeamResponse.Get createTeam2 = createTeam(member, "ownerTeam2"); + TeamResponse.Get inviteTeam = createTeam(member2, "memberTeam1"); + TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(inviteTeam.getId(),member2.getUsername()); + createAndInviteMember(teamOwner, member); + + //when + String response = mockMvc.perform(get("/api/members/" + member.getUsername() + "/teams") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + //then + MemberResponseDTO.TeamProfiles teamProfiles= objectMapper.readValue(response, MemberResponseDTO.TeamProfiles.class); + List profiles = teamProfiles.getProfiles(); + assertEquals(3, profiles.size()); + assertEquals("OWNER", profiles.get(0).getRole()); + assertEquals(createTeam1.getId(), profiles.get(0).getId()); + assertEquals("OWNER", profiles.get(1).getRole()); + assertEquals(createTeam2.getId(), profiles.get(1).getId()); + assertEquals("MEMBER", profiles.get(2).getRole()); + assertEquals(inviteTeam.getId(), profiles.get(2).getId()); } } diff --git a/src/test/java/com/example/codebase/controller/TeamControllerTest.java b/src/test/java/com/example/codebase/controller/TeamControllerTest.java index 9f01e054..0b0b487a 100644 --- a/src/test/java/com/example/codebase/controller/TeamControllerTest.java +++ b/src/test/java/com/example/codebase/controller/TeamControllerTest.java @@ -101,13 +101,6 @@ public TeamResponse.Get createTeam(Member member, String name) { return teamService.createTeam(createTeamRequest(name), member); } - public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { - TeamUserRequest.Create request = new TeamUserRequest.Create( - "ํŒ€์›" - ); - teamUserService.addTeamUser(loginUser, inviteMember, request); - } - @WithMockCustomUser(username = "testid", role = "USER") @DisplayName("ํŒ€ ์ƒ์„ฑ ํ…Œ์ŠคํŠธ") @Test @@ -295,33 +288,4 @@ public void createAndInviteMember(TeamUser loginUser, Member inviteMember) { assertEquals(1, teamUserResponse.getTeamUsers().size()); assertEquals("testid", teamUserResponse.getTeamUsers().get(0).getUsername()); } - - @DisplayName("ํ•ด๋‹น ์œ ์ €์˜ ํŒ€ ๋ชฉ๋ก ์กฐํšŒ ") - @Test - void ์œ ์ €๊ฐ€_์†ํ•œ_ํŒ€_์กฐํšŒ() throws Exception { - //given - Member member = createMember("testid"); - Member member2 = createMember("testid2"); - TeamResponse.Get createTeam1 = createTeam(member, "ownerTeam1"); - TeamResponse.Get createTeam2 = createTeam(member, "ownerTeam2"); - TeamResponse.Get inviteTeam = createTeam(member2, "memberTeam1"); - TeamUser teamOwner = teamUserService.findByTeamIdAndUsername(inviteTeam.getId(),member2.getUsername()); - createAndInviteMember(teamOwner, member); - - //when - String response = mockMvc.perform(get("/api/teams/members/" + member.getUsername()) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8); - - //then - List teamUserResponse = objectMapper.readValue(response, new TypeReference<>() {}); - assertEquals(3, teamUserResponse.size()); - assertEquals("OWNER", teamUserResponse.get(0).getRole().name()); - assertEquals(createTeam1.getId(),teamUserResponse.get(0).getId()); - assertEquals("OWNER", teamUserResponse.get(1).getRole().name()); - assertEquals(createTeam2.getId(),teamUserResponse.get(1).getId()); - assertEquals("MEMBER", teamUserResponse.get(2).getRole().name()); - assertEquals(inviteTeam.getId(),teamUserResponse.get(2).getId()); - } } From 7cba374c6f9acff1198bb9b91e35488ef4ba29c9 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Sun, 21 Apr 2024 23:52:24 +0900 Subject: [PATCH 100/103] =?UTF-8?q?refactor:=20=EC=9D=BC=EA=B4=80=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20teamUser=EC=A0=95=EB=A0=AC?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/codebase/domain/member/entity/Member.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/example/codebase/domain/member/entity/Member.java b/src/main/java/com/example/codebase/domain/member/entity/Member.java index d9135260..c161628d 100644 --- a/src/main/java/com/example/codebase/domain/member/entity/Member.java +++ b/src/main/java/com/example/codebase/domain/member/entity/Member.java @@ -124,6 +124,7 @@ public class Member { private NotificationSetting notificationSettings; @Builder.Default + @OrderBy("createdTime ASC") @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE, orphanRemoval = true) private List teamUser = new ArrayList<>(); From b8707e8779a7e6bf73fdd726f2a686ffc22db4ec Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 22 Apr 2024 01:29:28 +0900 Subject: [PATCH 101/103] =?UTF-8?q?refactor:=20=EB=B6=88=20=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=A0=AC=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/codebase/domain/member/entity/Member.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/member/entity/Member.java b/src/main/java/com/example/codebase/domain/member/entity/Member.java index c161628d..d9135260 100644 --- a/src/main/java/com/example/codebase/domain/member/entity/Member.java +++ b/src/main/java/com/example/codebase/domain/member/entity/Member.java @@ -124,7 +124,6 @@ public class Member { private NotificationSetting notificationSettings; @Builder.Default - @OrderBy("createdTime ASC") @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE, orphanRemoval = true) private List teamUser = new ArrayList<>(); From 37fd829212eb9a2fc5fc712f1ad5886e3d8768a5 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 22 Apr 2024 01:34:06 +0900 Subject: [PATCH 102/103] =?UTF-8?q?refactor:=20collect=EB=A5=BC=20tolist?= =?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 --- .../example/codebase/domain/member/dto/MemberResponseDTO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java index 45280b7c..493172f4 100644 --- a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java @@ -146,7 +146,7 @@ public static TeamProfiles from(List teamUsers) { TeamProfiles teamProfiles = new TeamProfiles(); teamProfiles.setProfiles(teamUsers.stream() .map(TeamProfileWithRole::from) - .collect(Collectors.toList())); + .toList()); return teamProfiles; } } From 6b8a5bae0cbea1446cb79ae67ca3635fd3b8b0c1 Mon Sep 17 00:00:00 2001 From: gimdonghyeon Date: Mon, 22 Apr 2024 01:38:23 +0900 Subject: [PATCH 103/103] =?UTF-8?q?feat:=20MemberResponseDTO=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20dto=EB=93=A4=EC=9D=98=20@schema=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/codebase/domain/member/dto/MemberResponseDTO.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java index 493172f4..ada15bb8 100644 --- a/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/com/example/codebase/domain/member/dto/MemberResponseDTO.java @@ -104,6 +104,7 @@ public static MemberResponseDTO from(Member member) { @Getter @Setter + @Schema(name = "MemberResponseDTO.TeamProfile", description = "ํŒ€์˜ ๊ธฐ๋ณธ ์ •๋ณด") public static class TeamProfile { private Long id; @@ -123,6 +124,7 @@ public static TeamProfile from(TeamUser teamUser) { @Getter @Setter + @Schema(name = "MemberResponseDTO.TeamProfileWithRole", description = "ํŒ€์˜ ๊ธฐ๋ณธ ์ •๋ณด์™€ ์†ํ•œ ์‚ฌ์šฉ์ž์˜ ์—ญํ• ") public static class TeamProfileWithRole extends TeamProfile { private String role;