From 7c22f51540873463d97504f6686081636aee3893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=EC=A3=BC=EC=84=B1?= <99165624+JoosungKwon@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:06:01 +0900 Subject: [PATCH 1/9] =?UTF-8?q?README.md=EC=97=90=20Architecture=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index df6f76ca..51520355 100644 --- a/README.md +++ b/README.md @@ -46,15 +46,24 @@



+ +## Architecture ๐Ÿฐ +

+ +![image](https://github.com/Clover-Habiters/backend/assets/99165624/c1fa8ad2-546e-46e9-b6c1-c912d0ee5873) + +

## API Spec ๐Ÿ’ฝ ### [Habiters API ๋ฌธ์„œ](https://api.habiters.store/docs/index.html) -



+
+ ## ERD ๐Ÿ’พ -



+
+ ## ํ”„๋กœ์ ํŠธ ์‹คํ–‰ ๋ฐฉ๋ฒ• โš™ ํ”„๋กœ์ ํŠธ ์‹คํ–‰ ์ „ ์•„๋ž˜ ํ•ญ๋ชฉ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š” From 155c6a1f39bf6995c692f9bdae43e4572bd2be92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=EC=A3=BC=EC=84=B1?= <99165624+JoosungKwon@users.noreply.github.com> Date: Mon, 14 Aug 2023 18:58:13 +0900 Subject: [PATCH 2/9] =?UTF-8?q?CI/CD=20=EC=9D=B4=EB=AF=B8=EC=A7=80=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 --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 51520355..7f83305f 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,14 @@ ![image](https://github.com/Clover-Habiters/backend/assets/99165624/c1fa8ad2-546e-46e9-b6c1-c912d0ee5873)

+ +## CI/CD ๐Ÿ” +

+ +![image](https://github.com/Clover-Habiters/backend/assets/99165624/159c8032-5460-4b1f-a0a6-3161ad0ae1e8) + +

+ ## API Spec ๐Ÿ’ฝ ### [Habiters API ๋ฌธ์„œ](https://api.habiters.store/docs/index.html) From e7cc9e1990e4ec5be39232139e19bc9abb49de8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=EC=A3=BC=EC=84=B1?= <99165624+JoosungKwon@users.noreply.github.com> Date: Mon, 14 Aug 2023 19:00:48 +0900 Subject: [PATCH 3/9] =?UTF-8?q?API=20=EB=AC=B8=EC=84=9C=20=EC=8D=B8?= =?UTF-8?q?=EB=84=A4=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 7f83305f..8a135f7d 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,9 @@ ## API Spec ๐Ÿ’ฝ ### [Habiters API ๋ฌธ์„œ](https://api.habiters.store/docs/index.html) +
+ +![image](https://github.com/Clover-Habiters/backend/assets/99165624/4c31c232-e689-41dc-9470-1faa45b0c93a)
From c6ec60f3734d9925d6e61f89d9f294471f7b5e25 Mon Sep 17 00:00:00 2001 From: gombasan Date: Mon, 14 Aug 2023 23:42:20 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat=20:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NCP ๋‚ด์— ObjectStorage ๋กœ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•œ ํ›„์— ์ด๋ฏธ์ง€์˜ url ์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. - ์ด๋ฏธ์ง€๋ฅผ ์ž„์‹œ ๊ฒฝ๋กœ์ธ tmp ํŒŒ์ผ์— ์ €์žฅํ•ด ๋‘” ํ›„์— ์Šคํ† ๋ฆฌ์ง€ ์„œ๋ฒ„์— ์—…๋กœ๋“œ ํ›„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค. --- .../infra/storage/ObjectStorageService.java | 68 ------------------- .../storage/api/ObjectStorageController.java | 29 ++++++++ .../storage/service/ObjectStorageService.java | 40 +++++++++++ .../habbittracker/global/util/ImageUtil.java | 40 +++++++++++ 4 files changed, 109 insertions(+), 68 deletions(-) delete mode 100644 src/main/java/com/clover/habbittracker/global/infra/storage/ObjectStorageService.java create mode 100644 src/main/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageController.java create mode 100644 src/main/java/com/clover/habbittracker/global/infra/storage/service/ObjectStorageService.java create mode 100644 src/main/java/com/clover/habbittracker/global/util/ImageUtil.java diff --git a/src/main/java/com/clover/habbittracker/global/infra/storage/ObjectStorageService.java b/src/main/java/com/clover/habbittracker/global/infra/storage/ObjectStorageService.java deleted file mode 100644 index 44645c5b..00000000 --- a/src/main/java/com/clover/habbittracker/global/infra/storage/ObjectStorageService.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.clover.habbittracker.global.infra.storage; - -import java.io.File; -import java.util.UUID; - -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.CannedAccessControlList; -import com.amazonaws.services.s3.model.PutObjectRequest; - -import lombok.extern.slf4j.Slf4j; - -/* 2023/05/30 ํ”„๋กœํ•„ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ๋•Œ๋ฌธ์— ํ•ด๋‹น ์„œ๋น„์Šค๋Š” ๋ฏธ์‚ฌ์šฉ - ** ์ถ”ํ›„ ๋‹ค์‹œ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ์Œ ** - */ -@Service -@Slf4j -public class ObjectStorageService { - - private final AmazonS3 objectStorage; - private final String imgPath; - - public ObjectStorageService(AmazonS3 objectStorage) { - this.objectStorage = objectStorage; - this.imgPath = System.getProperty("user.dir") + "/tmp"; - } - - public String profileImgSave(MultipartFile file) { - if (file == null) - return null; - - String buketName = "user-profile-image"; - String fileName = tempFileSave(file); - PutObjectRequest request = new PutObjectRequest(buketName, fileName, new File(imgPath, fileName)); - request.withCannedAcl(CannedAccessControlList.PublicRead); - objectStorage.putObject(request); - - if (!tempFileDelete(fileName)) { - log.error("Failed to delete file. The administrator should check it out"); - } - - return objectStorage.getUrl(buketName, fileName).toString(); - } - - protected boolean tempFileDelete(String fileName) { - File tempFile = new File(imgPath, fileName); - if (tempFile.exists()) { - return tempFile.delete(); - } - return false; - } - - protected String tempFileSave(MultipartFile file) { - String uuid = UUID.randomUUID().toString().substring(0, 8); - - String imgName = uuid + "_" + file.getOriginalFilename(); - File imgFile = new File(imgPath, imgName); - try { - file.transferTo(imgFile); - } catch (Exception e) { - System.out.println(e.getMessage()); - } - - return imgName; - } -} diff --git a/src/main/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageController.java b/src/main/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageController.java new file mode 100644 index 00000000..c5e95152 --- /dev/null +++ b/src/main/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageController.java @@ -0,0 +1,29 @@ +package com.clover.habbittracker.global.infra.storage.api; + +import static org.springframework.http.MediaType.*; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.clover.habbittracker.global.base.dto.ApiResponse; +import com.clover.habbittracker.global.infra.storage.service.ObjectStorageService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/image") +@RequiredArgsConstructor +public class ObjectStorageController { + private final ObjectStorageService objectStorageService; + + @PostMapping(value = "/upload", consumes = MULTIPART_FORM_DATA_VALUE) + ApiResponse postImageUpload( + @RequestPart("file") MultipartFile imageFile + ) { + String imgUrl = objectStorageService.imgSave(imageFile); + return ApiResponse.success(imgUrl); + } +} diff --git a/src/main/java/com/clover/habbittracker/global/infra/storage/service/ObjectStorageService.java b/src/main/java/com/clover/habbittracker/global/infra/storage/service/ObjectStorageService.java new file mode 100644 index 00000000..a294f59d --- /dev/null +++ b/src/main/java/com/clover/habbittracker/global/infra/storage/service/ObjectStorageService.java @@ -0,0 +1,40 @@ +package com.clover.habbittracker.global.infra.storage.service; + +import static com.clover.habbittracker.global.util.ImageUtil.*; + +import java.io.File; +import java.util.Optional; + +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.PutObjectRequest; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Service +@Slf4j +@RequiredArgsConstructor +public class ObjectStorageService { + + private final AmazonS3 objectStorage; + private final String BUCKET_NAME = "post-images"; + + public String imgSave(MultipartFile file) { + Optional.ofNullable(file).orElseThrow(IllegalArgumentException::new); + File tempFile = tempFileSave(file); + String imgUrl = objectStorageFileSave(tempFile); + tempFileDelete(tempFile); + return imgUrl; + } + + private String objectStorageFileSave(File tempFile) { + PutObjectRequest request = new PutObjectRequest(BUCKET_NAME, tempFile.getName(), tempFile); + request.withCannedAcl(CannedAccessControlList.PublicRead); + objectStorage.putObject(request); + return objectStorage.getUrl(BUCKET_NAME, tempFile.getName()).toString(); + } +} diff --git a/src/main/java/com/clover/habbittracker/global/util/ImageUtil.java b/src/main/java/com/clover/habbittracker/global/util/ImageUtil.java new file mode 100644 index 00000000..aae7db17 --- /dev/null +++ b/src/main/java/com/clover/habbittracker/global/util/ImageUtil.java @@ -0,0 +1,40 @@ +package com.clover.habbittracker.global.util; + +import static lombok.AccessLevel.*; + +import java.io.File; +import java.util.UUID; + +import org.springframework.web.multipart.MultipartFile; + +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@NoArgsConstructor(access = PRIVATE) +public class ImageUtil { + private static final String IMG_PATH = System.getProperty("user.dir") + "/tmp"; + + public static void tempFileDelete(File tempFile) { + if (tempFile.delete()) { + log.error("์ž„์‹œ ํŒŒ์ผ ์‚ญ์ œ๋ฅผ ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์ž„์‹œํŒŒ์ผ์„ ๊ด€๋ฆฌ์ž๊ฐ€ ์ง์ ‘ ์‚ญ์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. FileName = {}", tempFile.getName()); + } + } + + public static File tempFileSave(MultipartFile file) { + File imgFile = getImgFile(file); + try { + file.transferTo(imgFile); + } catch (Exception e) { + log.error("Failed to transfer file: {}", e.getMessage()); + } + return imgFile; + } + + private static File getImgFile(MultipartFile file) { + String uuid = UUID.randomUUID().toString().substring(0, 8); + + String imgName = uuid + "_" + file.getOriginalFilename(); + return new File(IMG_PATH, imgName); + } +} From 6aee0ba27b7d8bde43d09624f3afd22dca79d36b Mon Sep 17 00:00:00 2001 From: gombasan Date: Mon, 14 Aug 2023 23:44:25 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat(Test)=20:=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=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 - Mock ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Url ์„ ์ •์ƒ์ ์œผ๋กœ ๋ฐ˜ํ™˜ ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. - ๋งŒ์•ฝ ์ด๋ฏธ์ง€ ํŒŒ์ผ์ด null ๋กœ ์˜ค๊ฒŒ ๋œ๋‹ค๋ฉด, IllegalArgumentException ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. - RestDocs ๋กœ ๋ฌธ์„œํ™” ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค. --- .../api/ObjectStorageControllerTest.java | 50 +++++++++++++++++++ .../service}/ObjectStorageServiceTest.java | 17 +++---- .../restdocs/templates/request-parts.snippet | 13 +++++ 3 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 src/test/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageControllerTest.java rename src/test/java/com/clover/habbittracker/global/infra/{ => storage/service}/ObjectStorageServiceTest.java (75%) create mode 100644 src/test/resources/org/springframework/restdocs/templates/request-parts.snippet diff --git a/src/test/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageControllerTest.java b/src/test/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageControllerTest.java new file mode 100644 index 00000000..61db48a7 --- /dev/null +++ b/src/test/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageControllerTest.java @@ -0,0 +1,50 @@ +package com.clover.habbittracker.global.infra.storage.api; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import com.clover.habbittracker.base.RestDocsSupport; +import com.clover.habbittracker.global.infra.storage.service.ObjectStorageService; + +public class ObjectStorageControllerTest extends RestDocsSupport { + + @MockBean + private ObjectStorageService objectStorageService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + when(objectStorageService.imgSave(any(MultipartFile.class))).thenReturn("/sucess/url"); + } + + @Test + @DisplayName("์‚ฌ์ง„์„ ์š”์ฒญํ•˜์—ฌ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.") + void imageUploadTest() throws Exception { + MockMultipartFile file = new MockMultipartFile("file", "test.jpg", "image/jpeg", new byte[0]); + mockMvc.perform(multipart("/image/upload") + .file(file)) + .andExpect(status().isOk()) + .andDo(restDocs.document( + requestParts( + partWithName("file").description("์ด๋ฏธ์ง€ ํŒŒ์ผ") + ), + responseFields( + fieldWithPath("code").description("๊ฒฐ๊ณผ ์ฝ”๋“œ"), + fieldWithPath("message").description("๊ฒฐ๊ณผ ๋ฉ”์„ธ์ง€"), + fieldWithPath("data").description("์ด๋ฏธ์ง€ url") + ) + )); + } +} diff --git a/src/test/java/com/clover/habbittracker/global/infra/ObjectStorageServiceTest.java b/src/test/java/com/clover/habbittracker/global/infra/storage/service/ObjectStorageServiceTest.java similarity index 75% rename from src/test/java/com/clover/habbittracker/global/infra/ObjectStorageServiceTest.java rename to src/test/java/com/clover/habbittracker/global/infra/storage/service/ObjectStorageServiceTest.java index 2767a38b..6f845c4e 100644 --- a/src/test/java/com/clover/habbittracker/global/infra/ObjectStorageServiceTest.java +++ b/src/test/java/com/clover/habbittracker/global/infra/storage/service/ObjectStorageServiceTest.java @@ -1,11 +1,12 @@ -package com.clover.habbittracker.global.infra; +package com.clover.habbittracker.global.infra.storage.service; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import java.io.IOException; import java.net.URL; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -16,7 +17,6 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.PutObjectRequest; -import com.clover.habbittracker.global.infra.storage.ObjectStorageService; public class ObjectStorageServiceTest { private ObjectStorageService objectStorageService; @@ -35,21 +35,21 @@ public void setup() { public void naverCloudProfileImgSaveTest() throws IOException { //given MultipartFile mockFile = new MockMultipartFile("test.jpg", "test.jpg", "image/jpeg", new byte[0]); - String expectedBucketName = "user-profile-image"; + String expectedBucketName = "post-images"; String mockUrl = "http://simple.url"; //when when(mockStorage.putObject(any(PutObjectRequest.class))).thenReturn(null); when(mockStorage.getUrl(anyString(), anyString())).thenReturn(new URL(mockUrl)); - String result = objectStorageService.profileImgSave(mockFile); + String result = objectStorageService.imgSave(mockFile); //then - Assertions.assertThat(result).isEqualTo(mockUrl); + assertThat(result).isEqualTo(mockUrl); verify(mockStorage).getUrl(eq(expectedBucketName), anyString()); } @Test - @DisplayName("multipartForm ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค๋ฉด ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋Š” ๋”ฐ๋กœ ํ•˜์ง€์•Š๊ณ , null์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @DisplayName("multipartForm ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค๋ฉด IllegalArgumentException ์˜ˆ์™ธ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.") public void failedProfileImgSaveTest() throws IOException { //given String mockUrl = "http://simple.url"; @@ -57,9 +57,8 @@ public void failedProfileImgSaveTest() throws IOException { //when when(mockStorage.putObject(any(PutObjectRequest.class))).thenReturn(null); when(mockStorage.getUrl(anyString(), anyString())).thenReturn(new URL(mockUrl)); - String result = objectStorageService.profileImgSave(null); //then - Assertions.assertThat(result).isEqualTo(null); + assertThrows(IllegalArgumentException.class, () -> objectStorageService.imgSave(null)); } } diff --git a/src/test/resources/org/springframework/restdocs/templates/request-parts.snippet b/src/test/resources/org/springframework/restdocs/templates/request-parts.snippet new file mode 100644 index 00000000..14a2b6ae --- /dev/null +++ b/src/test/resources/org/springframework/restdocs/templates/request-parts.snippet @@ -0,0 +1,13 @@ +===== Request Parts + +|=== +|ํŒŒ์ผ๋ช…|ํ•„์ˆ˜๊ฐ’|์„ค๋ช… +{{#fields}} + |{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} + |{{#tableCellContent}} + {{^optional}}true{{/optional}} + {{#optional}}false{{/optional}} + {{/tableCellContent}} + |{{#tableCellContent}}{{description}}{{/tableCellContent}} +{{/fields}} +|=== \ No newline at end of file From 9c8152c0fe5e167aa4f98e15f49436d2816ad10d Mon Sep 17 00:00:00 2001 From: gombasan Date: Tue, 15 Aug 2023 00:28:04 +0900 Subject: [PATCH 6/9] =?UTF-8?q?docs=20:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20ap?= =?UTF-8?q?i=20=EB=AC=B8=EC=84=9C=ED=99=94=20=EB=82=B4=EC=9A=A9=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 - ์ด๋ฏธ์ง€ api.adoc ํŒŒ์ผ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค. - index ๋‚ด์— ๋งํฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค. --- src/docs/asciidoc/api/Image-API.adoc | 29 +++++++++++++++++++ src/docs/asciidoc/index.adoc | 4 ++- .../restdocs/templates/request-parts.snippet | 13 --------- 3 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 src/docs/asciidoc/api/Image-API.adoc delete mode 100644 src/test/resources/org/springframework/restdocs/templates/request-parts.snippet diff --git a/src/docs/asciidoc/api/Image-API.adoc b/src/docs/asciidoc/api/Image-API.adoc new file mode 100644 index 00000000..61fb25f5 --- /dev/null +++ b/src/docs/asciidoc/api/Image-API.adoc @@ -0,0 +1,29 @@ +:doctype: book +:icons: font +:source-highlighter: highlightjs +:toc: left +:toclevels: 2 +:sectlinks: + +[[Image-API]] +== ์ด๋ฏธ์ง€ API + +--- + +=== ์ด๋ฏธ์ง€ ๋“ฑ๋ก + +==== Request + +include::{snippets}/object-storage-controller-test/image-upload-test/request-parts.adoc[] + +===== Request HTTP Example + +include::{snippets}/object-storage-controller-test/image-upload-test/http-request.adoc[] + +==== Response + +include::{snippets}/object-storage-controller-test/image-upload-test/response-fields.adoc[] + +===== Response HTTP Example + +include::{snippets}/object-storage-controller-test/image-upload-test/http-response.adoc[] diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 5c8a15b3..bbab93c3 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -29,4 +29,6 @@ === link:api/Bookmark-API.html[๋ถ๋งˆํฌ API, window= _blank] -=== link:api/Emoji-API.html[์ด๋ชจ์ง€ API, window= _blank] \ No newline at end of file +=== link:api/Emoji-API.html[์ด๋ชจ์ง€ API, window= _blank] + +=== link:api/Image-API.html[์ด๋ฏธ์ง€ API, window= _blank] \ No newline at end of file diff --git a/src/test/resources/org/springframework/restdocs/templates/request-parts.snippet b/src/test/resources/org/springframework/restdocs/templates/request-parts.snippet deleted file mode 100644 index 14a2b6ae..00000000 --- a/src/test/resources/org/springframework/restdocs/templates/request-parts.snippet +++ /dev/null @@ -1,13 +0,0 @@ -===== Request Parts - -|=== -|ํŒŒ์ผ๋ช…|ํ•„์ˆ˜๊ฐ’|์„ค๋ช… -{{#fields}} - |{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} - |{{#tableCellContent}} - {{^optional}}true{{/optional}} - {{#optional}}false{{/optional}} - {{/tableCellContent}} - |{{#tableCellContent}}{{description}}{{/tableCellContent}} -{{/fields}} -|=== \ No newline at end of file From 248be3fa77a01897f60ff51e76c25fdc32df0e81 Mon Sep 17 00:00:00 2001 From: gombasan Date: Tue, 15 Aug 2023 00:29:50 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feat=20:=20=EC=8D=B8=EB=84=A4=EC=9D=BC=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ๊ฒŒ์‹œ๊ธ€์„ ์กฐํšŒ ํ•  ๊ฒฝ์šฐ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€์˜ url ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค. - ๊ฒŒ์‹œ๊ธ€์„ ๋“ฑ๋ก ํ•  ๊ฒฝ์šฐ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€์˜ url ์„ ๊ฐ™์ด ์š”์ฒญ๋ฐ›์•„ ๋“ฑ๋กํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค. - ์ธ๋„ค์ผ ์ด๋ฏธ์ง€ url ์€ ์˜ต์…˜๊ฐ’์œผ๋กœ ํ•„์ˆ˜๊ฐ’์€ ์•„๋‹™๋‹ˆ๋‹ค. --- .../clover/habbittracker/domain/post/dto/PostRequest.java | 1 + .../clover/habbittracker/domain/post/dto/PostResponse.java | 1 + .../com/clover/habbittracker/domain/post/entity/Post.java | 5 ++++- src/main/resources/schema.sql | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/clover/habbittracker/domain/post/dto/PostRequest.java b/src/main/java/com/clover/habbittracker/domain/post/dto/PostRequest.java index 334cdc25..a8eb980b 100644 --- a/src/main/java/com/clover/habbittracker/domain/post/dto/PostRequest.java +++ b/src/main/java/com/clover/habbittracker/domain/post/dto/PostRequest.java @@ -5,6 +5,7 @@ public record PostRequest( String title, String content, + String thumbnail, Post.Category category ) { diff --git a/src/main/java/com/clover/habbittracker/domain/post/dto/PostResponse.java b/src/main/java/com/clover/habbittracker/domain/post/dto/PostResponse.java index ac3827b8..16b8607e 100644 --- a/src/main/java/com/clover/habbittracker/domain/post/dto/PostResponse.java +++ b/src/main/java/com/clover/habbittracker/domain/post/dto/PostResponse.java @@ -11,6 +11,7 @@ public record PostResponse( Long id, String title, String content, + String thumbnail, Post.Category category, Long views, Integer numOfComments, diff --git a/src/main/java/com/clover/habbittracker/domain/post/entity/Post.java b/src/main/java/com/clover/habbittracker/domain/post/entity/Post.java index 99643188..56c0d0b6 100644 --- a/src/main/java/com/clover/habbittracker/domain/post/entity/Post.java +++ b/src/main/java/com/clover/habbittracker/domain/post/entity/Post.java @@ -56,15 +56,17 @@ public class Post extends BaseEntity { private String content; private Category category; private Long views; + private String thumbnail; @ManyToOne @JoinColumn(name = "memberId") private Member member; @Builder - public Post(String title, String content, Category category, Member member) { + public Post(String title, String content, Category category, String thumbnail, Member member) { this.title = title; this.content = content; this.category = category; + this.thumbnail = thumbnail; this.member = member; this.views = 0L; } @@ -72,6 +74,7 @@ public Post(String title, String content, Category category, Member member) { public void updatePost(PostRequest postRequest) { this.title = postRequest.title(); this.content = postRequest.content(); + this.thumbnail = postRequest.thumbnail(); this.category = postRequest.category(); } diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 90386f44..de4bee80 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -68,6 +68,7 @@ create table post id bigint auto_increment primary key, title varchar(255) not null, content varchar(255) not null, + thumbnail varchar(255) null, category varchar(255) not null, views bigint not null default 0, member_id bigint null, From 2fa44252f63dc3dbb7f793287996b76f5faa73cb Mon Sep 17 00:00:00 2001 From: gombasan Date: Tue, 15 Aug 2023 00:31:46 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat(Test)=20:=20=EC=8D=B8=EB=84=A4?= =?UTF-8?q?=EC=9D=BC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก, ๋ฆฌ์ŠคํŠธ ์กฐํšŒ ์‹œ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋“ฑ๋ก,์กฐํšŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค. - ์ธ๋„ค์ผ์˜ restDocs ๋ฌธ์„œํ™” ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค. - PostProvider ์—์„œ PostRequest ๋ฅผ ์ƒ์„ฑ ํ•  ๊ฒฝ์šฐ ๊ธฐ๋ณธ ์ธ๋„ค์ผ ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ƒ์„ฑํ•˜๋„๋ก ๋ณ€๊ฒฝํ•˜์˜€์Šต๋‹ˆ๋‹ค. --- .../domain/post/api/PostControllerTest.java | 10 +++-- .../domain/post/service/PostServiceTest.java | 38 ++++++++++--------- .../habbittracker/util/PostProvider.java | 5 ++- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/test/java/com/clover/habbittracker/domain/post/api/PostControllerTest.java b/src/test/java/com/clover/habbittracker/domain/post/api/PostControllerTest.java index c189eff8..ab175314 100644 --- a/src/test/java/com/clover/habbittracker/domain/post/api/PostControllerTest.java +++ b/src/test/java/com/clover/habbittracker/domain/post/api/PostControllerTest.java @@ -21,6 +21,8 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import com.clover.habbittracker.base.RestDocsSupport; import com.clover.habbittracker.domain.post.dto.PostRequest; @@ -28,8 +30,6 @@ import com.clover.habbittracker.domain.post.entity.Post; import com.clover.habbittracker.domain.post.repository.PostRepository; import com.clover.habbittracker.util.CustomTransaction; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; @Transactional(propagation = Propagation.REQUIRED) class PostControllerTest extends RestDocsSupport { @@ -72,6 +72,7 @@ void createPostTest() throws Exception { requestFields( fieldWithPath("title").description("๊ฒŒ์‹œ๊ธ€ ์ œ๋ชฉ").attributes(field("constraints", "์•„์ง ๋ฏธ์ •")), fieldWithPath("content").description("๊ฒŒ์‹œ๊ธ€ ๋ณธ๋ฌธ").attributes(field("constraints", "์•„์ง ๋ฏธ์ •")), + fieldWithPath("thumbnail").description("์ธ๋„ค์ผ ์ด๋ฏธ์ง€ url").optional(), fieldWithPath("category").description(generateLinkCode(DocUrl.CATEGORY)) ), responseHeaders( @@ -103,6 +104,7 @@ void getPostListTest() throws Exception { fieldWithPath("id").type(NUMBER).description("๊ฒŒ์‹œ๊ธ€ id"), fieldWithPath("title").type(STRING).description("๊ฒŒ์‹œ๊ธ€ ์ œ๋ชฉ"), fieldWithPath("content").type(STRING).description("๊ฒŒ์‹œ๊ธ€ ๋ณธ๋ฌธ"), + fieldWithPath("thumbnail").description("๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ url"), fieldWithPath("category").type(STRING).description(generateLinkCode(DocUrl.CATEGORY)), fieldWithPath("views").type(NUMBER).description("์กฐํšŒ์ˆ˜"), fieldWithPath("numOfComments").type(NUMBER).description("๋Œ“๊ธ€ ์ˆ˜"), @@ -184,6 +186,7 @@ void searchPostTest() throws Exception { fieldWithPath("content[].id").type(NUMBER).description("๊ฒŒ์‹œ๊ธ€ id"), fieldWithPath("content[].title").type(STRING).description("๊ฒŒ์‹œ๊ธ€ ์ œ๋ชฉ"), fieldWithPath("content[].content").type(STRING).description("๊ฒŒ์‹œ๊ธ€ ๋ณธ๋ฌธ"), + fieldWithPath("content[].thumbnail").description("๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ url"), fieldWithPath("content[].category").type(STRING).description(generateLinkCode(DocUrl.CATEGORY)), fieldWithPath("content[].views").type(NUMBER).description("์กฐํšŒ์ˆ˜"), fieldWithPath("content[].numOfComments").type(NUMBER).description("๋Œ“๊ธ€ ์ˆ˜"), @@ -200,7 +203,7 @@ void searchPostTest() throws Exception { @DisplayName("๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ์ž๋Š” ๊ฒŒ์‹œ๊ธ€์„ ์ˆ˜์ • ํ•  ์ˆ˜ ์žˆ๋‹ค.") void updatePostTest() throws Exception { //given - PostRequest postRequest = new PostRequest("updateTitle", "updateContent", Post.Category.STUDY); + PostRequest postRequest = new PostRequest("updateTitle", "updateContent", "/thumbnail", Post.Category.STUDY); String request = objectMapper.writeValueAsString(postRequest); //when then @@ -220,6 +223,7 @@ void updatePostTest() throws Exception { requestFields( fieldWithPath("title").description("์ˆ˜์ • ํ•  ๊ฒŒ์‹œ๊ธ€ ์ œ๋ชฉ").attributes(field("constraints", "์•„์ง ๋ฏธ์ •")), fieldWithPath("content").description("์ˆ˜์ • ํ•  ๊ฒŒ์‹œ๊ธ€ ๋ณธ๋ฌธ").attributes(field("constraints", "์•„์ง ๋ฏธ์ •")), + fieldWithPath("thumbnail").description("์ˆ˜์ • ํ•  ์ธ๋„ค์ผ ์ด๋ฏธ์ง€ url").optional(), fieldWithPath("category").description(generateLinkCode(DocUrl.CATEGORY)) ), responseHeaders( diff --git a/src/test/java/com/clover/habbittracker/domain/post/service/PostServiceTest.java b/src/test/java/com/clover/habbittracker/domain/post/service/PostServiceTest.java index 6f61bfb0..ed7a9c4a 100644 --- a/src/test/java/com/clover/habbittracker/domain/post/service/PostServiceTest.java +++ b/src/test/java/com/clover/habbittracker/domain/post/service/PostServiceTest.java @@ -1,14 +1,13 @@ package com.clover.habbittracker.domain.post.service; -import com.clover.habbittracker.domain.member.entity.Member; -import com.clover.habbittracker.domain.member.repository.MemberRepository; -import com.clover.habbittracker.domain.post.dto.PostDetailResponse; -import com.clover.habbittracker.domain.post.dto.PostRequest; -import com.clover.habbittracker.domain.post.dto.PostResponse; -import com.clover.habbittracker.domain.post.dto.PostSearchCondition; -import com.clover.habbittracker.domain.post.entity.Post; -import com.clover.habbittracker.domain.post.repository.PostRepository; -import com.clover.habbittracker.util.CustomTransaction; +import static com.clover.habbittracker.util.MemberProvider.*; +import static com.clover.habbittracker.util.PostProvider.*; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -24,14 +23,15 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.Optional; - -import static com.clover.habbittracker.util.MemberProvider.createTestMember; -import static com.clover.habbittracker.util.PostProvider.createPostRequest; -import static com.clover.habbittracker.util.PostProvider.createTestPost; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; +import com.clover.habbittracker.domain.member.entity.Member; +import com.clover.habbittracker.domain.member.repository.MemberRepository; +import com.clover.habbittracker.domain.post.dto.PostDetailResponse; +import com.clover.habbittracker.domain.post.dto.PostRequest; +import com.clover.habbittracker.domain.post.dto.PostResponse; +import com.clover.habbittracker.domain.post.dto.PostSearchCondition; +import com.clover.habbittracker.domain.post.entity.Post; +import com.clover.habbittracker.domain.post.repository.PostRepository; +import com.clover.habbittracker.util.CustomTransaction; @SpringBootTest @Transactional @@ -85,6 +85,7 @@ void updatePostTest() { assertThat(updatedPost).isPresent(); assertThat(updatedPost.get().getTitle()).isEqualTo(updateRequest.title()); assertThat(updatedPost.get().getContent()).isEqualTo(updateRequest.content()); + assertThat(updatedPost.get().getThumbnail()).isEqualTo(updateRequest.thumbnail()); assertThat(updatedPost.get().getCategory()).isEqualTo(updateRequest.category()); }); } @@ -144,6 +145,7 @@ void categoryTest() { assertThat(categoryFilterPost.size()).isEqualTo(1); assertThat(categoryFilterPost.get(0).title()).isEqualTo(savedPost.getTitle()); assertThat(categoryFilterPost.get(0).content()).isEqualTo(savedPost.getContent()); + assertThat(categoryFilterPost.get(0).thumbnail()).isEqualTo(savedPost.getThumbnail()); assertThat(categoryFilterPost.get(0).category()).isEqualTo(savedPost.getCategory()); }); @@ -166,6 +168,7 @@ void titleTest() { assertThat(postList.size()).isEqualTo(1); assertThat(postList.get(0).title()).isEqualTo(savedPost.getTitle()); assertThat(postList.get(0).content()).isEqualTo(savedPost.getContent()); + assertThat(postList.get(0).thumbnail()).isEqualTo(savedPost.getThumbnail()); assertThat(postList.get(0).category()).isEqualTo(savedPost.getCategory()); }); } @@ -187,6 +190,7 @@ void contentTest() { assertThat(postList.size()).isEqualTo(1); assertThat(postList.get(0).title()).isEqualTo(savedPost.getTitle()); assertThat(postList.get(0).content()).isEqualTo(savedPost.getContent()); + assertThat(postList.get(0).thumbnail()).isEqualTo(savedPost.getThumbnail()); assertThat(postList.get(0).category()).isEqualTo(savedPost.getCategory()); }); } diff --git a/src/test/java/com/clover/habbittracker/util/PostProvider.java b/src/test/java/com/clover/habbittracker/util/PostProvider.java index be611770..731d8ce8 100644 --- a/src/test/java/com/clover/habbittracker/util/PostProvider.java +++ b/src/test/java/com/clover/habbittracker/util/PostProvider.java @@ -10,6 +10,7 @@ public class PostProvider { private static final String DEFAULT_CONTENT = "testContent"; private static final String REQUEST_TITLE = "requestTitle"; private static final String REQUEST_CONTENT = "requestContent"; + private static final String DEFAULT_THUMBNAIL = "/thumbnail"; private static final Post.Category DEFAULT_CATEGORY = Post.Category.DAILY; private PostProvider() { @@ -33,6 +34,7 @@ public static Post createTestPost(Member member, Post.Category category) { .member(member) .build(); } + public static Post createTestPost(Member member, String title) { return Post.builder() .title(title) @@ -41,6 +43,7 @@ public static Post createTestPost(Member member, String title) { .member(member) .build(); } + public static Post createTestPost(Member member, String title, String content) { return Post.builder() .title(title) @@ -51,6 +54,6 @@ public static Post createTestPost(Member member, String title, String content) { } public static PostRequest createPostRequest() { - return new PostRequest(REQUEST_TITLE, REQUEST_CONTENT, DEFAULT_CATEGORY); + return new PostRequest(REQUEST_TITLE, REQUEST_CONTENT, DEFAULT_THUMBNAIL, DEFAULT_CATEGORY); } } From 608b9877e6774f34d3574fca0ec646bb22f0ef62 Mon Sep 17 00:00:00 2001 From: kwonjoosung Date: Tue, 15 Aug 2023 19:41:05 +0900 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/infra/storage/api/ObjectStorageControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageControllerTest.java b/src/test/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageControllerTest.java index 61db48a7..9322d139 100644 --- a/src/test/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageControllerTest.java +++ b/src/test/java/com/clover/habbittracker/global/infra/storage/api/ObjectStorageControllerTest.java @@ -26,7 +26,7 @@ public class ObjectStorageControllerTest extends RestDocsSupport { @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); - when(objectStorageService.imgSave(any(MultipartFile.class))).thenReturn("/sucess/url"); + when(objectStorageService.imgSave(any(MultipartFile.class))).thenReturn("/success/url"); } @Test