From 10b2c34e81d43d5c4bf2c5452772bd7ab316e650 Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Thu, 7 Sep 2023 17:15:32 +0900 Subject: [PATCH 01/13] =?UTF-8?q?chore=20:=20S3=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/build.gradle b/backend/build.gradle index ceb7e1b5..c4192d94 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -45,6 +45,10 @@ dependencies { testImplementation 'io.rest-assured:spring-mock-mvc' testImplementation 'org.assertj:assertj-core:3.19.0' + // S3 + implementation platform('com.amazonaws:aws-java-sdk-bom:1.11.1000') + implementation 'com.amazonaws:aws-java-sdk-s3' + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' From 1117fff562ef18bc2c84b50dc8422cb00d5efbf4 Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Thu, 7 Sep 2023 17:16:47 +0900 Subject: [PATCH 02/13] =?UTF-8?q?feat=20:=20Amazon=20S3=20Component=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 --- .../mapbefine/common/config/S3Config.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/common/config/S3Config.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/common/config/S3Config.java b/backend/src/main/java/com/mapbefine/mapbefine/common/config/S3Config.java new file mode 100644 index 00000000..204a09bc --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/common/config/S3Config.java @@ -0,0 +1,19 @@ +package com.mapbefine.mapbefine.common.config; + +import com.amazonaws.regions.Regions; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + + @Bean + public AmazonS3 amazonS3() { + return AmazonS3ClientBuilder.standard() + .withRegion(Regions.AP_NORTHEAST_2) + .build(); + } + +} From 55fe8bff28e74ec69273c33705543d6fc1f9ef04 Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Thu, 7 Sep 2023 19:10:05 +0900 Subject: [PATCH 03/13] =?UTF-8?q?feat=20:=20S3=20=EC=97=90=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EB=90=A0=20Image=20=EC=9D=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20=EC=84=A4=EC=A0=95=ED=95=B4=EC=A3=BC?= =?UTF-8?q?=EB=8A=94=20ImageName=20=EA=B3=BC=20UploadFile=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 --- .../mapbefine/s3/domain/ImageName.java | 34 ++++++++ .../mapbefine/s3/domain/UploadFile.java | 81 +++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/s3/domain/ImageName.java create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/s3/domain/UploadFile.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/ImageName.java b/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/ImageName.java new file mode 100644 index 00000000..c53553f6 --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/ImageName.java @@ -0,0 +1,34 @@ +package com.mapbefine.mapbefine.s3.domain; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class ImageName { + + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSSSS"); + private static final String EXTENSION_DELIMITER = "."; + + private final String fileName; + + private ImageName(String fileName) { + this.fileName = fileName; + } + + public static ImageName from(String originalFileName) { + String fileName = FORMATTER.format(LocalDateTime.now()); + String extension = getExtension(originalFileName); + + return new ImageName(fileName + extension); + } + + private static String getExtension(String originalFileName) { + return originalFileName.substring( + originalFileName.lastIndexOf(EXTENSION_DELIMITER) + ); + } + + public String getFileName() { + return fileName; + } + +} diff --git a/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/UploadFile.java b/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/UploadFile.java new file mode 100644 index 00000000..625e031e --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/UploadFile.java @@ -0,0 +1,81 @@ +package com.mapbefine.mapbefine.s3.domain; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import org.springframework.core.io.Resource; +import org.springframework.web.multipart.MultipartFile; + +public class UploadFile implements MultipartFile { + + private final String fileName; + private final byte[] bytes; + + private UploadFile( + String fileName, + byte[] bytes + ) { + this.fileName = fileName; + this.bytes = bytes; + } + + public static UploadFile of( + MultipartFile multipartFile + ) throws IOException { + ImageName imageName = ImageName.from(multipartFile.getOriginalFilename()); + byte[] multipartFileBytes = multipartFile.getBytes(); + + return new UploadFile(imageName.getFileName(), multipartFileBytes); + } + + @Override + public String getName() { + return fileName; + } + + @Override + public String getOriginalFilename() { + return fileName; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public long getSize() { + return 0; + } + + @Override + public byte[] getBytes() throws IOException { + return bytes; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(bytes); + } + + @Override + public Resource getResource() { + return MultipartFile.super + .getResource(); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + FileOutputStream fileOutputStream = new FileOutputStream(dest); + fileOutputStream.write(bytes); + fileOutputStream.close(); + } + +} From dda3f46e253879689e5e2dd405881a02afb47446 Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Thu, 7 Sep 2023 19:12:15 +0900 Subject: [PATCH 04/13] =?UTF-8?q?feat=20:=20S3=20=EC=97=90=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EB=90=A0=20Image=20=EC=9D=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20=EC=84=A4=EC=A0=95=ED=95=B4=EC=A3=BC?= =?UTF-8?q?=EB=8A=94=20ImageNae=20=EA=B3=BC=20UploadFile=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 --- .../mapbefine/s3/domain/S3Client.java | 47 +++++++++++++++++++ .../mapbefine/s3/domain/UploadFile.java | 6 +-- 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/s3/domain/S3Client.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/S3Client.java b/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/S3Client.java new file mode 100644 index 00000000..ab9775b5 --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/S3Client.java @@ -0,0 +1,47 @@ +package com.mapbefine.mapbefine.s3.domain; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.PutObjectRequest; +import java.io.File; +import java.io.IOException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component +public class S3Client { + + @Value("${s3.bucket}") + private String bucket; + private final AmazonS3 amazonS3; + + public S3Client(AmazonS3 amazonS3) { + this.amazonS3 = amazonS3; + } + + public void upload(MultipartFile multipartFile) { + File tempFile = null; + + try { + tempFile = File.createTempFile("upload_", ".tmp"); + multipartFile.transferTo(tempFile); + amazonS3.putObject(new PutObjectRequest(bucket, multipartFile.getOriginalFilename(), tempFile)); + } catch (IOException e) { // TODO: 2023/09/07 Exception 을 수정 + throw new RuntimeException(e); + } finally { + removeTempFileIfExists(tempFile); + } + } + + private void removeTempFileIfExists(final File tempFile) { + if (tempFile != null && tempFile.exists()) { + tempFile.delete(); + } + } + + public void delete(String key) { + amazonS3.deleteObject(new DeleteObjectRequest(bucket, key)); + } + +} diff --git a/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/UploadFile.java b/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/UploadFile.java index 625e031e..79a3b502 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/UploadFile.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/s3/domain/UploadFile.java @@ -73,9 +73,9 @@ public Resource getResource() { @Override public void transferTo(File dest) throws IOException, IllegalStateException { - FileOutputStream fileOutputStream = new FileOutputStream(dest); - fileOutputStream.write(bytes); - fileOutputStream.close(); + try (FileOutputStream fileOutputStream = new FileOutputStream(dest)) { + fileOutputStream.write(bytes); + } } } From cb8ee53791ace43e3afe6f07ed0e89844b1c4a6a Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Thu, 7 Sep 2023 19:35:08 +0900 Subject: [PATCH 05/13] =?UTF-8?q?feat=20:=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=ED=95=98=EA=B3=A0=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=ED=95=98=EB=8A=94=20URL=20=EC=9D=84=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=95=98=EB=8A=94=20Service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapbefine/s3/application/S3Service.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3Service.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3Service.java b/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3Service.java new file mode 100644 index 00000000..971503e3 --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3Service.java @@ -0,0 +1,40 @@ +package com.mapbefine.mapbefine.s3.application; + +import com.mapbefine.mapbefine.s3.domain.S3Client; +import com.mapbefine.mapbefine.s3.domain.UploadFile; +import java.io.IOException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +@Service +public class S3Service { + + @Value("prefix-upload-path") + private String prefixUploadPath; + private final S3Client s3Client; + + public S3Service(S3Client s3Client) { + this.s3Client = s3Client; + } + + public String upload(MultipartFile multipartFile) { + try { + UploadFile uploadFile = UploadFile.of(multipartFile); + s3Client.upload(uploadFile); + return getUploadPath(uploadFile); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + } + + private String getUploadPath(final UploadFile uploadFile) { + return String.join( + "/", + prefixUploadPath, + uploadFile.getOriginalFilename() + ); + } + +} + From 4098aed02168a09dfae5119c305c4272b6d8c2cf Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Thu, 7 Sep 2023 19:37:13 +0900 Subject: [PATCH 06/13] =?UTF-8?q?chore=20:=20s3=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config index ebb17a89..dd7978f2 160000 --- a/backend/src/main/resources/config +++ b/backend/src/main/resources/config @@ -1 +1 @@ -Subproject commit ebb17a895391a6cec3a893d48f632c72b0234326 +Subproject commit dd7978f2e6b35470eec1a3ff03db3a403b7d575e From 46bbd8bcd918a5bea91c6985d0f57833c6dca9b2 Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Thu, 7 Sep 2023 19:57:44 +0900 Subject: [PATCH 07/13] =?UTF-8?q?feat=20:=20String=20image=20->=20Multipar?= =?UTF-8?q?tFile=20image=20=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 --- .../mapbefine/pin/dto/request/PinImageCreateRequest.java | 4 +++- .../mapbefine/topic/dto/request/TopicCreateRequest.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/dto/request/PinImageCreateRequest.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/dto/request/PinImageCreateRequest.java index d43e74ee..4ad5ef73 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/dto/request/PinImageCreateRequest.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/dto/request/PinImageCreateRequest.java @@ -1,7 +1,9 @@ package com.mapbefine.mapbefine.pin.dto.request; +import org.springframework.web.multipart.MultipartFile; + public record PinImageCreateRequest( Long pinId, - String imageUrl + MultipartFile image ) { } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequest.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequest.java index e7dc64c5..78e44a18 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequest.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequest.java @@ -3,10 +3,11 @@ import com.mapbefine.mapbefine.topic.domain.PermissionType; import com.mapbefine.mapbefine.topic.domain.Publicity; import java.util.List; +import org.springframework.web.multipart.MultipartFile; public record TopicCreateRequest( String name, - String image, + MultipartFile image, String description, Publicity publicity, PermissionType permissionType, From 467ee08bb3900af62ea7a0190d4ecd1d4dbdf96d Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Thu, 7 Sep 2023 19:58:18 +0900 Subject: [PATCH 08/13] =?UTF-8?q?feat=20:=20@RequestPart=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20=EB=B0=8F=20S3=20upload=20=EB=A1=9C=EC=A7=81=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 --- .../mapbefine/pin/application/PinCommandService.java | 10 +++++++--- .../mapbefine/pin/presentation/PinController.java | 3 ++- .../topic/application/TopicCommandService.java | 9 +++++++-- .../mapbefine/topic/presentation/TopicController.java | 3 ++- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinCommandService.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinCommandService.java index 80f0cd35..2d563e4f 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinCommandService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinCommandService.java @@ -21,6 +21,7 @@ import com.mapbefine.mapbefine.pin.dto.request.PinUpdateRequest; import com.mapbefine.mapbefine.pin.exception.PinException.PinBadRequestException; import com.mapbefine.mapbefine.pin.exception.PinException.PinForbiddenException; +import com.mapbefine.mapbefine.s3.application.S3Service; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicBadRequestException; @@ -40,22 +41,24 @@ public class PinCommandService { private final TopicRepository topicRepository; private final MemberRepository memberRepository; private final PinImageRepository pinImageRepository; + private final S3Service s3Service; public PinCommandService( PinRepository pinRepository, LocationRepository locationRepository, TopicRepository topicRepository, MemberRepository memberRepository, - PinImageRepository pinImageRepository + PinImageRepository pinImageRepository, + S3Service s3Service ) { this.pinRepository = pinRepository; this.locationRepository = locationRepository; this.topicRepository = topicRepository; this.memberRepository = memberRepository; this.pinImageRepository = pinImageRepository; + this.s3Service = s3Service; } - public long save(AuthMember authMember, PinCreateRequest request) { Topic topic = findTopic(request.topicId()); validatePinCreateOrUpdate(authMember, topic); @@ -138,8 +141,9 @@ public void removeById(AuthMember authMember, Long pinId) { public void addImage(AuthMember authMember, PinImageCreateRequest request) { Pin pin = findPin(request.pinId()); validatePinCreateOrUpdate(authMember, pin.getTopic()); + String image = s3Service.upload(request.image()); - PinImage pinImage = PinImage.createPinImageAssociatedWithPin(request.imageUrl(), pin); + PinImage pinImage = PinImage.createPinImageAssociatedWithPin(image, pin); pinImageRepository.save(pinImage); } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/presentation/PinController.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/presentation/PinController.java index b85d6cb9..fe638e6f 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/presentation/PinController.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/presentation/PinController.java @@ -21,6 +21,7 @@ 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.RequestPart; import org.springframework.web.bind.annotation.RestController; @RestController @@ -93,7 +94,7 @@ public ResponseEntity> findAllPinsByMemberId( @LoginRequired @PostMapping("/images") - public ResponseEntity addImage(AuthMember member, @RequestBody PinImageCreateRequest request) { + public ResponseEntity addImage(AuthMember member, @RequestPart PinImageCreateRequest request) { pinCommandService.addImage(member, request); return ResponseEntity.status(HttpStatus.CREATED).build(); diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicCommandService.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicCommandService.java index 77b77643..1466c02d 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicCommandService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicCommandService.java @@ -14,6 +14,7 @@ import com.mapbefine.mapbefine.pin.domain.PinRepository; import com.mapbefine.mapbefine.pin.exception.PinException.PinBadRequestException; import com.mapbefine.mapbefine.pin.exception.PinException.PinForbiddenException; +import com.mapbefine.mapbefine.s3.application.S3Service; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; import com.mapbefine.mapbefine.topic.dto.request.TopicCreateRequest; @@ -35,15 +36,18 @@ public class TopicCommandService { private final TopicRepository topicRepository; private final PinRepository pinRepository; private final MemberRepository memberRepository; + private final S3Service s3Service; public TopicCommandService( TopicRepository topicRepository, PinRepository pinRepository, - MemberRepository memberRepository + MemberRepository memberRepository, + S3Service s3Service ) { this.topicRepository = topicRepository; this.pinRepository = pinRepository; this.memberRepository = memberRepository; + this.s3Service = s3Service; } public Long saveTopic(AuthMember member, TopicCreateRequest request) { @@ -61,11 +65,12 @@ public Long saveTopic(AuthMember member, TopicCreateRequest request) { private Topic convertToTopic(AuthMember member, TopicCreateRequest request) { Member creator = findCreatorByAuthMember(member); + String image = s3Service.upload(request.image()); return Topic.createTopicAssociatedWithCreator( request.name(), request.description(), - request.image(), + image, request.publicity(), request.permissionType(), creator diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java index fa1842db..2a6a53b1 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java @@ -20,6 +20,7 @@ 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.RequestPart; import org.springframework.web.bind.annotation.RestController; @RestController @@ -39,7 +40,7 @@ public TopicController( @LoginRequired @PostMapping("/new") - public ResponseEntity create(AuthMember member, @RequestBody TopicCreateRequest request) { + public ResponseEntity create(AuthMember member, @RequestPart TopicCreateRequest request) { Long topicId = topicCommandService.saveTopic(member, request); return ResponseEntity.created(URI.create("/topics/" + topicId)) From 54ad2dec15ddda1ab46318f9617d9257b3dbb7f1 Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Sat, 9 Sep 2023 02:22:20 +0900 Subject: [PATCH 09/13] =?UTF-8?q?chore=20:=20Test=20=EC=8B=9C=20Profile=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/test/resources/application.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml index b0c95fea..283d08bc 100644 --- a/backend/src/test/resources/application.yml +++ b/backend/src/test/resources/application.yml @@ -1,10 +1,15 @@ spring: + profiles: + active: test + datasource: url: jdbc:h2:mem:mapbefine;MODE=MySQL username: sa + sql: init: data-locations: + jpa: show-sql: true hibernate: From 2c2eb2e0a821dac5d1b4a2e1b6ca61f0d5b774bf Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Sat, 9 Sep 2023 02:25:36 +0900 Subject: [PATCH 10/13] =?UTF-8?q?test=20:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=9C=20S3=20=EC=97=90=20=EC=A0=91=EA=B7=BC=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20Profile=20=EB=B3=84=EB=A1=9C=20S3Service?= =?UTF-8?q?=20Bean=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapbefine/s3/application/S3Service.java | 33 +------------- .../s3/application/S3ServiceImpl.java | 43 +++++++++++++++++++ .../mapbefine/TestS3ServiceImpl.java | 18 ++++++++ 3 files changed, 63 insertions(+), 31 deletions(-) create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3ServiceImpl.java create mode 100644 backend/src/test/java/com/mapbefine/mapbefine/TestS3ServiceImpl.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3Service.java b/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3Service.java index 971503e3..a451f3b4 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3Service.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3Service.java @@ -1,40 +1,11 @@ package com.mapbefine.mapbefine.s3.application; -import com.mapbefine.mapbefine.s3.domain.S3Client; -import com.mapbefine.mapbefine.s3.domain.UploadFile; -import java.io.IOException; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @Service -public class S3Service { +public interface S3Service { - @Value("prefix-upload-path") - private String prefixUploadPath; - private final S3Client s3Client; - - public S3Service(S3Client s3Client) { - this.s3Client = s3Client; - } - - public String upload(MultipartFile multipartFile) { - try { - UploadFile uploadFile = UploadFile.of(multipartFile); - s3Client.upload(uploadFile); - return getUploadPath(uploadFile); - } catch (IOException exception) { - throw new RuntimeException(exception); - } - } - - private String getUploadPath(final UploadFile uploadFile) { - return String.join( - "/", - prefixUploadPath, - uploadFile.getOriginalFilename() - ); - } + String upload(MultipartFile multipartFile); } - diff --git a/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3ServiceImpl.java b/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3ServiceImpl.java new file mode 100644 index 00000000..843dc18c --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/s3/application/S3ServiceImpl.java @@ -0,0 +1,43 @@ +package com.mapbefine.mapbefine.s3.application; + +import com.mapbefine.mapbefine.s3.domain.S3Client; +import com.mapbefine.mapbefine.s3.domain.UploadFile; +import java.io.IOException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +@Service +@Profile("!test") +public class S3ServiceImpl implements S3Service { + + @Value("${prefix.upload.path}") + private String prefixUploadPath; + private final S3Client s3Client; + + public S3ServiceImpl(S3Client s3Client) { + this.s3Client = s3Client; + } + + @Override + public String upload(MultipartFile multipartFile) { + try { + UploadFile uploadFile = UploadFile.of(multipartFile); + s3Client.upload(uploadFile); + return getUploadPath(uploadFile); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + } + + private String getUploadPath(final UploadFile uploadFile) { + return String.join( + "/", + prefixUploadPath, + uploadFile.getOriginalFilename() + ); + } + +} + diff --git a/backend/src/test/java/com/mapbefine/mapbefine/TestS3ServiceImpl.java b/backend/src/test/java/com/mapbefine/mapbefine/TestS3ServiceImpl.java new file mode 100644 index 00000000..b53f80df --- /dev/null +++ b/backend/src/test/java/com/mapbefine/mapbefine/TestS3ServiceImpl.java @@ -0,0 +1,18 @@ +package com.mapbefine.mapbefine; + +import com.mapbefine.mapbefine.s3.application.S3Service; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +@Service +@Profile("test") +public class TestS3ServiceImpl implements S3Service { + + @Override + public String upload(MultipartFile multipartFile) { + System.out.println("TestS3ServiceImple Upload Method Called !!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + return "https://mapbefine.github.io/favicon.png"; + } + +} From bd5d76d38980a5ea21d7c6cc75e1be9146a68e6f Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Sat, 9 Sep 2023 02:27:36 +0900 Subject: [PATCH 11/13] =?UTF-8?q?test=20:=20=EC=B6=94=EA=B0=80=EB=90=9C=20?= =?UTF-8?q?Image=20=EC=A0=80=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EC=B6=B0=20=EC=9D=BC=EB=B6=80=20Test=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 --- .../pin/presentation/PinController.java | 15 ++++- .../com/mapbefine/mapbefine/FileFixture.java | 11 ++++ .../com/mapbefine/mapbefine/StubFile.java | 63 +++++++++++++++++++ .../oauth/application/OauthServiceTest.java | 1 - .../oauth/domain/OauthMemberTest.java | 1 - .../mapbefine/pin/PinIntegrationTest.java | 21 +++++-- .../application/PinCommandServiceTest.java | 12 ++-- .../pin/presentation/PinControllerTest.java | 4 +- .../mapbefine/topic/TopicFixture.java | 4 +- .../mapbefine/topic/TopicIntegrationTest.java | 17 ++--- .../presentation/TopicControllerTest.java | 3 +- backend/src/test/resources/test.png | 0 12 files changed, 127 insertions(+), 25 deletions(-) create mode 100644 backend/src/test/java/com/mapbefine/mapbefine/FileFixture.java create mode 100644 backend/src/test/java/com/mapbefine/mapbefine/StubFile.java create mode 100644 backend/src/test/resources/test.png diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/presentation/PinController.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/presentation/PinController.java index fe638e6f..2334d1c9 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/presentation/PinController.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/presentation/PinController.java @@ -12,6 +12,7 @@ import java.net.URI; import java.util.List; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/pins") @@ -93,9 +95,16 @@ public ResponseEntity> findAllPinsByMemberId( } @LoginRequired - @PostMapping("/images") - public ResponseEntity addImage(AuthMember member, @RequestPart PinImageCreateRequest request) { - pinCommandService.addImage(member, request); + @PostMapping( + value = "/images", + consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE} + ) + public ResponseEntity addImage( + AuthMember member, + @RequestPart Long pinId, + @RequestPart MultipartFile image + ) { + pinCommandService.addImage(member, new PinImageCreateRequest(pinId, image)); return ResponseEntity.status(HttpStatus.CREATED).build(); } diff --git a/backend/src/test/java/com/mapbefine/mapbefine/FileFixture.java b/backend/src/test/java/com/mapbefine/mapbefine/FileFixture.java new file mode 100644 index 00000000..7acc3fec --- /dev/null +++ b/backend/src/test/java/com/mapbefine/mapbefine/FileFixture.java @@ -0,0 +1,11 @@ +package com.mapbefine.mapbefine; + +import org.springframework.web.multipart.MultipartFile; + +public class FileFixture { + + public static MultipartFile createFile() { + return new StubFile(); + } + +} diff --git a/backend/src/test/java/com/mapbefine/mapbefine/StubFile.java b/backend/src/test/java/com/mapbefine/mapbefine/StubFile.java new file mode 100644 index 00000000..f90b4407 --- /dev/null +++ b/backend/src/test/java/com/mapbefine/mapbefine/StubFile.java @@ -0,0 +1,63 @@ +package com.mapbefine.mapbefine; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Base64; +import org.springframework.web.multipart.MultipartFile; + +public class StubFile implements MultipartFile { + + private final String fileName; + private final byte[] bytes; + + public StubFile() { + this.fileName = "yyyyMMddHHmmssSSSSSS"; + this.bytes = fileName.getBytes(); + } + + @Override + public String getName() { + return fileName; + } + + @Override + public String getOriginalFilename() { + return fileName; + } + + @Override + public String getContentType() { + return "text/plain"; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public long getSize() { + return 0; + } + + @Override + public byte[] getBytes() throws IOException { + return Base64.getEncoder() + .encode(bytes); + } + + @Override + public InputStream getInputStream() { + return InputStream.nullInputStream(); + } + + @Override + public void transferTo(final File dest) throws IOException, IllegalStateException { + FileOutputStream fileOutputStream = new FileOutputStream(dest); + fileOutputStream.write(this.bytes); + fileOutputStream.close(); + } + +} diff --git a/backend/src/test/java/com/mapbefine/mapbefine/oauth/application/OauthServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/oauth/application/OauthServiceTest.java index 12d143ee..2d5c7122 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/oauth/application/OauthServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/oauth/application/OauthServiceTest.java @@ -83,7 +83,6 @@ void tearDown() { void getAuthCodeRequestUrl_success() { // when String url = oauthService.getAuthCodeRequestUrl(OauthServerType.KAKAO); - System.out.println(oauthMember.toRegisterMember().getMemberInfo().getNickName()); // then assertThat(url).isEqualTo("https://kauth.kakao.com/oauth/authorize?" diff --git a/backend/src/test/java/com/mapbefine/mapbefine/oauth/domain/OauthMemberTest.java b/backend/src/test/java/com/mapbefine/mapbefine/oauth/domain/OauthMemberTest.java index 06e29413..beb6c1b8 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/oauth/domain/OauthMemberTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/oauth/domain/OauthMemberTest.java @@ -22,7 +22,6 @@ void toRegisterMember() { .getMemberInfo() .getNickName(); - System.out.println(expected); assertThat(expected).contains("모험가"); } } diff --git a/backend/src/test/java/com/mapbefine/mapbefine/pin/PinIntegrationTest.java b/backend/src/test/java/com/mapbefine/mapbefine/pin/PinIntegrationTest.java index 0949a5cc..47d7db63 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/pin/PinIntegrationTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/pin/PinIntegrationTest.java @@ -12,7 +12,6 @@ import com.mapbefine.mapbefine.member.domain.MemberRepository; import com.mapbefine.mapbefine.member.domain.Role; import com.mapbefine.mapbefine.pin.dto.request.PinCreateRequest; -import com.mapbefine.mapbefine.pin.dto.request.PinImageCreateRequest; import com.mapbefine.mapbefine.pin.dto.response.PinImageResponse; import com.mapbefine.mapbefine.pin.dto.response.PinResponse; import com.mapbefine.mapbefine.topic.TopicFixture; @@ -21,6 +20,7 @@ import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import java.io.File; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -31,8 +31,6 @@ class PinIntegrationTest extends IntegrationTest { - private static final String BASE_IMAGE = "https://map-befine-official.github.io/favicon.png"; - private Topic topic; private Location location; private Member member; @@ -187,15 +185,28 @@ void addImage_Success() { // when ExtractableResponse response = createPinImage(pinId); + System.out.println(response); + // then assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); } private ExtractableResponse createPinImage(long pinId) { + String imageFilePath = getClass().getClassLoader().getResource("test.png").getPath(); + File mockFile = new File(imageFilePath); + return RestAssured.given().log().all() .header(AUTHORIZATION, authHeader) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(new PinImageCreateRequest(pinId, BASE_IMAGE)) + .multiPart( + "image", + mockFile, + MediaType.MULTIPART_FORM_DATA_VALUE + ) + .multiPart( + "pinId", + pinId, + MediaType.APPLICATION_JSON_VALUE + ) .when().post("/pins/images") .then().log().all() .extract(); diff --git a/backend/src/test/java/com/mapbefine/mapbefine/pin/application/PinCommandServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/pin/application/PinCommandServiceTest.java index ef41b89f..362242e9 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/pin/application/PinCommandServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/pin/application/PinCommandServiceTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.mapbefine.mapbefine.FileFixture; import com.mapbefine.mapbefine.auth.domain.AuthMember; import com.mapbefine.mapbefine.auth.domain.member.Admin; import com.mapbefine.mapbefine.auth.domain.member.Guest; @@ -33,11 +34,13 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.multipart.MultipartFile; @ServiceTest class PinCommandServiceTest { - private static final String BASE_IMAGE = "https://map-befine-official.github.io/favicon.png"; + private static final MultipartFile BASE_IMAGE_FILE = FileFixture.createFile(); + private static final String BASE_IMAGE = "https://mapbefine.github.io/favicon.png"; @Autowired private PinCommandService pinCommandService; @@ -192,7 +195,7 @@ void addImage_Success() { // then pinImageRepository.findById(pinImageId) .ifPresentOrElse( - found -> assertThat(found.getImageUrl()).isEqualTo(BASE_IMAGE), + found -> assertThat(found.getImageUrl()).isNotNull(), Assertions::fail ); } @@ -204,7 +207,8 @@ void addImage_FailByForbidden() { long pinId = pinCommandService.save(authMember, createRequest); // when, then - assertThatThrownBy(() -> pinCommandService.addImage(new Guest(), new PinImageCreateRequest(pinId, BASE_IMAGE))) + assertThatThrownBy(() -> pinCommandService.addImage(new Guest(), new PinImageCreateRequest(pinId, + BASE_IMAGE_FILE))) .isInstanceOf(PinForbiddenException.class); } @@ -227,7 +231,7 @@ void removeImageById_Success() { } private long savePinImageAndGetId(long pinId) { - pinCommandService.addImage(authMember, new PinImageCreateRequest(pinId, BASE_IMAGE)); + pinCommandService.addImage(authMember, new PinImageCreateRequest(pinId, BASE_IMAGE_FILE)); List pinImages = pinQueryService.findDetailById(authMember, pinId) .images(); PinImageResponse pinImage = pinImages.get(0); diff --git a/backend/src/test/java/com/mapbefine/mapbefine/pin/presentation/PinControllerTest.java b/backend/src/test/java/com/mapbefine/mapbefine/pin/presentation/PinControllerTest.java index 74252c88..1b17f8b6 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/pin/presentation/PinControllerTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/pin/presentation/PinControllerTest.java @@ -6,6 +6,7 @@ import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import com.mapbefine.mapbefine.FileFixture; import com.mapbefine.mapbefine.common.RestDocsIntegration; import com.mapbefine.mapbefine.pin.application.PinCommandService; import com.mapbefine.mapbefine.pin.application.PinQueryService; @@ -148,7 +149,8 @@ void findAll() throws Exception { void addImage() throws Exception { PinImageCreateRequest pinImageCreateRequest = new PinImageCreateRequest( 1L, - "https://map-befine-official.github.io/favicon.png" + FileFixture.createFile() +// "https://map-befine-official.github.io/favicon.png" ); mockMvc.perform( diff --git a/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicFixture.java b/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicFixture.java index 818e81f3..ed8a196a 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicFixture.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicFixture.java @@ -1,6 +1,7 @@ package com.mapbefine.mapbefine.topic; +import com.mapbefine.mapbefine.FileFixture; import com.mapbefine.mapbefine.member.domain.Member; import com.mapbefine.mapbefine.topic.domain.PermissionType; import com.mapbefine.mapbefine.topic.domain.Publicity; @@ -62,7 +63,8 @@ public static TopicCreateRequest createPublicAndAllMembersCreateRequestWithPins( ) { return new TopicCreateRequest( "아무나 읽을 수 있는 토픽", - IMAGE_URL, + FileFixture.createFile(), +// IMAGE_URL, "아무나 읽을 수 있는 토픽입니다.", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, diff --git a/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicIntegrationTest.java b/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicIntegrationTest.java index 833e5333..0ae7d52b 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicIntegrationTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicIntegrationTest.java @@ -3,6 +3,7 @@ import static org.apache.http.HttpHeaders.AUTHORIZATION; import static org.assertj.core.api.Assertions.assertThat; +import com.mapbefine.mapbefine.FileFixture; import com.mapbefine.mapbefine.bookmark.domain.Bookmark; import com.mapbefine.mapbefine.bookmark.domain.BookmarkRepository; import com.mapbefine.mapbefine.common.IntegrationTest; @@ -72,7 +73,7 @@ void setMember() { void createNewTopicWithoutPins_Success() { TopicCreateRequest 준팍의_또간집 = new TopicCreateRequest( "준팍의 또간집", - "https://map-befine-official.github.io/favicon.png", + FileFixture.createFile(), "준팍이 2번 이상 간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -111,7 +112,7 @@ void createNewTopicWithPins_Success() { TopicCreateRequest 준팍의_또간집 = new TopicCreateRequest( "준팍의 또간집", - "https://map-befine-official.github.io/favicon.png", + FileFixture.createFile(), "준팍이 2번 이상 간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -132,7 +133,7 @@ void createMergeTopic_Success() { // given TopicCreateRequest 준팍의_또간집 = new TopicCreateRequest( "준팍의 또간집", - "https://map-befine-official.github.io/favicon.png", + FileFixture.createFile(), "준팍이 2번 이상 간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -140,7 +141,7 @@ void createMergeTopic_Success() { ); TopicCreateRequest 준팍의_또안간집 = new TopicCreateRequest( "준팍의 또안간집", - "https://map-befine-official.github.io/favicon.png", + FileFixture.createFile(), "준팍이 2번 이상 안간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -182,7 +183,7 @@ void updateTopic_Success() { ExtractableResponse newTopic = createNewTopic( new TopicCreateRequest( "준팍의 또간집", - "https://map-befine-official.github.io/favicon.png", + FileFixture.createFile(), "준팍이 두번 간집", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -219,7 +220,7 @@ void deleteTopic_Success() { ExtractableResponse newTopic = createNewTopic( new TopicCreateRequest( "준팍의 또간집", - "https://map-befine-official.github.io/favicon.png", + FileFixture.createFile(), "준팍이 두번 간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -264,7 +265,7 @@ void findTopicDetail_Success() { //given TopicCreateRequest request = new TopicCreateRequest( "topicName", - "https://map-befine-official.github.io/favicon.png", + FileFixture.createFile(), "description", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -293,7 +294,7 @@ void findTopicDetailsByIds_Success() { //given TopicCreateRequest request = new TopicCreateRequest( "topicName", - "https://map-befine-official.github.io/favicon.png", + FileFixture.createFile(), "description", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, diff --git a/backend/src/test/java/com/mapbefine/mapbefine/topic/presentation/TopicControllerTest.java b/backend/src/test/java/com/mapbefine/mapbefine/topic/presentation/TopicControllerTest.java index 4fd2e7f9..31608af0 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/topic/presentation/TopicControllerTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/topic/presentation/TopicControllerTest.java @@ -4,6 +4,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import com.mapbefine.mapbefine.FileFixture; import com.mapbefine.mapbefine.common.RestDocsIntegration; import com.mapbefine.mapbefine.pin.dto.response.PinResponse; import com.mapbefine.mapbefine.topic.application.TopicCommandService; @@ -60,7 +61,7 @@ void create() throws Exception { TopicCreateRequest topicCreateRequest = new TopicCreateRequest( "준팍의 안갈집", - "https://map-befine-official.github.io/favicon.png", + FileFixture.createFile(), "준팍이 두번 다시 안갈집", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, diff --git a/backend/src/test/resources/test.png b/backend/src/test/resources/test.png new file mode 100644 index 00000000..e69de29b From 55e1a917c16b5057c12ae93759add16de814dad3 Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Sat, 9 Sep 2023 02:28:27 +0900 Subject: [PATCH 12/13] =?UTF-8?q?chore=20:=20S3,=20CloudFront=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EC=84=A4=EC=A0=95=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config index dd7978f2..9842778b 160000 --- a/backend/src/main/resources/config +++ b/backend/src/main/resources/config @@ -1 +1 @@ -Subproject commit dd7978f2e6b35470eec1a3ff03db3a403b7d575e +Subproject commit 9842778b9712e4c1eb5c918e2fa254edbafb8a04 From 3b9eac6b2747dbc9024e2a6d955c49a960c63e20 Mon Sep 17 00:00:00 2001 From: jaeyeon kim Date: Mon, 11 Sep 2023 13:20:17 +0900 Subject: [PATCH 13/13] =?UTF-8?q?test=20:=20RestDocs=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../topic/dto/request/TopicCreateRequest.java | 14 ++++++ .../TopicCreateRequestWithOutImage.java | 14 ++++++ .../topic/presentation/TopicController.java | 13 +++++- .../mapbefine/pin/PinIntegrationTest.java | 16 +++---- .../pin/presentation/PinControllerTest.java | 19 +++++--- .../mapbefine/topic/TopicIntegrationTest.java | 45 +++++++++++-------- .../presentation/TopicControllerTest.java | 24 ++++++++-- 7 files changed, 103 insertions(+), 42 deletions(-) create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequestWithOutImage.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequest.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequest.java index 78e44a18..211149fc 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequest.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequest.java @@ -13,4 +13,18 @@ public record TopicCreateRequest( PermissionType permissionType, List pins ) { + + public static TopicCreateRequest of( + TopicCreateRequestWithOutImage request, + MultipartFile image + ) { + return new TopicCreateRequest( + request.name(), + image, + request.description(), + request.publicity(), + request.permissionType(), + request.pins() + ); + } } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequestWithOutImage.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequestWithOutImage.java new file mode 100644 index 00000000..2f59da22 --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/dto/request/TopicCreateRequestWithOutImage.java @@ -0,0 +1,14 @@ +package com.mapbefine.mapbefine.topic.dto.request; + +import com.mapbefine.mapbefine.topic.domain.PermissionType; +import com.mapbefine.mapbefine.topic.domain.Publicity; +import java.util.List; + +public record TopicCreateRequestWithOutImage( + String name, + String description, + Publicity publicity, + PermissionType permissionType, + List pins +) { +} diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java index 2a6a53b1..a614d0e1 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java @@ -5,6 +5,7 @@ import com.mapbefine.mapbefine.topic.application.TopicCommandService; import com.mapbefine.mapbefine.topic.application.TopicQueryService; import com.mapbefine.mapbefine.topic.dto.request.TopicCreateRequest; +import com.mapbefine.mapbefine.topic.dto.request.TopicCreateRequestWithOutImage; import com.mapbefine.mapbefine.topic.dto.request.TopicMergeRequest; import com.mapbefine.mapbefine.topic.dto.request.TopicUpdateRequest; import com.mapbefine.mapbefine.topic.dto.response.TopicDetailResponse; @@ -22,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/topics") @@ -40,8 +42,15 @@ public TopicController( @LoginRequired @PostMapping("/new") - public ResponseEntity create(AuthMember member, @RequestPart TopicCreateRequest request) { - Long topicId = topicCommandService.saveTopic(member, request); + public ResponseEntity create( + AuthMember member, + @RequestPart TopicCreateRequestWithOutImage request, + @RequestPart MultipartFile image + ) { + System.out.println(request); + System.out.println(image); + TopicCreateRequest topicCreateRequest = TopicCreateRequest.of(request, image); + Long topicId = topicCommandService.saveTopic(member, topicCreateRequest); return ResponseEntity.created(URI.create("/topics/" + topicId)) .build(); diff --git a/backend/src/test/java/com/mapbefine/mapbefine/pin/PinIntegrationTest.java b/backend/src/test/java/com/mapbefine/mapbefine/pin/PinIntegrationTest.java index 47d7db63..c1d6b653 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/pin/PinIntegrationTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/pin/PinIntegrationTest.java @@ -192,21 +192,15 @@ void addImage_Success() { } private ExtractableResponse createPinImage(long pinId) { - String imageFilePath = getClass().getClassLoader().getResource("test.png").getPath(); + String imageFilePath = getClass().getClassLoader() + .getResource("test.png") + .getPath(); File mockFile = new File(imageFilePath); return RestAssured.given().log().all() .header(AUTHORIZATION, authHeader) - .multiPart( - "image", - mockFile, - MediaType.MULTIPART_FORM_DATA_VALUE - ) - .multiPart( - "pinId", - pinId, - MediaType.APPLICATION_JSON_VALUE - ) + .multiPart("image", mockFile, MediaType.MULTIPART_FORM_DATA_VALUE) + .multiPart("pinId", pinId, MediaType.APPLICATION_JSON_VALUE) .when().post("/pins/images") .then().log().all() .extract(); diff --git a/backend/src/test/java/com/mapbefine/mapbefine/pin/presentation/PinControllerTest.java b/backend/src/test/java/com/mapbefine/mapbefine/pin/presentation/PinControllerTest.java index 1b17f8b6..c9cbdb24 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/pin/presentation/PinControllerTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/pin/presentation/PinControllerTest.java @@ -3,6 +3,7 @@ import static org.apache.http.HttpHeaders.AUTHORIZATION; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; @@ -16,6 +17,7 @@ import com.mapbefine.mapbefine.pin.dto.response.PinDetailResponse; import com.mapbefine.mapbefine.pin.dto.response.PinImageResponse; import com.mapbefine.mapbefine.pin.dto.response.PinResponse; +import java.io.File; import java.time.LocalDateTime; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -147,17 +149,22 @@ void findAll() throws Exception { @Test @DisplayName("핀 이미지 추가") void addImage() throws Exception { - PinImageCreateRequest pinImageCreateRequest = new PinImageCreateRequest( - 1L, - FileFixture.createFile() -// "https://map-befine-official.github.io/favicon.png" - ); + String imageFilePath = getClass().getClassLoader() + .getResource("test.png") + .getPath(); + File mockFile = new File(imageFilePath); + +// PinImageCreateRequest pinImageCreateRequest = new PinImageCreateRequest( +// 1L, +// FileFixture.createFile() +// ); mockMvc.perform( MockMvcRequestBuilders.post("/pins/images") .header(AUTHORIZATION, testAuthHeaderProvider.createAuthHeaderById(1L)) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(pinImageCreateRequest)) + .content(objectMapper.writeValueAsString(1L)) + .content(objectMapper.writeValueAsString(mockFile)) ).andDo(restDocs.document()); } diff --git a/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicIntegrationTest.java b/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicIntegrationTest.java index 0ae7d52b..f344ce9a 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicIntegrationTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/topic/TopicIntegrationTest.java @@ -22,12 +22,14 @@ import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; import com.mapbefine.mapbefine.topic.dto.request.TopicCreateRequest; +import com.mapbefine.mapbefine.topic.dto.request.TopicCreateRequestWithOutImage; import com.mapbefine.mapbefine.topic.dto.request.TopicMergeRequest; import com.mapbefine.mapbefine.topic.dto.request.TopicUpdateRequest; import com.mapbefine.mapbefine.topic.dto.response.TopicDetailResponse; import com.mapbefine.mapbefine.topic.dto.response.TopicResponse; import io.restassured.*; import io.restassured.response.*; +import java.io.File; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -36,6 +38,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; class TopicIntegrationTest extends IntegrationTest { @@ -71,9 +74,8 @@ void setMember() { @Test @DisplayName("Pin 목록 없이 Topic을 생성하면 201을 반환한다") void createNewTopicWithoutPins_Success() { - TopicCreateRequest 준팍의_또간집 = new TopicCreateRequest( + TopicCreateRequestWithOutImage 준팍의_또간집 = new TopicCreateRequestWithOutImage( "준팍의 또간집", - FileFixture.createFile(), "준팍이 2번 이상 간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -88,12 +90,24 @@ void createNewTopicWithoutPins_Success() { assertThat(response.header("Location")).isNotBlank(); } - private ExtractableResponse createNewTopic(TopicCreateRequest request, String authHeader) { + private ExtractableResponse createNewTopic(TopicCreateRequestWithOutImage request, String authHeader) { + String imageFilePath = getClass().getClassLoader() + .getResource("test.png") + .getPath(); + File mockFile = new File(imageFilePath); + +// MockMultipartFile mockFile = new MockMultipartFile( // 이것은 왜 그런 것일까?? +// "test", +// "test.png", +// "image/png", +// "byteCode".getBytes() +// ); + return RestAssured.given() .log().all() .header(AUTHORIZATION, authHeader) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(request) + .multiPart("image", mockFile, MediaType.MULTIPART_FORM_DATA_VALUE) + .multiPart("request", request, MediaType.APPLICATION_JSON_VALUE) .when().post("/topics/new") .then().log().all() .extract(); @@ -110,9 +124,8 @@ void createNewTopicWithPins_Success() { .map(Pin::getId) .toList(); - TopicCreateRequest 준팍의_또간집 = new TopicCreateRequest( + TopicCreateRequestWithOutImage 준팍의_또간집 = new TopicCreateRequestWithOutImage( "준팍의 또간집", - FileFixture.createFile(), "준팍이 2번 이상 간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -131,17 +144,15 @@ void createNewTopicWithPins_Success() { @DisplayName("여러개의 토픽을 병합하면 201을 반환한다") void createMergeTopic_Success() { // given - TopicCreateRequest 준팍의_또간집 = new TopicCreateRequest( + TopicCreateRequestWithOutImage 준팍의_또간집 = new TopicCreateRequestWithOutImage( "준팍의 또간집", - FileFixture.createFile(), "준팍이 2번 이상 간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, Collections.emptyList() ); - TopicCreateRequest 준팍의_또안간집 = new TopicCreateRequest( + TopicCreateRequestWithOutImage 준팍의_또안간집 = new TopicCreateRequestWithOutImage( "준팍의 또안간집", - FileFixture.createFile(), "준팍이 2번 이상 안간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -181,9 +192,8 @@ void createMergeTopic_Success() { @DisplayName("Topic을 수정하면 200을 반환한다") void updateTopic_Success() { ExtractableResponse newTopic = createNewTopic( - new TopicCreateRequest( + new TopicCreateRequestWithOutImage( "준팍의 또간집", - FileFixture.createFile(), "준팍이 두번 간집", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -218,9 +228,8 @@ void updateTopic_Success() { @DisplayName("Topic을 삭제하면 204를 반환한다") void deleteTopic_Success() { ExtractableResponse newTopic = createNewTopic( - new TopicCreateRequest( + new TopicCreateRequestWithOutImage( "준팍의 또간집", - FileFixture.createFile(), "준팍이 두번 간집 ", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -263,9 +272,8 @@ void findTopics_Success() { @DisplayName("Topic 상세 정보를 조회하면 200을 반환한다") void findTopicDetail_Success() { //given - TopicCreateRequest request = new TopicCreateRequest( + TopicCreateRequestWithOutImage request = new TopicCreateRequestWithOutImage( "topicName", - FileFixture.createFile(), "description", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, @@ -292,9 +300,8 @@ void findTopicDetail_Success() { @DisplayName("Topic 상세 정보 여러개를 조회하면 200을 반환한다") void findTopicDetailsByIds_Success() { //given - TopicCreateRequest request = new TopicCreateRequest( + TopicCreateRequestWithOutImage request = new TopicCreateRequestWithOutImage( "topicName", - FileFixture.createFile(), "description", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, diff --git a/backend/src/test/java/com/mapbefine/mapbefine/topic/presentation/TopicControllerTest.java b/backend/src/test/java/com/mapbefine/mapbefine/topic/presentation/TopicControllerTest.java index 31608af0..2e15e271 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/topic/presentation/TopicControllerTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/topic/presentation/TopicControllerTest.java @@ -3,6 +3,7 @@ import static org.apache.http.HttpHeaders.AUTHORIZATION; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; import com.mapbefine.mapbefine.FileFixture; import com.mapbefine.mapbefine.common.RestDocsIntegration; @@ -12,17 +13,28 @@ import com.mapbefine.mapbefine.topic.domain.PermissionType; import com.mapbefine.mapbefine.topic.domain.Publicity; import com.mapbefine.mapbefine.topic.dto.request.TopicCreateRequest; +import com.mapbefine.mapbefine.topic.dto.request.TopicCreateRequestWithOutImage; import com.mapbefine.mapbefine.topic.dto.request.TopicMergeRequest; import com.mapbefine.mapbefine.topic.dto.request.TopicUpdateRequest; import com.mapbefine.mapbefine.topic.dto.response.TopicDetailResponse; import com.mapbefine.mapbefine.topic.dto.response.TopicResponse; +import java.io.File; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; class TopicControllerTest extends RestDocsIntegration { // TODO: 2023/07/25 Image 칼람 추가됨으로 인해 수정 필요 @@ -58,21 +70,25 @@ class TopicControllerTest extends RestDocsIntegration { // TODO: 2023/07/25 Imag @DisplayName("토픽 새로 생성") void create() throws Exception { given(topicCommandService.saveTopic(any(), any())).willReturn(1L); + File mockFile = new File(getClass().getClassLoader().getResource("test.png").getPath()); + MultiValueMap param = new LinkedMultiValueMap<>(); - TopicCreateRequest topicCreateRequest = new TopicCreateRequest( + TopicCreateRequestWithOutImage request = new TopicCreateRequestWithOutImage( "준팍의 안갈집", - FileFixture.createFile(), "준팍이 두번 다시 안갈집", Publicity.PUBLIC, PermissionType.ALL_MEMBERS, List.of(1L, 2L, 3L) ); + param.add("image", mockFile); + param.add("request", request); + mockMvc.perform( MockMvcRequestBuilders.post("/topics/new") .header(AUTHORIZATION, testAuthHeaderProvider.createAuthHeaderById(1L)) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(topicCreateRequest)) + .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .content(objectMapper.writeValueAsString(mockFile)) ).andDo(restDocs.document()); }