From 1aed5ddb9e9056ae16528c10d1315b686a800abe Mon Sep 17 00:00:00 2001 From: oliviarla Date: Sat, 30 Dec 2023 20:45:42 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20s3=EC=99=80=20cloudfront=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=ED=95=B4=20=ED=8C=8C=EC=9D=BC=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- perfume-api/build.gradle.kts | 3 + .../api/common/config/S3Configuration.java | 28 ++++++++++ .../file/adapter/in/http/FileController.java | 36 +++--------- .../in/http/dto/UpdateFileResponseDto.java | 8 ++- .../persistence/s3/S3PersistenceAdapter.java | 56 +++++++++++++++++++ .../exception/SaveFileNotFoundException.java | 15 ++++- .../port/in/UploadFileUseCase.java | 13 +++++ .../application/port/out/S3Repository.java | 7 +++ .../file/application/service/FileService.java | 38 ------------- .../service/UploadFileService.java | 41 ++++++++++++++ .../in/http/UserSupportController.java | 11 +--- .../port/in/UpdateProfilePicUseCase.java | 3 +- .../service/UpdateProfilePicService.java | 11 ++-- .../src/main/resources/application-dev.yaml | 7 +++ .../src/main/resources/application-local.yaml | 7 +++ .../src/main/resources/application-prod.yaml | 7 +++ .../adapter/in/http/FileControllerTest.java | 14 +++++ ...st.java => FindUploadFileServiceTest.java} | 2 +- .../src/test/resources/application.yaml | 7 +++ 19 files changed, 229 insertions(+), 85 deletions(-) create mode 100644 perfume-api/src/main/java/io/perfume/api/common/config/S3Configuration.java create mode 100644 perfume-api/src/main/java/io/perfume/api/file/adapter/out/persistence/s3/S3PersistenceAdapter.java create mode 100644 perfume-api/src/main/java/io/perfume/api/file/application/port/in/UploadFileUseCase.java create mode 100644 perfume-api/src/main/java/io/perfume/api/file/application/port/out/S3Repository.java delete mode 100644 perfume-api/src/main/java/io/perfume/api/file/application/service/FileService.java create mode 100644 perfume-api/src/main/java/io/perfume/api/file/application/service/UploadFileService.java rename perfume-api/src/test/java/io/perfume/api/file/application/service/{FindFileServiceTest.java => FindUploadFileServiceTest.java} (98%) diff --git a/perfume-api/build.gradle.kts b/perfume-api/build.gradle.kts index f889890a..9aa499c4 100644 --- a/perfume-api/build.gradle.kts +++ b/perfume-api/build.gradle.kts @@ -40,6 +40,9 @@ dependencies { testImplementation("org.springframework.restdocs:spring-restdocs-asciidoctor") testImplementation("io.rest-assured:rest-assured:5.4.0") testImplementation("org.springframework.security:spring-security-test:6.2.1") + + implementation(platform("software.amazon.awssdk:bom:2.17.230")) + implementation("software.amazon.awssdk:s3:2.20.68") } tasks.jar { diff --git a/perfume-api/src/main/java/io/perfume/api/common/config/S3Configuration.java b/perfume-api/src/main/java/io/perfume/api/common/config/S3Configuration.java new file mode 100644 index 00000000..491d8772 --- /dev/null +++ b/perfume-api/src/main/java/io/perfume/api/common/config/S3Configuration.java @@ -0,0 +1,28 @@ +package io.perfume.api.common.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +@Configuration +public class S3Configuration { + @Value("${aws.s3.access-key}") + private String accessKey; + + @Value("${aws.s3.private-key}") + private String privateKey; + + @Bean + public S3Client amazonS3() { + AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKey, privateKey); + + return S3Client.builder() + .region(Region.AP_NORTHEAST_2) + .credentialsProvider(StaticCredentialsProvider.create(credentials)) + .build(); + } +} diff --git a/perfume-api/src/main/java/io/perfume/api/file/adapter/in/http/FileController.java b/perfume-api/src/main/java/io/perfume/api/file/adapter/in/http/FileController.java index 26431594..96ac7bd8 100644 --- a/perfume-api/src/main/java/io/perfume/api/file/adapter/in/http/FileController.java +++ b/perfume-api/src/main/java/io/perfume/api/file/adapter/in/http/FileController.java @@ -2,13 +2,10 @@ import io.perfume.api.file.adapter.in.http.dto.UpdateFileResponseDto; import io.perfume.api.file.application.port.in.dto.FileResult; -import io.perfume.api.file.application.service.FileService; -import java.io.IOException; +import io.perfume.api.file.application.service.UploadFileService; import java.time.LocalDateTime; import java.util.List; -import java.util.Objects; import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; @@ -23,46 +20,29 @@ @RequiredArgsConstructor public class FileController { - private final FileService fileService; + private final UploadFileService uploadFileService; @PostMapping("/v1/file") - public ResponseEntity saveFile( + public UpdateFileResponseDto saveFile( @AuthenticationPrincipal final User user, final MultipartFile file) { final LocalDateTime uploadTime = LocalDateTime.now(); final long userId = parseUserId(user); - final FileResult result = fileService.uploadFile(getFileContent(file), userId, uploadTime); + final FileResult result = uploadFileService.uploadFile(file, userId, uploadTime); - return ResponseEntity.ok(mapToFileResponseDto(result)); + return UpdateFileResponseDto.from(result); } @PostMapping("/v1/files") - public ResponseEntity> saveFiles( + public List saveFiles( @AuthenticationPrincipal final User user, final List files) { final LocalDateTime uploadTime = LocalDateTime.now(); final long userId = parseUserId(user); - final List results = - fileService.uploadFiles(userId, getFileContentAsList(files), uploadTime); + final List results = uploadFileService.uploadFiles(files, userId, uploadTime); - return ResponseEntity.ok(results.stream().map(this::mapToFileResponseDto).toList()); + return results.stream().map(UpdateFileResponseDto::from).toList(); } private long parseUserId(User user) { return Long.parseLong(user.getUsername()); } - - private byte[] getFileContent(MultipartFile file) { - try { - return file.getBytes(); - } catch (IOException e) { - return null; - } - } - - private List getFileContentAsList(List files) { - return files.stream().map(this::getFileContent).filter(Objects::nonNull).toList(); - } - - private UpdateFileResponseDto mapToFileResponseDto(FileResult result) { - return new UpdateFileResponseDto(result.id(), result.url()); - } } diff --git a/perfume-api/src/main/java/io/perfume/api/file/adapter/in/http/dto/UpdateFileResponseDto.java b/perfume-api/src/main/java/io/perfume/api/file/adapter/in/http/dto/UpdateFileResponseDto.java index 64fc6c76..d53faf33 100644 --- a/perfume-api/src/main/java/io/perfume/api/file/adapter/in/http/dto/UpdateFileResponseDto.java +++ b/perfume-api/src/main/java/io/perfume/api/file/adapter/in/http/dto/UpdateFileResponseDto.java @@ -1,3 +1,9 @@ package io.perfume.api.file.adapter.in.http.dto; -public record UpdateFileResponseDto(long id, String url) {} +import io.perfume.api.file.application.port.in.dto.FileResult; + +public record UpdateFileResponseDto(long id, String url) { + public static UpdateFileResponseDto from(FileResult result) { + return new UpdateFileResponseDto(result.id(), result.url()); + } +} diff --git a/perfume-api/src/main/java/io/perfume/api/file/adapter/out/persistence/s3/S3PersistenceAdapter.java b/perfume-api/src/main/java/io/perfume/api/file/adapter/out/persistence/s3/S3PersistenceAdapter.java new file mode 100644 index 00000000..cf77dec2 --- /dev/null +++ b/perfume-api/src/main/java/io/perfume/api/file/adapter/out/persistence/s3/S3PersistenceAdapter.java @@ -0,0 +1,56 @@ +package io.perfume.api.file.adapter.out.persistence.s3; + +import io.perfume.api.base.PersistenceAdapter; +import io.perfume.api.file.application.exception.SaveFileNotFoundException; +import io.perfume.api.file.application.port.out.S3Repository; +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.multipart.MultipartFile; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; + +@PersistenceAdapter +@RequiredArgsConstructor +public class S3PersistenceAdapter implements S3Repository { + @Value("${aws.s3.bucket}") + private String bucketName; + + @Value("${aws.s3.cloudFrontPath}") + private String cloudFrontPath; + + private final S3Client s3Client; + + @Override + public String uploadFile(MultipartFile file) { + String key = file.getOriginalFilename(); + PutObjectRequest putObjectRequest = + PutObjectRequest.builder() + .bucket(bucketName) + .key(key) + .contentType(file.getContentType()) + .contentLength(file.getSize()) + .build(); + + byte[] fileContent = getFileContent(file); + + PutObjectResponse response = + s3Client.putObject(putObjectRequest, RequestBody.fromBytes(fileContent)); + + if (!response.sdkHttpResponse().statusText().orElse("FAIL").equals("OK")) { + throw new IllegalStateException("Failed to upload file to S3."); + } + + return cloudFrontPath + file.getOriginalFilename(); + } + + private byte[] getFileContent(MultipartFile file) { + try { + return file.getBytes(); + } catch (IOException e) { + throw new SaveFileNotFoundException(e.getMessage()); + } + } +} diff --git a/perfume-api/src/main/java/io/perfume/api/file/application/exception/SaveFileNotFoundException.java b/perfume-api/src/main/java/io/perfume/api/file/application/exception/SaveFileNotFoundException.java index 6a5fea33..b213ef92 100644 --- a/perfume-api/src/main/java/io/perfume/api/file/application/exception/SaveFileNotFoundException.java +++ b/perfume-api/src/main/java/io/perfume/api/file/application/exception/SaveFileNotFoundException.java @@ -1,3 +1,16 @@ package io.perfume.api.file.application.exception; -public class SaveFileNotFoundException extends RuntimeException {} +import io.perfume.api.base.CustomHttpException; +import io.perfume.api.base.LogLevel; +import org.springframework.http.HttpStatus; + +public class SaveFileNotFoundException extends CustomHttpException { + + public SaveFileNotFoundException(String message) { + super( + HttpStatus.BAD_REQUEST, + "Failed to save file because " + message, + "failed to save empty file.", + LogLevel.INFO); + } +} diff --git a/perfume-api/src/main/java/io/perfume/api/file/application/port/in/UploadFileUseCase.java b/perfume-api/src/main/java/io/perfume/api/file/application/port/in/UploadFileUseCase.java new file mode 100644 index 00000000..56fe5d4c --- /dev/null +++ b/perfume-api/src/main/java/io/perfume/api/file/application/port/in/UploadFileUseCase.java @@ -0,0 +1,13 @@ +package io.perfume.api.file.application.port.in; + +import io.perfume.api.file.application.port.in.dto.FileResult; +import java.time.LocalDateTime; +import java.util.List; +import org.springframework.web.multipart.MultipartFile; + +public interface UploadFileUseCase { + FileResult uploadFile(MultipartFile file, final long userId, final LocalDateTime now); + + List uploadFiles( + final List files, final long userId, final LocalDateTime now); +} diff --git a/perfume-api/src/main/java/io/perfume/api/file/application/port/out/S3Repository.java b/perfume-api/src/main/java/io/perfume/api/file/application/port/out/S3Repository.java new file mode 100644 index 00000000..69a3d516 --- /dev/null +++ b/perfume-api/src/main/java/io/perfume/api/file/application/port/out/S3Repository.java @@ -0,0 +1,7 @@ +package io.perfume.api.file.application.port.out; + +import org.springframework.web.multipart.MultipartFile; + +public interface S3Repository { + String uploadFile(MultipartFile file); +} diff --git a/perfume-api/src/main/java/io/perfume/api/file/application/service/FileService.java b/perfume-api/src/main/java/io/perfume/api/file/application/service/FileService.java deleted file mode 100644 index dba13724..00000000 --- a/perfume-api/src/main/java/io/perfume/api/file/application/service/FileService.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.perfume.api.file.application.service; - -import io.perfume.api.file.application.port.in.dto.FileResult; -import io.perfume.api.file.application.port.out.FileRepository; -import io.perfume.api.file.domain.File; -import java.time.LocalDateTime; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class FileService { - - private final FileRepository fileRepository; - - public FileResult uploadFile( - final byte[] uploadFile, final long userId, final LocalDateTime now) { - // TODO: AWS S3에 파일 업로드 - final File uploadedFile = - fileRepository.save(File.createFile("https://picsum.photos/200/300", userId, now)); - - return new FileResult(uploadedFile.getId(), uploadedFile.getUrl()); - } - - public List uploadFiles( - final long userId, final List files, final LocalDateTime now) { - return files.stream() - .map( - file -> { - final File uploadedFile = - fileRepository.save( - File.createFile("https://picsum.photos/200/300", userId, now)); - return new FileResult(uploadedFile.getId(), uploadedFile.getUrl()); - }) - .toList(); - } -} diff --git a/perfume-api/src/main/java/io/perfume/api/file/application/service/UploadFileService.java b/perfume-api/src/main/java/io/perfume/api/file/application/service/UploadFileService.java new file mode 100644 index 00000000..3cd359b2 --- /dev/null +++ b/perfume-api/src/main/java/io/perfume/api/file/application/service/UploadFileService.java @@ -0,0 +1,41 @@ +package io.perfume.api.file.application.service; + +import io.perfume.api.file.application.port.in.UploadFileUseCase; +import io.perfume.api.file.application.port.in.dto.FileResult; +import io.perfume.api.file.application.port.out.FileRepository; +import io.perfume.api.file.application.port.out.S3Repository; +import io.perfume.api.file.domain.File; +import jakarta.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +@Service +@RequiredArgsConstructor +public class UploadFileService implements UploadFileUseCase { + + private final FileRepository fileRepository; + private final S3Repository s3Repository; + + @Transactional + public FileResult uploadFile(MultipartFile file, long userId, LocalDateTime now) { + String url = s3Repository.uploadFile(file); + + final File uploadedFile = fileRepository.save(File.createFile(url, userId, now)); + + return new FileResult(uploadedFile.getId(), uploadedFile.getUrl()); + } + + public List uploadFiles(List files, long userId, LocalDateTime now) { + return files.parallelStream() + .map( + file -> { + String url = s3Repository.uploadFile(file); + final File uploadedFile = fileRepository.save(File.createFile(url, userId, now)); + return new FileResult(uploadedFile.getId(), uploadedFile.getUrl()); + }) + .toList(); + } +} diff --git a/perfume-api/src/main/java/io/perfume/api/user/adapter/in/http/UserSupportController.java b/perfume-api/src/main/java/io/perfume/api/user/adapter/in/http/UserSupportController.java index f8ca46c5..08ae3436 100644 --- a/perfume-api/src/main/java/io/perfume/api/user/adapter/in/http/UserSupportController.java +++ b/perfume-api/src/main/java/io/perfume/api/user/adapter/in/http/UserSupportController.java @@ -12,7 +12,6 @@ import io.perfume.api.user.application.port.in.dto.UserProfileResult; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; -import java.io.IOException; import java.time.LocalDateTime; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -120,7 +119,7 @@ public void updateProfilePic(@AuthenticationPrincipal User user, final Multipart var now = LocalDateTime.now(); checkAuthenticatedUser(user); long userId = parseUserId(user); - updateProfilePicUseCase.update(userId, getFileContent(file), now); + updateProfilePicUseCase.update(userId, file, now); } @GetMapping("/users/{id}/reviews") @@ -134,14 +133,6 @@ private long parseUserId(User user) { return Long.parseLong(user.getUsername()); } - private byte[] getFileContent(MultipartFile file) { - try { - return file.getBytes(); - } catch (IOException e) { - return null; - } - } - private void checkAuthenticatedUser(User user) { if (user == null || user.getUsername() == null) { throw new UserNotAuthenticatedException(); diff --git a/perfume-api/src/main/java/io/perfume/api/user/application/port/in/UpdateProfilePicUseCase.java b/perfume-api/src/main/java/io/perfume/api/user/application/port/in/UpdateProfilePicUseCase.java index 7380ebc2..871821b8 100644 --- a/perfume-api/src/main/java/io/perfume/api/user/application/port/in/UpdateProfilePicUseCase.java +++ b/perfume-api/src/main/java/io/perfume/api/user/application/port/in/UpdateProfilePicUseCase.java @@ -2,8 +2,9 @@ import io.perfume.api.user.application.port.in.dto.UpdateProfilePicResult; import java.time.LocalDateTime; +import org.springframework.web.multipart.MultipartFile; public interface UpdateProfilePicUseCase { - UpdateProfilePicResult update(Long userId, byte[] imageFileContent, LocalDateTime now); + UpdateProfilePicResult update(Long userId, MultipartFile file, LocalDateTime now); } diff --git a/perfume-api/src/main/java/io/perfume/api/user/application/service/UpdateProfilePicService.java b/perfume-api/src/main/java/io/perfume/api/user/application/service/UpdateProfilePicService.java index 5ce80925..c7dcd5b1 100644 --- a/perfume-api/src/main/java/io/perfume/api/user/application/service/UpdateProfilePicService.java +++ b/perfume-api/src/main/java/io/perfume/api/user/application/service/UpdateProfilePicService.java @@ -1,7 +1,7 @@ package io.perfume.api.user.application.service; import io.perfume.api.file.application.port.in.dto.FileResult; -import io.perfume.api.file.application.service.FileService; +import io.perfume.api.file.application.service.UploadFileService; import io.perfume.api.user.application.exception.UserNotFoundException; import io.perfume.api.user.application.port.in.UpdateProfilePicUseCase; import io.perfume.api.user.application.port.in.dto.UpdateProfilePicResult; @@ -11,6 +11,7 @@ import java.time.LocalDateTime; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; @Service @Transactional @@ -18,12 +19,12 @@ public class UpdateProfilePicService implements UpdateProfilePicUseCase { private final UserQueryRepository userQueryRepository; private final UserRepository userRepository; - private final FileService fileUploadService; + private final UploadFileService fileUploadService; public UpdateProfilePicService( UserQueryRepository userQueryRepository, UserRepository userRepository, - FileService fileUploadService) { + UploadFileService fileUploadService) { this.userQueryRepository = userQueryRepository; this.userRepository = userRepository; this.fileUploadService = fileUploadService; @@ -31,8 +32,8 @@ public UpdateProfilePicService( @Override @Transactional - public UpdateProfilePicResult update(Long userId, byte[] imageFileContent, LocalDateTime now) { - FileResult fileResult = fileUploadService.uploadFile(imageFileContent, userId, now); + public UpdateProfilePicResult update(Long userId, MultipartFile file, LocalDateTime now) { + FileResult fileResult = fileUploadService.uploadFile(file, userId, now); User user = userQueryRepository .findUserById(userId) diff --git a/perfume-api/src/main/resources/application-dev.yaml b/perfume-api/src/main/resources/application-dev.yaml index 0d510d01..490937c4 100644 --- a/perfume-api/src/main/resources/application-dev.yaml +++ b/perfume-api/src/main/resources/application-dev.yaml @@ -86,3 +86,10 @@ flyway: validate-on-migrate: true baseline-on-migrate: true baseline-version: 1 + +aws: + s3: + access-key: ${ACCESS_KEY} + private-key: ${PRIVATE_KEY} + bucket: ${BUCKET_NAME} + cloudFrontPath: ${CLOUDFRONT_PATH} diff --git a/perfume-api/src/main/resources/application-local.yaml b/perfume-api/src/main/resources/application-local.yaml index 434d93dd..932d3848 100644 --- a/perfume-api/src/main/resources/application-local.yaml +++ b/perfume-api/src/main/resources/application-local.yaml @@ -69,3 +69,10 @@ encryption: flyway: enabled: false validate-on-migrate: false + +aws: + s3: + access-key: 'test' + private-key: 'test' + bucket: 'read-a-perfume' + cloudFrontPath: 'test' diff --git a/perfume-api/src/main/resources/application-prod.yaml b/perfume-api/src/main/resources/application-prod.yaml index f4dde403..0682d8fc 100644 --- a/perfume-api/src/main/resources/application-prod.yaml +++ b/perfume-api/src/main/resources/application-prod.yaml @@ -48,3 +48,10 @@ flyway: validate-on-migrate: true baseline-on-migrate: true baseline-version: 1 + +aws: + s3: + access-key: 'test' + private-key: 'test' + bucket: 'read-a-perfume' + cloudFrontPath: 'test' diff --git a/perfume-api/src/test/java/io/perfume/api/file/adapter/in/http/FileControllerTest.java b/perfume-api/src/test/java/io/perfume/api/file/adapter/in/http/FileControllerTest.java index c066d897..7e071792 100644 --- a/perfume-api/src/test/java/io/perfume/api/file/adapter/in/http/FileControllerTest.java +++ b/perfume-api/src/test/java/io/perfume/api/file/adapter/in/http/FileControllerTest.java @@ -1,5 +1,7 @@ package io.perfume.api.file.adapter.in.http; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.multipart; @@ -7,14 +9,18 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.restdocs.request.RequestDocumentation.partWithName; import static org.springframework.restdocs.request.RequestDocumentation.requestParts; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.fasterxml.jackson.databind.ObjectMapper; +import io.perfume.api.file.application.port.out.S3Repository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.RestDocumentationExtension; @@ -34,6 +40,8 @@ class FileControllerTest { @Autowired private ObjectMapper objectMapper; + @MockBean private S3Repository s3Repository; + @BeforeEach void setUp( WebApplicationContext webApplicationContext, @@ -51,6 +59,7 @@ void testFileUpload() throws Exception { // given final MockMultipartFile file = new MockMultipartFile("file", "test.txt", "text/plain", "file".getBytes()); + given(s3Repository.uploadFile(any())).willReturn("testurl.com"); // when & then mockMvc @@ -59,6 +68,8 @@ void testFileUpload() throws Exception { .file(file) .characterEncoding("UTF-8") .contentType("multipart/form-data")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.url").value("testurl.com")) .andDo( document( "file-upload", @@ -77,6 +88,7 @@ void testUploadMultipleFiles() throws Exception { new MockMultipartFile("files", "test1.txt", "text/plain", "file1".getBytes()); final MockMultipartFile file2 = new MockMultipartFile("files", "test2.txt", "text/plain", "file2".getBytes()); + given(s3Repository.uploadFile(any())).willReturn("testurl.com"); // when & then mockMvc @@ -86,6 +98,8 @@ void testUploadMultipleFiles() throws Exception { .file(file2) .characterEncoding("UTF-8") .contentType("multipart/form-data")) + .andExpect(status().isOk()) + .andExpect(jsonPath("[0].url").value("testurl.com")) .andDo( document( "files-upload", diff --git a/perfume-api/src/test/java/io/perfume/api/file/application/service/FindFileServiceTest.java b/perfume-api/src/test/java/io/perfume/api/file/application/service/FindUploadFileServiceTest.java similarity index 98% rename from perfume-api/src/test/java/io/perfume/api/file/application/service/FindFileServiceTest.java rename to perfume-api/src/test/java/io/perfume/api/file/application/service/FindUploadFileServiceTest.java index 20c5de39..6ebeadab 100644 --- a/perfume-api/src/test/java/io/perfume/api/file/application/service/FindFileServiceTest.java +++ b/perfume-api/src/test/java/io/perfume/api/file/application/service/FindUploadFileServiceTest.java @@ -18,7 +18,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -class FindFileServiceTest { +class FindUploadFileServiceTest { @InjectMocks private FindFileService findFileService; diff --git a/perfume-api/src/test/resources/application.yaml b/perfume-api/src/test/resources/application.yaml index 3f9c80ff..1577e4c5 100644 --- a/perfume-api/src/test/resources/application.yaml +++ b/perfume-api/src/test/resources/application.yaml @@ -43,3 +43,10 @@ jwt: encryption: aes: secret-key: 'readaperfumesecretkeyhelloletsma' + +aws: + s3: + access-key: 'test' + private-key: 'test' + bucket: 'read-a-perfume' + cloudFrontPath: 'test'