diff --git a/src/docs/asciidoc/form.adoc b/src/docs/asciidoc/form.adoc index d74e62e1..cde392d8 100644 --- a/src/docs/asciidoc/form.adoc +++ b/src/docs/asciidoc/form.adoc @@ -37,9 +37,6 @@ include::{snippets}/form-controller-test/원서를_제출할_때_잘못된_형 ===== Request Header include::{snippets}/form-controller-test/원서를_최종_제출한다/request-headers.adoc[] -===== Request Body -include::{snippets}/form-controller-test/원서를_최종_제출한다/request-fields.adoc[] - ==== 요청 include::{snippets}/form-controller-test/원서를_최종_제출한다/http-request.adoc[] @@ -264,9 +261,6 @@ include::{snippets}/form-controller-test/원서를_수정할_때_반려된_원 ===== Request Header include::{snippets}/form-controller-test/증명_사진을_업로드한다/request-headers.adoc[] -===== Request Part -include::{snippets}/form-controller-test/증명_사진을_업로드한다/request-parts.adoc[] - ==== 요청 include::{snippets}/form-controller-test/증명_사진을_업로드한다/http-request.adoc[] @@ -302,9 +296,6 @@ include::{snippets}/form-controller-test/증명_사진을_업로드할_때_콘 ===== Request Header include::{snippets}/form-controller-test/원서_서류를_업로드한다/request-headers.adoc[] -===== Request Part -include::{snippets}/form-controller-test/원서_서류를_업로드한다/request-parts.adoc[] - ==== 요청 include::{snippets}/form-controller-test/원서_서류를_업로드한다/http-request.adoc[] diff --git a/src/docs/asciidoc/notice.adoc b/src/docs/asciidoc/notice.adoc index 11871514..1751df4a 100644 --- a/src/docs/asciidoc/notice.adoc +++ b/src/docs/asciidoc/notice.adoc @@ -8,8 +8,11 @@ ===== Request Header include::{snippets}/notice-controller-test/공지사항_파일을_업로드한다/request-headers.adoc[] -===== Request Body -include::{snippets}/notice-controller-test/공지사항_파일을_업로드한다/request-parts.adoc[] +==== Request Body +include::{snippets}/notice-controller-test/공지사항_파일을_업로드한다/request-fields.adoc[] + +==== 요청 +include::{snippets}/notice-controller-test/공지사항_파일을_업로드한다/http-request.adoc[] ==== 응답 diff --git a/src/docs/asciidoc/overview.adoc b/src/docs/asciidoc/overview.adoc index f2f99e65..bb131f06 100644 --- a/src/docs/asciidoc/overview.adoc +++ b/src/docs/asciidoc/overview.adoc @@ -16,7 +16,7 @@ |환경 |DNS |비고 |local | link:[http://localhost:8080/] |API 문서 제공 |dev | link:[https://api.dev.maru.bamdoliro.com] |API 문서 제공 -|prod | link:[] |API 문서 미제공 +|prod | link:[https://maru-base.com] |API 문서 미제공 |==== === 응답형식 diff --git a/src/docs/asciidoc/shared.adoc b/src/docs/asciidoc/shared.adoc index a9f0fdca..500d5fc7 100644 --- a/src/docs/asciidoc/shared.adoc +++ b/src/docs/asciidoc/shared.adoc @@ -34,4 +34,282 @@ include::{snippets}/jwt-controller-test/권한이_없다면_에러가_발생한 include::{snippets}/jwt-controller-test/토큰을_전달하지_않으면_인증에_실패한다/http-response.adoc[] ===== 토큰에 대응하는 유저가 없는 경우 -include::{snippets}/jwt-controller-test/유저가_없다면_에러가_발생한다/http-response.adoc[] \ No newline at end of file +include::{snippets}/jwt-controller-test/유저가_없다면_에러가_발생한다/http-response.adoc[] + + +=== presigned URL 업로드 +증명사진, 서류, 공지사항 파일 등을 업로드할 때 공통적으로 다음 형식을 따라야 합니다. +업로드 presigned URL의 토큰은 최대 3분 동안 유효합니다. + +==== 요청 형식 + +===== Request Header +|=== +|Name|Description + +|`+X-Amz-Algorithm+` +|서명을 계산하는데 사용되는 알고리즘 + +|`+X-Amz-Date+` +|날짜 (ISO 8601) + +|`+X-Amz-SignedHeaders+` +|서명을 계산하는데 사용되는 헤더 목록 + +|`+X-Amz-Expires+` +|presigned URL이 유효한 시간 주기. 초단위. 정수 값. 최소 1 에서 최대 604800 (7일) + +|`+X-Amz-Credential+` +|액세스 키 ID, qjadnl wjdqh + +|`+X-Amz-Signature+` +|요청을 인증하기 위한 서명 + +|=== + +===== Request Part +multipart/form-data 가 아닌 binary 형식으로 전송해야 합니다. + +==== 요청 +[source,httprequest,options="nowrap"] +---- +PUT https://s3.ap-northeast-2.amazonaws.com/maru-s3-bucket/identification-picture/b059a4aa-bc50-4e97-809e-e48f19c6023a?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240809T173821Z&X-Amz-SignedHeaders=host&X-Amz-Expires=59&X-Amz-Credential=AKIATCKAQBAH6AYPJEFO%2F20240809%2Fap-northeast-2%2Fs3%2Faws4_request&X-Amz-Signature=3a0ee6d0bb47718083f30783bda69f39920a3f8c5888afc60130fdc51259585c + +---- + +==== 응답 + +===== 정상 응답 +[source,http,options="nowrap"] +---- +HTTP/1.1 200 OK +x-amz-id-2: INFGzaG7eZ75x0fsDNoMo8l9VGMEtZqfrT0Y8brKcyIANKjGJh6hGxkhdw+MiDPsXsiK61vjfxRMm+pIpmSUXuef6e8TFdBc +x-amz-request-id: RJE458Q81A5QR49D +Date: Fri, 09 Aug 2024 17:43:20 GMT +x-amz-version-id: g0aKBdMZWCPYmCzIZnRS_x8en5ojLzqp +x-amz-server-side-encryption: AES256 +ETag: "d41d8cd98f00b204e9800998ecf8427e" +Server: AmazonS3 +Content-Length: 0 + + + +---- + +===== 토큰이 만료된 경우 +[source,http,options="nowrap"] +---- +HTTP/1.1 403 Forbidden +x-amz-request-id: Q5V13J8REGKZF5Q5 +x-amz-id-2: Cfrpnxtd3yfYzJPkZt//xEyJOIBUtN2A/x5PJtzFbXf+Jf+/B907FRHqgpRvzgL/omDKL8ediYn5zeUXQDqfVbR4pUx2bt4I +Content-Type: application/xml +Transfer-Encoding: chunked +Date: Fri, 09 Aug 2024 17:44:00 GMT +Server: AmazonS3 + + + + AccessDenied + Request has expired + 59 + 2024-08-09T17:43:55Z + 2024-08-09T17:44:00Z + Q5V13J8REGKZF5Q5 + Cfrpnxtd3yfYzJPkZt//xEyJOIBUtN2A/x5PJtzFbXf+Jf+/B907FRHqgpRvzgL/omDKL8ediYn5zeUXQDqfVbR4pUx2bt4I + +---- + +===== 계산된 서명값이 일치하지 않는 경우 +- HTTP 메서드가 잘못된 경우 +- 버킷 이름과 객체 키 이름이 올바르지 않은 경우 +- HTTP 헤더가 잘못된 경우 +[source,http,options="nowrap"] +---- +HTTP/1.1 403 Forbidden +x-amz-request-id: TQ5M8JP1MGQQA16S +x-amz-id-2: OHEMIv+uJn9WqNSwktOlci0ik/6NFXGGBE4qiquAqh0Bu4YLbz5oto7yGyZqBjXQg0CGksG27Qs= +Content-Type: application/xml +Transfer-Encoding: chunked +Date: Fri, 09 Aug 2024 17:48:31 GMT +Server: AmazonS3 + + + + SignatureDoesNotMatch + The request signature we calculated does not match the signature you provided. Check your key and signing + method. + + AKIATCKAQBAH6AYPJEFO + AWS4-HMAC-SHA256 + 20240809T173821Z + 20240809/ap-northeast-2/s3/aws4_request + bd9c66ffe13a1209251eed603e3dbe7b10a96d6211da64205d884a79a1cd2929 + + 431972495f78e9d2616eed3a9c329f426a111a3385a50486bde24a60ad6a0461 + 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 34 30 38 30 39 54 31 37 33 38 32 31 + 5a 0a 32 30 32 34 30 38 30 39 2f 61 70 2d 6e 6f 72 74 68 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 + 75 65 73 74 0a 62 64 39 63 36 36 66 66 65 31 33 61 31 32 30 39 32 35 31 65 65 64 36 30 33 65 33 64 62 65 37 62 + 31 30 61 39 36 64 36 32 31 31 64 61 36 34 32 30 35 64 38 38 34 61 37 39 61 31 63 64 32 39 32 39 + + PUT + /maru-s3-bucket/identification-picture/b059a4aa-bc50-4e97-809e-e48f19c6023a + X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIATCKAQBAH6AYPJEFO%2F20240809%2Fap-northeast-2%2Fs3%2Faws4_request&X-Amz-Date=20240809T173821Z&X-Amz-Expires=36000&X-Amz-SignedHeaders=host + host:s3.ap-northeast-2.amazonaws.com + + host + UNSIGNED-PAYLOAD + + 50 55 54 0a 2f 6d 61 72 75 2d 73 33 2d 62 75 63 6b 65 74 2f 69 64 65 6e 74 69 66 69 63 61 74 + 69 6f 6e 2d 70 69 63 74 75 72 65 2f 62 30 35 39 61 34 61 61 2d 62 63 35 30 2d 34 65 39 37 2d 38 30 39 65 2d 65 + 34 38 66 31 39 63 36 30 32 33 61 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 + 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 54 43 4b 41 51 42 41 48 + 36 41 59 50 4a 45 46 4f 25 32 46 32 30 32 34 30 38 30 39 25 32 46 61 70 2d 6e 6f 72 74 68 65 61 73 74 2d 32 25 + 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 34 30 38 + 30 39 54 31 37 33 38 32 31 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 33 36 30 30 30 26 58 2d 41 6d 7a 2d + 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 0a 68 6f 73 74 3a 73 33 2e 61 70 2d 6e 6f 72 74 68 65 61 + 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 + 4c 4f 41 44 + + TQ5M8JP1MGQQA16S + OHEMIv+uJn9WqNSwktOlci0ik/6NFXGGBE4qiquAqh0Bu4YLbz5oto7yGyZqBjXQg0CGksG27Qs= + + +---- + +=== presigned URL 다운로드 +증명사진, 서류, 공지사항 파일 등을 다운로드할 때 공통적으로 다음 형식을 따라야 합니다. +다운로드 presigned URL의 토큰은 최대 10시간 동안 유효합니다. + +==== 요청 형식 + +===== Request Header +|=== +|Name|Description + +|`+X-Amz-Algorithm+` +|서명을 계산하는데 사용되는 알고리즘 + +|`+X-Amz-Date+` +|날짜 (ISO 8601) + +|`+X-Amz-SignedHeaders+` +|서명을 계산하는데 사용되는 헤더 목록 + +|`+X-Amz-Expires+` +|presigned URL이 유효한 시간 주기. 초단위. 정수 값. 최소 1 에서 최대 604800 (7일) + +|`+X-Amz-Credential+` +|액세스 키 ID, 범위 정보 + +|`+X-Amz-Signature+` +|요청을 인증하기 위한 서명 + +|=== + +==== 요청 +[source,httprequest,options="nowrap"] +---- +GET https://s3.ap-northeast-2.amazonaws.com/maru-s3-bucket/identification-picture/b059a4aa-bc50-4e97-809e-e48f19c6023a?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240809T173821Z&X-Amz-SignedHeaders=host&X-Amz-Expires=36000&X-Amz-Credential=AKIATCKAQBAH6AYPJEFO%2F20240809%2Fap-northeast-2%2Fs3%2Faws4_request&X-Amz-Signature=431972495f78e9d2616eed3a9c329f426a111a3385a50486bde24a60ad6a0461 + +---- + +==== 응답 + +===== 정상 응답 +[source,http,options="nowrap"] +---- +HTTP/1.1 200 OK +x-amz-id-2: D3WOVRzzhZg5XiVoisJLnjc/VqKAUFZT/T24NrZzW4+4tKc2aQBsyy6bj5n/HbKwxniFQho/mh0= +x-amz-request-id: VWP1KGSZKFH0ADKZ +Date: Fri, 09 Aug 2024 17:39:48 GMT +Last-Modified: Fri, 09 Aug 2024 17:38:29 GMT +ETag: "3449755b8cc5fd62448743f7541e4987" +x-amz-server-side-encryption: AES256 +Cache-Control: no-cache +x-amz-version-id: QU9onplQzpB1S2XrBYKSs0jtl64Ex2Lt +Accept-Ranges: bytes +Content-Type: image/png +Server: AmazonS3 +Content-Length: 25099 + +<파일> +---- +===== 토큰이 만료된 경우 +[source,http,options="nowrap"] +---- +HTTP/1.1 403 Forbidden +x-amz-request-id: Q5V13J8REGKZF5Q5 +x-amz-id-2: Cfrpnxtd3yfYzJPkZt//xEyJOIBUtN2A/x5PJtzFbXf+Jf+/B907FRHqgpRvzgL/omDKL8ediYn5zeUXQDqfVbR4pUx2bt4I +Content-Type: application/xml +Transfer-Encoding: chunked +Date: Fri, 09 Aug 2024 17:44:00 GMT +Server: AmazonS3 + + + + AccessDenied + Request has expired + 59 + 2024-08-09T17:43:55Z + 2024-08-09T17:44:00Z + Q5V13J8REGKZF5Q5 + Cfrpnxtd3yfYzJPkZt//xEyJOIBUtN2A/x5PJtzFbXf+Jf+/B907FRHqgpRvzgL/omDKL8ediYn5zeUXQDqfVbR4pUx2bt4I + +---- + +==== 계산된 서명값이 일치하지 않는 경우 +- HTTP 메서드가 잘못된 경우 +- 버킷 이름과 객체 키 이름이 올바르지 않은 경우 +- HTTP 헤더가 잘못된 경우 +[source,http,options="nowrap"] +---- +HTTP/1.1 403 Forbidden +x-amz-request-id: TQ5M8JP1MGQQA16S +x-amz-id-2: OHEMIv+uJn9WqNSwktOlci0ik/6NFXGGBE4qiquAqh0Bu4YLbz5oto7yGyZqBjXQg0CGksG27Qs= +Content-Type: application/xml +Transfer-Encoding: chunked +Date: Fri, 09 Aug 2024 17:48:31 GMT +Server: AmazonS3 + + + + SignatureDoesNotMatch + The request signature we calculated does not match the signature you provided. Check your key and signing + method. + + AKIATCKAQBAH6AYPJEFO + AWS4-HMAC-SHA256 + 20240809T173821Z + 20240809/ap-northeast-2/s3/aws4_request + bd9c66ffe13a1209251eed603e3dbe7b10a96d6211da64205d884a79a1cd2929 + + 431972495f78e9d2616eed3a9c329f426a111a3385a50486bde24a60ad6a0461 + 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 34 30 38 30 39 54 31 37 33 38 32 31 + 5a 0a 32 30 32 34 30 38 30 39 2f 61 70 2d 6e 6f 72 74 68 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 + 75 65 73 74 0a 62 64 39 63 36 36 66 66 65 31 33 61 31 32 30 39 32 35 31 65 65 64 36 30 33 65 33 64 62 65 37 62 + 31 30 61 39 36 64 36 32 31 31 64 61 36 34 32 30 35 64 38 38 34 61 37 39 61 31 63 64 32 39 32 39 + + PUT + /maru-s3-bucket/identification-picture/b059a4aa-bc50-4e97-809e-e48f19c6023a + X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIATCKAQBAH6AYPJEFO%2F20240809%2Fap-northeast-2%2Fs3%2Faws4_request&X-Amz-Date=20240809T173821Z&X-Amz-Expires=36000&X-Amz-SignedHeaders=host + host:s3.ap-northeast-2.amazonaws.com + + host + UNSIGNED-PAYLOAD + + 50 55 54 0a 2f 6d 61 72 75 2d 73 33 2d 62 75 63 6b 65 74 2f 69 64 65 6e 74 69 66 69 63 61 74 + 69 6f 6e 2d 70 69 63 74 75 72 65 2f 62 30 35 39 61 34 61 61 2d 62 63 35 30 2d 34 65 39 37 2d 38 30 39 65 2d 65 + 34 38 66 31 39 63 36 30 32 33 61 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 + 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 54 43 4b 41 51 42 41 48 + 36 41 59 50 4a 45 46 4f 25 32 46 32 30 32 34 30 38 30 39 25 32 46 61 70 2d 6e 6f 72 74 68 65 61 73 74 2d 32 25 + 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 34 30 38 + 30 39 54 31 37 33 38 32 31 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 33 36 30 30 30 26 58 2d 41 6d 7a 2d + 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 0a 68 6f 73 74 3a 73 33 2e 61 70 2d 6e 6f 72 74 68 65 61 + 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 + 4c 4f 41 44 + + TQ5M8JP1MGQQA16S + OHEMIv+uJn9WqNSwktOlci0ik/6NFXGGBE4qiquAqh0Bu4YLbz5oto7yGyZqBjXQg0CGksG27Qs= + + +---- \ No newline at end of file diff --git a/src/main/java/com/bamdoliro/maru/application/form/ExportFormUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/ExportFormUseCase.java index 1572cb84..8cc163cf 100644 --- a/src/main/java/com/bamdoliro/maru/application/form/ExportFormUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/form/ExportFormUseCase.java @@ -9,6 +9,8 @@ import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.infrastructure.pdf.GeneratePdfService; import com.bamdoliro.maru.infrastructure.pdf.MergePdfService; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.infrastructure.s3.constants.FolderConstant; import com.bamdoliro.maru.infrastructure.thymeleaf.ProcessTemplateService; import com.bamdoliro.maru.infrastructure.thymeleaf.Templates; import com.bamdoliro.maru.shared.annotation.UseCase; @@ -37,10 +39,10 @@ public class ExportFormUseCase { private final ProcessTemplateService processTemplateService; private final GeneratePdfService generatePdfService; private final MergePdfService mergePdfService; + private final FileService fileService; public ByteArrayResource execute(User user) { Form form = formFacade.getForm(user); - validateFormStatus(form); SubjectMap subjectMap = form.getGrade().getSubjectList().getSubjectMap(); Map formMap = Map.of( @@ -49,7 +51,8 @@ public ByteArrayResource execute(User user) { "grade22", subjectMap.getSubjectListOf(2, 2), "grade31", subjectMap.getSubjectListOf(3, 1), "subjectList", getSubjectList(form), - "year", Schedule.getAdmissionYear() + "year", Schedule.getAdmissionYear(), + "identificationPictureUri", fileService.getPresignedUrl(FolderConstant.IDENTIFICATION_PICTURE, user.getUuid().toString()).getDownloadUrl() ); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PdfDocument mergedDocument = new PdfDocument(new PdfWriter(outputStream)); @@ -94,12 +97,6 @@ private List getSubjectList(Form form) { return value; } - private void validateFormStatus(Form form) { - if (!(form.isSubmitted() || form.isRejected())) { - throw new FormAlreadySubmittedException(); - } - } - private List getRequiredTemplates(Form form) { if (form.getType().isRegular()) { return List.of( diff --git a/src/main/java/com/bamdoliro/maru/application/form/GenerateAdmissionTicketUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/GenerateAdmissionTicketUseCase.java index 9022082d..cf1dc02c 100644 --- a/src/main/java/com/bamdoliro/maru/application/form/GenerateAdmissionTicketUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/form/GenerateAdmissionTicketUseCase.java @@ -5,6 +5,8 @@ import com.bamdoliro.maru.domain.form.service.FormFacade; import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.infrastructure.pdf.GeneratePdfService; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.infrastructure.s3.constants.FolderConstant; import com.bamdoliro.maru.infrastructure.thymeleaf.ProcessTemplateService; import com.bamdoliro.maru.infrastructure.thymeleaf.Templates; import com.bamdoliro.maru.shared.annotation.UseCase; @@ -15,10 +17,7 @@ import java.io.ByteArrayOutputStream; import java.util.Map; -import static com.bamdoliro.maru.shared.constants.Schedule.CODING_TEST; -import static com.bamdoliro.maru.shared.constants.Schedule.DEPTH_INTERVIEW; -import static com.bamdoliro.maru.shared.constants.Schedule.NCS; -import static com.bamdoliro.maru.shared.constants.Schedule.PHYSICAL_EXAMINATION; +import static com.bamdoliro.maru.shared.constants.Schedule.*; @RequiredArgsConstructor @UseCase @@ -27,6 +26,7 @@ public class GenerateAdmissionTicketUseCase { private final FormFacade formFacade; private final ProcessTemplateService processTemplateService; private final GeneratePdfService generatePdfService; + private final FileService fileService; public ByteArrayResource execute(User user) { Form form = formFacade.getForm(user); @@ -38,7 +38,9 @@ public ByteArrayResource execute(User user) { "codingTest", Schedule.toLocaleString(CODING_TEST), "ncs", Schedule.toLocaleString(NCS), "depthInterview", Schedule.toLocaleString(DEPTH_INTERVIEW), - "physicalExamination", Schedule.toLocaleString(PHYSICAL_EXAMINATION) + "physicalExamination", Schedule.toLocaleString(PHYSICAL_EXAMINATION), + "announcementOfSecondPass", Schedule.toLocaleString(ANNOUNCEMENT_OF_SECOND_PASS), + "identificationPictureUri", fileService.getPresignedUrl(FolderConstant.IDENTIFICATION_PICTURE, user.getUuid().toString()).getDownloadUrl() ); String html = processTemplateService.execute(Templates.ADMISSION_TICKET, formMap); ByteArrayOutputStream outputStream = generatePdfService.execute(html); diff --git a/src/main/java/com/bamdoliro/maru/application/form/QueryFormUrlUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/QueryFormUrlUseCase.java index 70b4a99f..4b1b917b 100644 --- a/src/main/java/com/bamdoliro/maru/application/form/QueryFormUrlUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/form/QueryFormUrlUseCase.java @@ -1,6 +1,8 @@ package com.bamdoliro.maru.application.form; import com.bamdoliro.maru.infrastructure.persistence.form.FormRepository; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.infrastructure.s3.constants.FolderConstant; import com.bamdoliro.maru.presentation.form.dto.response.FormUrlResponse; import com.bamdoliro.maru.shared.annotation.UseCase; import lombok.RequiredArgsConstructor; @@ -12,10 +14,14 @@ public class QueryFormUrlUseCase { private final FormRepository formRepository; + private final FileService fileService; public List execute(List formIdList) { return formRepository.findFormUrlByFormIdList(formIdList).stream() - .map(FormUrlResponse::new) - .toList(); + .map(vo -> new FormUrlResponse( + vo.getExaminationNumber(), + vo.getName(), + fileService.getPresignedUrl(FolderConstant.FORM, vo.getUuid()).getDownloadUrl() + )).toList(); } } diff --git a/src/main/java/com/bamdoliro/maru/application/form/QueryFormUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/QueryFormUseCase.java index 5c750c6f..7427807e 100644 --- a/src/main/java/com/bamdoliro/maru/application/form/QueryFormUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/form/QueryFormUseCase.java @@ -3,7 +3,8 @@ import com.bamdoliro.maru.domain.form.domain.Form; import com.bamdoliro.maru.domain.form.service.FormFacade; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.domain.user.domain.type.Authority; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.infrastructure.s3.constants.FolderConstant; import com.bamdoliro.maru.presentation.form.dto.response.FormResponse; import com.bamdoliro.maru.shared.annotation.UseCase; import lombok.RequiredArgsConstructor; @@ -13,10 +14,16 @@ public class QueryFormUseCase { private final FormFacade formFacade; + private final FileService fileService; public FormResponse execute(User user, Long id) { Form form = formFacade.getForm(id); form.isApplicantOrAdmin(user); - return new FormResponse(form); + + String uuid = form.getUser().getUuid().toString(); + String identificationPictureUri = fileService.getPresignedUrl(FolderConstant.IDENTIFICATION_PICTURE, uuid).getDownloadUrl(); + String formUrl = fileService.getPresignedUrl(FolderConstant.FORM, uuid).getDownloadUrl(); + + return new FormResponse(form, identificationPictureUri, formUrl); } } diff --git a/src/main/java/com/bamdoliro/maru/application/form/SubmitFinalFormUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/SubmitFinalFormUseCase.java index e2e1b653..e478a6d6 100644 --- a/src/main/java/com/bamdoliro/maru/application/form/SubmitFinalFormUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/form/SubmitFinalFormUseCase.java @@ -4,7 +4,7 @@ import com.bamdoliro.maru.domain.form.exception.FormAlreadySubmittedException; import com.bamdoliro.maru.domain.form.service.FormFacade; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.presentation.form.dto.request.SubmitFinalFormRequest; +import com.bamdoliro.maru.infrastructure.s3.FileService; import com.bamdoliro.maru.shared.annotation.UseCase; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -14,13 +14,14 @@ public class SubmitFinalFormUseCase { private final FormFacade formFacade; + private final FileService fileService; @Transactional - public void execute(User user, SubmitFinalFormRequest request) { + public void execute(User user) { Form form = formFacade.getForm(user); validateFormStatus(form); - form.submit(request.getFormUrl()); + form.submit(); } private void validateFormStatus(Form form) { diff --git a/src/main/java/com/bamdoliro/maru/application/form/UploadFormUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/UploadFormUseCase.java index 73898a53..b3694ffc 100644 --- a/src/main/java/com/bamdoliro/maru/application/form/UploadFormUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/form/UploadFormUseCase.java @@ -1,33 +1,19 @@ package com.bamdoliro.maru.application.form; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.infrastructure.s3.UploadFileService; +import com.bamdoliro.maru.infrastructure.s3.FileService; import com.bamdoliro.maru.infrastructure.s3.constants.FolderConstant; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; -import com.bamdoliro.maru.infrastructure.s3.exception.FileSizeLimitExceededException; -import com.bamdoliro.maru.infrastructure.s3.exception.MediaTypeMismatchException; +import com.bamdoliro.maru.infrastructure.s3.dto.response.UrlResponse; import com.bamdoliro.maru.shared.annotation.UseCase; import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; -import org.springframework.web.multipart.MultipartFile; - -import static com.bamdoliro.maru.shared.constants.FileConstant.MB; @RequiredArgsConstructor @UseCase public class UploadFormUseCase { - private final UploadFileService uploadFileService; - - public UploadResponse execute(User user, MultipartFile formFile) { - return uploadFileService.execute(formFile, FolderConstant.FORM, user.getUuid().toString(), file -> { - if (file.getContentType() != null && !file.getContentType().equals(MediaType.APPLICATION_PDF_VALUE)) { - throw new MediaTypeMismatchException(); - } + private final FileService fileService; - if (file.getSize() > 20 * MB) { - throw new FileSizeLimitExceededException(); - } - }); + public UrlResponse execute(User user) { + return fileService.getPresignedUrl(FolderConstant.FORM, user.getUuid().toString()); } } diff --git a/src/main/java/com/bamdoliro/maru/application/form/UploadIdentificationPictureUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/UploadIdentificationPictureUseCase.java index 3a091a72..f1d9e010 100644 --- a/src/main/java/com/bamdoliro/maru/application/form/UploadIdentificationPictureUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/form/UploadIdentificationPictureUseCase.java @@ -1,51 +1,19 @@ package com.bamdoliro.maru.application.form; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.infrastructure.s3.UploadFileService; +import com.bamdoliro.maru.infrastructure.s3.FileService; import com.bamdoliro.maru.infrastructure.s3.constants.FolderConstant; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; -import com.bamdoliro.maru.infrastructure.s3.exception.FailedToSaveException; -import com.bamdoliro.maru.infrastructure.s3.exception.FileSizeLimitExceededException; -import com.bamdoliro.maru.infrastructure.s3.exception.ImageSizeMismatchException; -import com.bamdoliro.maru.infrastructure.s3.exception.MediaTypeMismatchException; +import com.bamdoliro.maru.infrastructure.s3.dto.response.UrlResponse; import com.bamdoliro.maru.shared.annotation.UseCase; import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; -import org.springframework.web.multipart.MultipartFile; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.IOException; - -import static com.bamdoliro.maru.shared.constants.FileConstant.MB; @RequiredArgsConstructor @UseCase public class UploadIdentificationPictureUseCase { - private final UploadFileService uploadFileService; - - public UploadResponse execute(User user, MultipartFile image) { - return uploadFileService.execute(image, FolderConstant.IDENTIFICATION_PICTURE, user.getUuid().toString(), file -> { - if (file.getContentType() != null && !( - file.getContentType().equals(MediaType.IMAGE_JPEG_VALUE) || - file.getContentType().equals(MediaType.IMAGE_PNG_VALUE) - )) { - throw new MediaTypeMismatchException(); - } - - try { - BufferedImage bufferedImage = ImageIO.read(file.getInputStream()); - if (!(bufferedImage.getWidth() == 117 && bufferedImage.getHeight() == 156)) { - throw new ImageSizeMismatchException(); - } - } catch (IOException e) { - throw new FailedToSaveException(); - } + private final FileService fileService; - if (file.getSize() > 2 * MB) { - throw new FileSizeLimitExceededException(); - } - }); + public UrlResponse execute(User user) { + return fileService.getPresignedUrl(FolderConstant.IDENTIFICATION_PICTURE, user.getUuid().toString()); } } diff --git a/src/main/java/com/bamdoliro/maru/application/notice/CreateNoticeUseCase.java b/src/main/java/com/bamdoliro/maru/application/notice/CreateNoticeUseCase.java index 1945fcc3..f7e01c44 100644 --- a/src/main/java/com/bamdoliro/maru/application/notice/CreateNoticeUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/notice/CreateNoticeUseCase.java @@ -14,8 +14,9 @@ public class CreateNoticeUseCase { private final NoticeRepository noticeRepository; public IdResponse execute(NoticeRequest request) { + String fileName = request.getFileName(); Notice notice = noticeRepository.save( - new Notice(request.getTitle(), request.getContent(), request.getFileUrl()) + new Notice(request.getTitle(), request.getContent(), fileName) ); return new IdResponse(notice); diff --git a/src/main/java/com/bamdoliro/maru/application/notice/QueryNoticeListUseCase.java b/src/main/java/com/bamdoliro/maru/application/notice/QueryNoticeListUseCase.java index 69f5ad1b..760949ac 100644 --- a/src/main/java/com/bamdoliro/maru/application/notice/QueryNoticeListUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/notice/QueryNoticeListUseCase.java @@ -1,7 +1,6 @@ package com.bamdoliro.maru.application.notice; import com.bamdoliro.maru.infrastructure.persistence.notice.NoticeRepository; -import com.bamdoliro.maru.presentation.notice.dto.response.NoticeResponse; import com.bamdoliro.maru.presentation.notice.dto.response.NoticeSimpleResponse; import com.bamdoliro.maru.shared.annotation.UseCase; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/bamdoliro/maru/application/notice/QueryNoticeUseCase.java b/src/main/java/com/bamdoliro/maru/application/notice/QueryNoticeUseCase.java index 7743fff2..00004c2d 100644 --- a/src/main/java/com/bamdoliro/maru/application/notice/QueryNoticeUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/notice/QueryNoticeUseCase.java @@ -1,5 +1,8 @@ package com.bamdoliro.maru.application.notice; +import com.bamdoliro.maru.domain.notice.domain.Notice; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.infrastructure.s3.constants.FolderConstant; import com.bamdoliro.maru.presentation.notice.dto.response.NoticeResponse; import com.bamdoliro.maru.shared.annotation.UseCase; import lombok.RequiredArgsConstructor; @@ -9,10 +12,14 @@ public class QueryNoticeUseCase { private final NoticeFacade noticeFacade; + private final FileService fileService; public NoticeResponse execute(Long id) { - return new NoticeResponse( - noticeFacade.getNotice(id) - ); + Notice notice = noticeFacade.getNotice(id); + String fileUrl = notice.getFileName() != null ? + fileService.getPresignedUrl(FolderConstant.NOTICE_FILE, notice.getFileName()).getDownloadUrl() + : null; + + return new NoticeResponse(notice, fileUrl); } } diff --git a/src/main/java/com/bamdoliro/maru/application/notice/UpdateNoticeUseCase.java b/src/main/java/com/bamdoliro/maru/application/notice/UpdateNoticeUseCase.java index d59cc399..6ea928b7 100644 --- a/src/main/java/com/bamdoliro/maru/application/notice/UpdateNoticeUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/notice/UpdateNoticeUseCase.java @@ -15,6 +15,6 @@ public class UpdateNoticeUseCase { @Transactional public void execute(Long id, NoticeRequest request) { Notice notice = noticeFacade.getNotice(id); - notice.update(request.getTitle(), request.getContent(), request.getFileUrl()); + notice.update(request.getTitle(), request.getContent(), request.getFileName()); } } diff --git a/src/main/java/com/bamdoliro/maru/application/notice/UploadFileUseCase.java b/src/main/java/com/bamdoliro/maru/application/notice/UploadFileUseCase.java index ea3e41bb..5f08bc15 100644 --- a/src/main/java/com/bamdoliro/maru/application/notice/UploadFileUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/notice/UploadFileUseCase.java @@ -1,31 +1,23 @@ package com.bamdoliro.maru.application.notice; -import com.bamdoliro.maru.domain.notice.domain.Notice; -import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.infrastructure.s3.UploadFileService; +import com.bamdoliro.maru.infrastructure.s3.FileService; import com.bamdoliro.maru.infrastructure.s3.constants.FolderConstant; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; -import com.bamdoliro.maru.infrastructure.s3.exception.FileSizeLimitExceededException; -import com.bamdoliro.maru.infrastructure.s3.exception.MediaTypeMismatchException; +import com.bamdoliro.maru.presentation.notice.dto.request.UploadFileRequest; +import com.bamdoliro.maru.presentation.notice.dto.response.UploadFileResponse; import com.bamdoliro.maru.shared.annotation.UseCase; import lombok.RequiredArgsConstructor; -import org.springframework.web.multipart.MultipartFile; import java.util.UUID; -import static com.bamdoliro.maru.shared.constants.FileConstant.MB; - @RequiredArgsConstructor @UseCase public class UploadFileUseCase { - private final UploadFileService uploadFileService; + private final FileService fileService; + + public UploadFileResponse execute(UploadFileRequest request) { + String fileName = UUID.randomUUID() + "_" + request.getFileName(); - public UploadResponse execute(MultipartFile noticeFile) { - return uploadFileService.execute(noticeFile, FolderConstant.NOTICE_FILE, UUID.randomUUID().toString(), file -> { - if (file.getSize() > 20 * MB) { - throw new FileSizeLimitExceededException(); - } - }); + return new UploadFileResponse(fileService.getPresignedUrl(FolderConstant.NOTICE_FILE, fileName), fileName); } -} +} \ No newline at end of file diff --git a/src/main/java/com/bamdoliro/maru/domain/form/domain/Form.java b/src/main/java/com/bamdoliro/maru/domain/form/domain/Form.java index d5841cf6..da6adf0c 100644 --- a/src/main/java/com/bamdoliro/maru/domain/form/domain/Form.java +++ b/src/main/java/com/bamdoliro/maru/domain/form/domain/Form.java @@ -61,9 +61,6 @@ public class Form extends BaseTimeEntity { @Embedded private Score score; - @Column(nullable = true, length = 150) - private String formUrl; - @Enumerated(EnumType.STRING) @Column(nullable = false, length = 30) private FormType type; @@ -92,8 +89,7 @@ public Form(Applicant applicant, Parent parent, Education education, Grade grade this.status = FormStatus.SUBMITTED; } - public void submit(String formUrl) { - this.formUrl = formUrl; + public void submit() { this.status = FormStatus.FINAL_SUBMITTED; } diff --git a/src/main/java/com/bamdoliro/maru/domain/form/domain/value/Applicant.java b/src/main/java/com/bamdoliro/maru/domain/form/domain/value/Applicant.java index cf05d5c1..44a94952 100644 --- a/src/main/java/com/bamdoliro/maru/domain/form/domain/value/Applicant.java +++ b/src/main/java/com/bamdoliro/maru/domain/form/domain/value/Applicant.java @@ -21,9 +21,6 @@ @Embeddable public class Applicant { - @Column(nullable = false, length = 150) - private String identificationPictureUri; - @Column(nullable = false, length = 20) private String name; diff --git a/src/main/java/com/bamdoliro/maru/domain/notice/domain/Notice.java b/src/main/java/com/bamdoliro/maru/domain/notice/domain/Notice.java index b698df53..73f66e9c 100644 --- a/src/main/java/com/bamdoliro/maru/domain/notice/domain/Notice.java +++ b/src/main/java/com/bamdoliro/maru/domain/notice/domain/Notice.java @@ -29,19 +29,19 @@ public class Notice extends BaseTimeEntity { @Column(nullable = false, length = 1024) private String content; - @Column(nullable = true, length = 150) - private String fileUrl; + @Column(unique = true, nullable = true) + private String fileName; @Builder - public Notice(String title, String content, String fileUrl) { + public Notice(String title, String content, String fileName) { this.title = title; this.content = content; - this.fileUrl = fileUrl; + this.fileName = fileName; } - public void update(String title, String content, String fileUrl) { + public void update(String title, String content, String fileName) { this.title = title; this.content = content; - this.fileUrl = fileUrl; + this.fileName = fileName; } } diff --git a/src/main/java/com/bamdoliro/maru/infrastructure/persistence/form/FormRepositoryImpl.java b/src/main/java/com/bamdoliro/maru/infrastructure/persistence/form/FormRepositoryImpl.java index 2d86f610..4111aa2e 100644 --- a/src/main/java/com/bamdoliro/maru/infrastructure/persistence/form/FormRepositoryImpl.java +++ b/src/main/java/com/bamdoliro/maru/infrastructure/persistence/form/FormRepositoryImpl.java @@ -233,7 +233,7 @@ public List findFormUrlByFormIdList(List idList) { .select(new QFormUrlVo( form.examinationNumber, form.applicant.name, - form.formUrl + form.user.uuid )) .from(form) .where(form.id.in(idList)) diff --git a/src/main/java/com/bamdoliro/maru/infrastructure/persistence/form/vo/FormUrlVo.java b/src/main/java/com/bamdoliro/maru/infrastructure/persistence/form/vo/FormUrlVo.java index 6d9343b6..0fe323eb 100644 --- a/src/main/java/com/bamdoliro/maru/infrastructure/persistence/form/vo/FormUrlVo.java +++ b/src/main/java/com/bamdoliro/maru/infrastructure/persistence/form/vo/FormUrlVo.java @@ -3,17 +3,19 @@ import com.querydsl.core.annotations.QueryProjection; import lombok.Getter; +import java.util.UUID; + @Getter public class FormUrlVo { private Long examinationNumber; private String name; - private String formUrl; + private String uuid; @QueryProjection - public FormUrlVo(Long examinationNumber, String name, String formUrl) { + public FormUrlVo(Long examinationNumber, String name, UUID uuid) { this.examinationNumber = examinationNumber; this.name = name; - this.formUrl = formUrl; + this.uuid = uuid.toString(); } } diff --git a/src/main/java/com/bamdoliro/maru/infrastructure/s3/FileService.java b/src/main/java/com/bamdoliro/maru/infrastructure/s3/FileService.java new file mode 100644 index 00000000..601e8382 --- /dev/null +++ b/src/main/java/com/bamdoliro/maru/infrastructure/s3/FileService.java @@ -0,0 +1,103 @@ +package com.bamdoliro.maru.infrastructure.s3; + +import com.amazonaws.HttpMethod; +import com.amazonaws.SdkClientException; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.*; +import com.bamdoliro.maru.infrastructure.s3.dto.response.UrlResponse; +import com.bamdoliro.maru.infrastructure.s3.exception.FailedToSaveException; +import com.bamdoliro.maru.infrastructure.s3.validator.FileValidator; +import com.bamdoliro.maru.shared.config.properties.S3Properties; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Date; + +@Service +@RequiredArgsConstructor +public class FileService { + + private final S3Properties s3Properties; + private final AmazonS3Client amazonS3Client; + + @Value("${spring.cloud.aws.s3.bucket}") + private String bucket; + + public UrlResponse execute(MultipartFile file, String folder, String fileName, FileValidator validator) { + validator.validate(file); + String fullFileName = createFileName(folder, fileName); + + try { + PutObjectRequest request = new PutObjectRequest( + s3Properties.getBucket(), + fullFileName, + file.getInputStream(), + getObjectMetadata(file) + ).withCannedAcl(CannedAccessControlList.PublicRead); + + amazonS3Client.putObject(request); + } catch (SdkClientException | IOException e) { + throw new FailedToSaveException(); + } + + return new UrlResponse( + amazonS3Client.getUrl(s3Properties.getBucket(), fullFileName).toString(), + amazonS3Client.getUrl(s3Properties.getBucket(), fullFileName).toString() + ); + } + + public UrlResponse getPresignedUrl(String folder, String fileName) { + String fullFileName = createFileName(folder, fileName); + GeneratePresignedUrlRequest uploadRequest = getGenerateUploadPresignedUrlRequest(bucket, fullFileName); + GeneratePresignedUrlRequest downloadRequest = getGenerateDownloadPresignedUrlRequest(bucket, fullFileName); + + return new UrlResponse( + amazonS3Client.generatePresignedUrl(uploadRequest).toString(), + downloadRequest != null ? amazonS3Client.generatePresignedUrl(downloadRequest).toString() : null + ); + } + + private GeneratePresignedUrlRequest getGenerateUploadPresignedUrlRequest(String bucket, String fileName) { + return new GeneratePresignedUrlRequest(bucket, fileName) + .withMethod(HttpMethod.PUT) + .withExpiration(getPresignedUrlExpiration(3)); + } + + private GeneratePresignedUrlRequest getGenerateDownloadPresignedUrlRequest(String bucket, String fileName) { + try { + amazonS3Client.getObjectMetadata(bucket, fileName); + + return new GeneratePresignedUrlRequest(bucket, fileName) + .withMethod(HttpMethod.GET) + .withExpiration(getPresignedUrlExpiration(60 * 10)); + } catch (AmazonS3Exception e) { + if (e.getStatusCode() == 404) { + return null; + } + throw e; + } + } + + private Date getPresignedUrlExpiration(int duration) { + Date expiration = new Date(); + long expTimeMillis = expiration.getTime(); + expTimeMillis += 1000L * 60 * duration; + expiration.setTime(expTimeMillis); + + return expiration; + } + + private String createFileName(String folder, String fileName) { + return folder + "/" + fileName; + } + + private ObjectMetadata getObjectMetadata(MultipartFile file) { + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(file.getSize()); + objectMetadata.setContentType(file.getContentType()); + return objectMetadata; + } +} \ No newline at end of file diff --git a/src/main/java/com/bamdoliro/maru/infrastructure/s3/UploadFileService.java b/src/main/java/com/bamdoliro/maru/infrastructure/s3/UploadFileService.java deleted file mode 100644 index ffb21e01..00000000 --- a/src/main/java/com/bamdoliro/maru/infrastructure/s3/UploadFileService.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.bamdoliro.maru.infrastructure.s3; - -import com.amazonaws.SdkClientException; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.CannedAccessControlList; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; -import com.bamdoliro.maru.infrastructure.s3.exception.FailedToSaveException; -import com.bamdoliro.maru.infrastructure.s3.validator.FileValidator; -import com.bamdoliro.maru.shared.config.properties.S3Properties; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; - -@Service -@RequiredArgsConstructor -public class UploadFileService { - - private final S3Properties s3Properties; - private final AmazonS3Client amazonS3Client; - - public UploadResponse execute(MultipartFile file, String folder, String fileName, FileValidator validator) { - validator.validate(file); - String fullFileName = createFileName(folder, fileName); - - try { - PutObjectRequest request = new PutObjectRequest( - s3Properties.getBucket(), - fullFileName, - file.getInputStream(), - getObjectMetadata(file) - ).withCannedAcl(CannedAccessControlList.PublicRead); - - amazonS3Client.putObject(request); - } catch (SdkClientException | IOException e) { - throw new FailedToSaveException(); - } - - return new UploadResponse( - amazonS3Client.getUrl(s3Properties.getBucket(), fullFileName).toString() - ); - } - - private String createFileName(String folder, String fileName) { - return folder + "/" + fileName; - } - - private ObjectMetadata getObjectMetadata(MultipartFile file) { - ObjectMetadata objectMetadata = new ObjectMetadata(); - objectMetadata.setContentLength(file.getSize()); - objectMetadata.setContentType(file.getContentType()); - return objectMetadata; - } -} \ No newline at end of file diff --git a/src/main/java/com/bamdoliro/maru/infrastructure/s3/dto/response/UploadResponse.java b/src/main/java/com/bamdoliro/maru/infrastructure/s3/dto/response/UrlResponse.java similarity index 62% rename from src/main/java/com/bamdoliro/maru/infrastructure/s3/dto/response/UploadResponse.java rename to src/main/java/com/bamdoliro/maru/infrastructure/s3/dto/response/UrlResponse.java index d9018fbf..2a8d44cb 100644 --- a/src/main/java/com/bamdoliro/maru/infrastructure/s3/dto/response/UploadResponse.java +++ b/src/main/java/com/bamdoliro/maru/infrastructure/s3/dto/response/UrlResponse.java @@ -5,7 +5,8 @@ @Getter @AllArgsConstructor -public class UploadResponse { +public class UrlResponse { - private String url; + private String uploadUrl; + private String downloadUrl; } diff --git a/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java b/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java index 73b74244..59f5ecf1 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java +++ b/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java @@ -4,9 +4,8 @@ import com.bamdoliro.maru.domain.form.domain.type.FormStatus; import com.bamdoliro.maru.domain.form.domain.type.FormType; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; +import com.bamdoliro.maru.infrastructure.s3.dto.response.UrlResponse; import com.bamdoliro.maru.presentation.form.dto.request.PassOrFailFormListRequest; -import com.bamdoliro.maru.presentation.form.dto.request.SubmitFinalFormRequest; import com.bamdoliro.maru.presentation.form.dto.request.SubmitFormRequest; import com.bamdoliro.maru.presentation.form.dto.request.UpdateFormRequest; import com.bamdoliro.maru.presentation.form.dto.response.FormResponse; @@ -24,6 +23,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -83,10 +83,9 @@ public void submitForm( @ResponseStatus(HttpStatus.NO_CONTENT) @PatchMapping public void submitForm( - @AuthenticationPrincipal(authority = Authority.USER) User user, - @RequestBody @Valid SubmitFinalFormRequest request + @AuthenticationPrincipal(authority = Authority.USER) User user ) { - submitFinalFormUseCase.execute(user, request); + submitFinalFormUseCase.execute(user); } @ResponseStatus(HttpStatus.NO_CONTENT) @@ -155,34 +154,31 @@ public void updateForm( } @ResponseStatus(HttpStatus.CREATED) - @PostMapping( - value = "/identification-picture", - consumes = MediaType.MULTIPART_FORM_DATA_VALUE - ) - public SingleCommonResponse uploadIdentificationPicture( - @AuthenticationPrincipal(authority = Authority.USER) User user, - @RequestPart(value = "image") MultipartFile image + @PostMapping(value = "/identification-picture") + public SingleCommonResponse uploadIdentificationPicture( + @AuthenticationPrincipal(authority = Authority.USER) User user ) { return SingleCommonResponse.ok( - uploadIdentificationPictureUseCase.execute(user, image) + uploadIdentificationPictureUseCase.execute(user) ); } @ResponseStatus(HttpStatus.CREATED) - @PostMapping(value = "/form-document", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public SingleCommonResponse uploadFormDocument( - @AuthenticationPrincipal(authority = Authority.USER) User user, - @RequestPart(value = "file") MultipartFile file + @PostMapping(value = "/form-document") + public SingleCommonResponse uploadFormDocument( + @AuthenticationPrincipal(authority = Authority.USER) User user ) { return SingleCommonResponse.ok( - uploadFormUseCase.execute(user, file) + uploadFormUseCase.execute(user) ); } @GetMapping(value = "/export") public ResponseEntity exportForm( - @AuthenticationPrincipal(authority = Authority.USER) User user + @AuthenticationPrincipal(authority = Authority.USER) User user, + Model model ) { + model.addAttribute("a", "a"); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_PDF) .body(exportFormUseCase.execute(user)); diff --git a/src/main/java/com/bamdoliro/maru/presentation/form/dto/request/ApplicantRequest.java b/src/main/java/com/bamdoliro/maru/presentation/form/dto/request/ApplicantRequest.java index edf2dd48..cf9e9cf0 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/form/dto/request/ApplicantRequest.java +++ b/src/main/java/com/bamdoliro/maru/presentation/form/dto/request/ApplicantRequest.java @@ -19,10 +19,6 @@ @AllArgsConstructor public class ApplicantRequest { - @NotBlank(message = "필수값입니다.") - @URL(message = "올바른 URL 형식이어야 합니다.") - private String identificationPictureUri; - @NotBlank(message = "필수값입니다.") @Size(max = 20, message = "20자 이하여야 합니다.") private String name; @@ -40,7 +36,6 @@ public class ApplicantRequest { public Applicant toValue() { return new Applicant( - identificationPictureUri, name, new PhoneNumber(phoneNumber), birthday, diff --git a/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/ApplicantResponse.java b/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/ApplicantResponse.java index b8a0ca2b..5f85d9d0 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/ApplicantResponse.java +++ b/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/ApplicantResponse.java @@ -2,7 +2,6 @@ import com.bamdoliro.maru.domain.form.domain.type.Gender; import com.bamdoliro.maru.domain.form.domain.value.Applicant; -import com.bamdoliro.maru.domain.form.domain.value.PhoneNumber; import lombok.AllArgsConstructor; import lombok.Getter; @@ -18,8 +17,8 @@ public class ApplicantResponse { private LocalDate birthday; private Gender gender; - public ApplicantResponse(Applicant applicant) { - this.identificationPictureUri = applicant.getIdentificationPictureUri(); + public ApplicantResponse(Applicant applicant, String identificationPictureUri) { + this.identificationPictureUri = identificationPictureUri; this.name = applicant.getName(); this.phoneNumber = applicant.getPhoneNumber().toString(); this.birthday = applicant.getBirthday(); diff --git a/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/FormResponse.java b/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/FormResponse.java index 4393bb74..505654ac 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/FormResponse.java +++ b/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/FormResponse.java @@ -9,7 +9,6 @@ @Getter @AllArgsConstructor public class FormResponse { - private Long id; private Long examinationNumber; private ApplicantResponse applicant; @@ -21,15 +20,15 @@ public class FormResponse { private FormType type; private FormStatus status; - public FormResponse(Form form) { + public FormResponse(Form form, String identificationPictureUri, String formUrl) { this.id = form.getId(); this.examinationNumber = form.getExaminationNumber(); - this.applicant = new ApplicantResponse(form.getApplicant()); + this.applicant = new ApplicantResponse(form.getApplicant(), identificationPictureUri); this.parent = new ParentResponse(form.getParent()); this.education = new EducationResponse(form.getEducation()); this.grade = new GradeResponse(form.getGrade()); this.document = new DocumentResponse(form.getDocument()); - this.formUrl = form.getFormUrl(); + this.formUrl = formUrl; this.type = form.getType(); this.status = form.getStatus(); } diff --git a/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/FormUrlResponse.java b/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/FormUrlResponse.java index e55827ae..f641beac 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/FormUrlResponse.java +++ b/src/main/java/com/bamdoliro/maru/presentation/form/dto/response/FormUrlResponse.java @@ -15,6 +15,6 @@ public class FormUrlResponse { public FormUrlResponse(FormUrlVo vo) { this.examinationNumber = vo.getExaminationNumber(); this.name = vo.getName(); - this.formUrl = vo.getFormUrl(); + this.formUrl = vo.getUuid(); } } diff --git a/src/main/java/com/bamdoliro/maru/presentation/notice/NoticeController.java b/src/main/java/com/bamdoliro/maru/presentation/notice/NoticeController.java index 24e74b11..a7dc6a70 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/notice/NoticeController.java +++ b/src/main/java/com/bamdoliro/maru/presentation/notice/NoticeController.java @@ -2,10 +2,11 @@ import com.bamdoliro.maru.application.notice.*; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; import com.bamdoliro.maru.presentation.notice.dto.request.NoticeRequest; +import com.bamdoliro.maru.presentation.notice.dto.request.UploadFileRequest; import com.bamdoliro.maru.presentation.notice.dto.response.NoticeResponse; import com.bamdoliro.maru.presentation.notice.dto.response.NoticeSimpleResponse; +import com.bamdoliro.maru.presentation.notice.dto.response.UploadFileResponse; import com.bamdoliro.maru.shared.auth.AuthenticationPrincipal; import com.bamdoliro.maru.shared.auth.Authority; import com.bamdoliro.maru.shared.response.CommonResponse; @@ -16,7 +17,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; @RequiredArgsConstructor @RequestMapping("/notice") @@ -40,14 +40,13 @@ public SingleCommonResponse createNotice( createNoticeUseCase.execute(request)); } - @ResponseStatus(HttpStatus.CREATED) @PostMapping("/file") - public SingleCommonResponse uploadFile( + public SingleCommonResponse uploadFile( @AuthenticationPrincipal(authority = Authority.ADMIN) User user, - @RequestPart("file") MultipartFile file + @RequestBody @Valid UploadFileRequest request ) { return SingleCommonResponse.ok( - uploadFileUseCase.execute(file) + uploadFileUseCase.execute(request) ); } diff --git a/src/main/java/com/bamdoliro/maru/presentation/notice/dto/request/NoticeRequest.java b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/request/NoticeRequest.java index 1f55d492..0719621d 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/notice/dto/request/NoticeRequest.java +++ b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/request/NoticeRequest.java @@ -1,11 +1,11 @@ package com.bamdoliro.maru.presentation.notice.dto.request; +import jakarta.annotation.Nullable; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.validator.constraints.URL; @Getter @NoArgsConstructor @@ -20,7 +20,6 @@ public class NoticeRequest { @Size(max = 1024, message = "1024글자 이하여야 합니다.") private String content; - @Size(max = 150, message = "150자 이하여야합니다.") - @URL(message = "올바른 URL 형식이어야 합니다.") - private String fileUrl; + @Nullable + private String fileName; } diff --git a/src/main/java/com/bamdoliro/maru/presentation/notice/dto/request/UploadFileRequest.java b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/request/UploadFileRequest.java new file mode 100644 index 00000000..faf06ea5 --- /dev/null +++ b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/request/UploadFileRequest.java @@ -0,0 +1,15 @@ +package com.bamdoliro.maru.presentation.notice.dto.request; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class UploadFileRequest { + + @NotBlank(message = "필수값입니다.") + private String fileName; +} diff --git a/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/NoticeResponse.java b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/NoticeResponse.java index 15b78209..8da3427d 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/NoticeResponse.java +++ b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/NoticeResponse.java @@ -10,13 +10,17 @@ public class NoticeResponse { private final String title; private final String content; + private final String fileName; private final String fileUrl; private final LocalDateTime createdAt; + private final LocalDateTime updatedAt; - public NoticeResponse(Notice notice) { + public NoticeResponse(Notice notice, String fileUrl) { this.title = notice.getTitle(); this.content = notice.getContent(); - this.fileUrl = notice.getFileUrl(); + this.fileName = notice.getFileName(); + this.fileUrl = fileUrl; this.createdAt = notice.getCreatedAt(); + this.updatedAt = notice.getUpdatedAt(); } } diff --git a/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/NoticeSimpleResponse.java b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/NoticeSimpleResponse.java index 3268a37e..c31a6d70 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/NoticeSimpleResponse.java +++ b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/NoticeSimpleResponse.java @@ -15,6 +15,6 @@ public class NoticeSimpleResponse { public NoticeSimpleResponse(Notice notice) { this.id = notice.getId(); this.title = notice.getTitle(); - this.createdAt = notice.getUpdatedAt(); + this.createdAt = notice.getCreatedAt(); } } diff --git a/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/UploadFileResponse.java b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/UploadFileResponse.java new file mode 100644 index 00000000..5ad7a16f --- /dev/null +++ b/src/main/java/com/bamdoliro/maru/presentation/notice/dto/response/UploadFileResponse.java @@ -0,0 +1,17 @@ +package com.bamdoliro.maru.presentation.notice.dto.response; + +import com.bamdoliro.maru.infrastructure.s3.dto.response.UrlResponse; +import lombok.Getter; + +@Getter +public class UploadFileResponse { + + private final UrlResponse url; + + private final String fileName; + + public UploadFileResponse(UrlResponse url, String fileName) { + this.url = url; + this.fileName = fileName; + } +} diff --git a/src/main/java/com/bamdoliro/maru/shared/constants/Schedule.java b/src/main/java/com/bamdoliro/maru/shared/constants/Schedule.java index 661b66fc..7931e5b3 100644 --- a/src/main/java/com/bamdoliro/maru/shared/constants/Schedule.java +++ b/src/main/java/com/bamdoliro/maru/shared/constants/Schedule.java @@ -2,7 +2,6 @@ import lombok.experimental.UtilityClass; -import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; @@ -10,14 +9,14 @@ @UtilityClass public class Schedule { - public static final LocalDateTime START = LocalDateTime.of(2023, 10, 16, 9, 0); - public static final LocalDateTime END = LocalDateTime.of(2023, 10, 19, 17, 0); - public static final LocalDateTime ANNOUNCEMENT_OF_FIRST_PASS = LocalDateTime.of(2023, 10, 23, 15, 0); - public static final LocalDateTime ANNOUNCEMENT_OF_SECOND_PASS = LocalDateTime.of(2023, 11, 2, 15, 0); - public static final LocalDateTime CODING_TEST = LocalDateTime.of(2023, 10, 27, 9, 30); - public static final LocalDateTime NCS = LocalDateTime.of(2023, 10, 27, 11, 0); - public static final LocalDateTime DEPTH_INTERVIEW = LocalDateTime.of(2023, 10, 27, 13, 0); - public static final LocalDateTime PHYSICAL_EXAMINATION = LocalDateTime.of(2023, 10, 27, 13, 0); + public static final LocalDateTime START = LocalDateTime.of(2024, 10, 14, 9, 0); + public static final LocalDateTime END = LocalDateTime.of(2024, 10, 17, 17, 0); + public static final LocalDateTime ANNOUNCEMENT_OF_FIRST_PASS = LocalDateTime.of(2024, 10, 21, 15, 0); + public static final LocalDateTime ANNOUNCEMENT_OF_SECOND_PASS = LocalDateTime.of(2024, 10, 31, 15, 0); + public static final LocalDateTime CODING_TEST = LocalDateTime.of(2024, 10, 25, 9, 30); + public static final LocalDateTime NCS = LocalDateTime.of(2024, 10, 25, 11, 0); + public static final LocalDateTime DEPTH_INTERVIEW = LocalDateTime.of(2024, 10, 25, 13, 0); + public static final LocalDateTime PHYSICAL_EXAMINATION = LocalDateTime.of(2024, 10, 25, 15, 0); public static final String SELECT_FIRST_PASS_CRON = "0 0 1 20 10 ?"; public static int getAdmissionYear() { diff --git a/src/main/resources/templates/admission-ticket.html b/src/main/resources/templates/admission-ticket.html index d26577d7..f6e703d3 100644 --- a/src/main/resources/templates/admission-ticket.html +++ b/src/main/resources/templates/admission-ticket.html @@ -125,7 +125,7 @@ 증명사진 수험번호 @@ -192,6 +192,11 @@ 검사장 + + 합격발표일 + + 본교 홈페이지 +

※ 코딩테스트는 '마이스터인재전형' 지원자만 실시합니다.

diff --git a/src/main/resources/templates/form.html b/src/main/resources/templates/form.html index dbf21fb8..bdba554e 100644 --- a/src/main/resources/templates/form.html +++ b/src/main/resources/templates/form.html @@ -139,7 +139,7 @@ 증명사진 diff --git a/src/test/java/com/bamdoliro/maru/application/form/ApproveFormUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/ApproveFormUseCaseTest.java index cc233797..69866504 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/ApproveFormUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/ApproveFormUseCaseTest.java @@ -45,7 +45,7 @@ class ApproveFormUseCaseTest { void 원서를_승인할_때_원서가_없으면_에러가_발생한다() { // given Form form = FormFixture.createForm(FormType.REGULAR); - form.submit("https://maru.bamdoliro.com/form.pdf"); + form.submit(); willThrow(new FormNotFoundException()).given(formFacade).getForm(form.getId()); // when and then diff --git a/src/test/java/com/bamdoliro/maru/application/form/ExportFormUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/ExportFormUseCaseTest.java index d38d84d9..ee54ef74 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/ExportFormUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/ExportFormUseCaseTest.java @@ -2,14 +2,15 @@ import com.bamdoliro.maru.domain.form.domain.Form; import com.bamdoliro.maru.domain.form.domain.type.FormType; -import com.bamdoliro.maru.domain.form.exception.FormAlreadySubmittedException; import com.bamdoliro.maru.domain.form.service.FormFacade; import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.infrastructure.pdf.GeneratePdfService; import com.bamdoliro.maru.infrastructure.pdf.MergePdfService; import com.bamdoliro.maru.infrastructure.pdf.exception.FailedToExportPdfException; +import com.bamdoliro.maru.infrastructure.s3.FileService; import com.bamdoliro.maru.infrastructure.thymeleaf.ProcessTemplateService; import com.bamdoliro.maru.shared.fixture.FormFixture; +import com.bamdoliro.maru.shared.fixture.SharedFixture; import com.bamdoliro.maru.shared.fixture.UserFixture; import com.itextpdf.kernel.utils.PdfMerger; import org.junit.jupiter.api.Test; @@ -47,6 +48,9 @@ class ExportFormUseCaseTest { @Mock private MergePdfService mergePdfService; + @Mock + private FileService fileService; + @Test void 일반전형_원서를_pdf로_다운받는다() { // given @@ -55,6 +59,7 @@ class ExportFormUseCaseTest { given(formFacade.getForm(user)).willReturn(form); given(processTemplateService.execute(any(String.class), any())).willReturn("html"); given(generatePdfService.execute(any(String.class))).willReturn(new ByteArrayOutputStream()); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createFormUrlResponse()); willDoNothing().given(mergePdfService).execute(any(PdfMerger.class), any(ByteArrayOutputStream.class)); // when @@ -65,6 +70,7 @@ class ExportFormUseCaseTest { verify(processTemplateService, times(4)).execute(any(String.class), any()); verify(generatePdfService, times(4)).execute(any(String.class)); verify(mergePdfService, times(4)).execute(any(PdfMerger.class), any(ByteArrayOutputStream.class)); + verify(fileService, times(1)).getPresignedUrl(any(String.class), any(String.class)); } @Test @@ -75,6 +81,7 @@ class ExportFormUseCaseTest { given(formFacade.getForm(user)).willReturn(form); given(processTemplateService.execute(any(String.class), any())).willReturn("html"); given(generatePdfService.execute(any(String.class))).willReturn(new ByteArrayOutputStream()); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createFormUrlResponse()); willDoNothing().given(mergePdfService).execute(any(PdfMerger.class), any(ByteArrayOutputStream.class)); // when @@ -85,23 +92,7 @@ class ExportFormUseCaseTest { verify(processTemplateService, times(5)).execute(any(String.class), any()); verify(generatePdfService, times(5)).execute(any(String.class)); verify(mergePdfService, times(5)).execute(any(PdfMerger.class), any(ByteArrayOutputStream.class)); - } - - @Test - void 원서를_pdf로_다운받을_때_이미_제출한_원서라면_에러가_발생한다() { - // given - User user = UserFixture.createUser(); - Form form = FormFixture.createForm(FormType.REGULAR); - form.submit("https://maru.bamdoliro.com/form.pdf"); - given(formFacade.getForm(user)).willReturn(form); - - // when and then - assertThrows(FormAlreadySubmittedException.class, () -> exportFormUseCase.execute(user)); - - verify(formFacade, times(1)).getForm(user); - verify(processTemplateService, never()).execute(any(String.class), any()); - verify(generatePdfService, never()).execute(any(String.class)); - verify(mergePdfService, never()).execute(any(PdfMerger.class), any(ByteArrayOutputStream.class)); + verify(fileService, times(1)).getPresignedUrl(any(String.class), any(String.class)); } @Test @@ -111,6 +102,7 @@ class ExportFormUseCaseTest { Form form = FormFixture.createForm(FormType.REGULAR); given(formFacade.getForm(user)).willReturn(form); given(processTemplateService.execute(any(String.class), any())).willReturn("html"); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createFormUrlResponse()); doThrow(FailedToExportPdfException.class).when(generatePdfService).execute(any(String.class)); // when and then @@ -121,5 +113,6 @@ class ExportFormUseCaseTest { verify(processTemplateService, times(1)).execute(any(String.class), any()); verify(generatePdfService, times(1)).execute(any(String.class)); verify(mergePdfService, never()).execute(any(PdfMerger.class), any(ByteArrayOutputStream.class)); + verify(fileService, times(1)).getPresignedUrl(any(String.class), any(String.class)); } } \ No newline at end of file diff --git a/src/test/java/com/bamdoliro/maru/application/form/ExportResultUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/ExportResultUseCaseTest.java index 31c61ff5..f156903a 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/ExportResultUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/ExportResultUseCaseTest.java @@ -2,7 +2,6 @@ import com.bamdoliro.maru.domain.form.domain.Form; import com.bamdoliro.maru.domain.form.domain.type.FormStatus; -import com.bamdoliro.maru.domain.form.domain.type.FormType; import com.bamdoliro.maru.domain.form.service.AssignExaminationNumberService; import com.bamdoliro.maru.domain.form.service.CalculateFormScoreService; import com.bamdoliro.maru.domain.user.domain.User; @@ -23,8 +22,6 @@ import java.util.List; import java.util.Random; -import static com.bamdoliro.maru.domain.form.domain.type.FormStatus.SUBMITTED; - @Disabled @ActiveProfiles("test") @SpringBootTest @@ -56,7 +53,7 @@ void setUp() { assignExaminationNumberService.execute(form); switch (randomFormStatus()) { - case FINAL_SUBMITTED -> form.submit(""); + case FINAL_SUBMITTED -> form.submit(); case APPROVED -> form.approve(); case REJECTED -> form.reject(); case RECEIVED -> form.receive(); diff --git a/src/test/java/com/bamdoliro/maru/application/form/GenerateAdmissionTicketUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/GenerateAdmissionTicketUseCaseTest.java index 8b0cb7b6..51209ef0 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/GenerateAdmissionTicketUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/GenerateAdmissionTicketUseCaseTest.java @@ -7,8 +7,10 @@ import com.bamdoliro.maru.domain.form.service.FormFacade; import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.infrastructure.pdf.GeneratePdfService; +import com.bamdoliro.maru.infrastructure.s3.FileService; import com.bamdoliro.maru.infrastructure.thymeleaf.ProcessTemplateService; import com.bamdoliro.maru.shared.fixture.FormFixture; +import com.bamdoliro.maru.shared.fixture.SharedFixture; import com.bamdoliro.maru.shared.fixture.UserFixture; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -42,6 +44,9 @@ class GenerateAdmissionTicketUseCaseTest { @Mock private GeneratePdfService generatePdfService; + @Mock + private FileService fileService; + @Test void 수험표를_생성한다() { // given @@ -50,6 +55,7 @@ class GenerateAdmissionTicketUseCaseTest { form.firstPass(); given(formFacade.getForm(user)).willReturn(form); given(processTemplateService.execute(any(String.class), any(Map.class))).willReturn("html"); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createFormUrlResponse()); given(generatePdfService.execute(any(String.class))).willReturn(new ByteArrayOutputStream()); // when @@ -59,6 +65,7 @@ class GenerateAdmissionTicketUseCaseTest { verify(formFacade, times(1)).getForm(user); verify(processTemplateService, times(1)).execute(any(String.class), any(Map.class)); verify(generatePdfService, times(1)).execute(any(String.class)); + verify(fileService, times(1)).getPresignedUrl(any(String.class), any(String.class)); } @Test diff --git a/src/test/java/com/bamdoliro/maru/application/form/QueryFormUrlUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/QueryFormUrlUseCaseTest.java index b8b1a5ae..a5feadb1 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/QueryFormUrlUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/QueryFormUrlUseCaseTest.java @@ -1,8 +1,10 @@ package com.bamdoliro.maru.application.form; import com.bamdoliro.maru.infrastructure.persistence.form.FormRepository; +import com.bamdoliro.maru.infrastructure.s3.FileService; import com.bamdoliro.maru.presentation.form.dto.response.FormUrlResponse; import com.bamdoliro.maru.shared.fixture.FormFixture; +import com.bamdoliro.maru.shared.fixture.SharedFixture; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -12,6 +14,7 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -25,6 +28,9 @@ class QueryFormUrlUseCaseTest { @Mock private FormRepository formRepository; + @Mock + private FileService fileService; + @Test void 원서_url을_조회한다() { // given @@ -34,6 +40,7 @@ class QueryFormUrlUseCaseTest { FormFixture.createFormUrlVo(), FormFixture.createFormUrlVo() )); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createFormUrlResponse()); // when List responseList = queryFormUrlUseCase.execute(idList); diff --git a/src/test/java/com/bamdoliro/maru/application/form/QueryFormUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/QueryFormUseCaseTest.java index e2f14984..147c842a 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/QueryFormUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/QueryFormUseCaseTest.java @@ -6,9 +6,10 @@ import com.bamdoliro.maru.domain.form.exception.FormNotFoundException; import com.bamdoliro.maru.domain.form.service.FormFacade; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.domain.user.service.UserFacade; +import com.bamdoliro.maru.infrastructure.s3.FileService; import com.bamdoliro.maru.presentation.form.dto.response.FormResponse; import com.bamdoliro.maru.shared.fixture.FormFixture; +import com.bamdoliro.maru.shared.fixture.SharedFixture; import com.bamdoliro.maru.shared.fixture.UserFixture; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -18,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.times; @@ -32,6 +34,9 @@ class QueryFormUseCaseTest { @Mock private FormFacade formFacade; + @Mock + private FileService fileService; + @Test void 원서를_조회한다() { // given @@ -41,6 +46,7 @@ class QueryFormUseCaseTest { given(formFacade.getForm(id)).willReturn(form); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createFormUrlResponse()); // when FormResponse response = queryFormUseCase.execute(user, id); @@ -74,7 +80,6 @@ class QueryFormUseCaseTest { Long id = -1L; User user = UserFixture.createUser(); - willThrow(new FormNotFoundException()).given(formFacade).getForm(id); // when and then diff --git a/src/test/java/com/bamdoliro/maru/application/form/RejectFormUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/RejectFormUseCaseTest.java index e2ebe941..e50fc6c9 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/RejectFormUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/RejectFormUseCaseTest.java @@ -45,7 +45,7 @@ class RejectFormUseCaseTest { void 원서를_반려할_때_원서가_없으면_에러가_발생한다() { // given Form form = FormFixture.createForm(FormType.REGULAR); - form.submit("https://maru.bamdoliro.com/form.pdf"); + form.submit(); willThrow(new FormNotFoundException()).given(formFacade).getForm(form.getId()); // when and then diff --git a/src/test/java/com/bamdoliro/maru/application/form/SubmitFinalFormUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/SubmitFinalFormUseCaseTest.java index 6697ad90..d0be22f5 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/SubmitFinalFormUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/SubmitFinalFormUseCaseTest.java @@ -5,7 +5,6 @@ import com.bamdoliro.maru.domain.form.exception.FormAlreadySubmittedException; import com.bamdoliro.maru.domain.form.service.FormFacade; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.presentation.form.dto.request.SubmitFinalFormRequest; import com.bamdoliro.maru.shared.fixture.FormFixture; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -33,15 +32,13 @@ class SubmitFinalFormUseCaseTest { void 원서를_최종_제출한다() { // given Form form = FormFixture.createForm(FormType.REGULAR); - SubmitFinalFormRequest request = new SubmitFinalFormRequest("https://maru.bamdoliro.com/form.pdf"); given(formFacade.getForm(any(User.class))).willReturn(form); // when - submitFinalFormUseCase.execute(form.getUser(), request); + submitFinalFormUseCase.execute(form.getUser()); // then - assertEquals(form.getFormUrl(), request.getFormUrl()); verify(formFacade, times(1)).getForm(any(User.class)); } @@ -50,14 +47,12 @@ class SubmitFinalFormUseCaseTest { // given Form form = FormFixture.createForm(FormType.REGULAR); form.approve(); - SubmitFinalFormRequest request = new SubmitFinalFormRequest("https://maru.bamdoliro.com/form.pdf"); given(formFacade.getForm(any(User.class))).willReturn(form); // when and then - assertThrows(FormAlreadySubmittedException.class, () -> submitFinalFormUseCase.execute(form.getUser(), request)); + assertThrows(FormAlreadySubmittedException.class, () -> submitFinalFormUseCase.execute(form.getUser())); - assertNull(form.getFormUrl()); verify(formFacade, times(1)).getForm(any(User.class)); } } \ No newline at end of file diff --git a/src/test/java/com/bamdoliro/maru/application/form/UploadFormUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/UploadFormUseCaseTest.java index 3bc2f5cf..ae750b9e 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/UploadFormUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/UploadFormUseCaseTest.java @@ -1,23 +1,14 @@ package com.bamdoliro.maru.application.form; -import com.bamdoliro.maru.domain.form.domain.Form; -import com.bamdoliro.maru.domain.form.domain.type.FormType; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.infrastructure.s3.UploadFileService; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; -import com.bamdoliro.maru.infrastructure.s3.validator.FileValidator; -import com.bamdoliro.maru.shared.fixture.FormFixture; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.shared.fixture.SharedFixture; import com.bamdoliro.maru.shared.fixture.UserFixture; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.web.multipart.MultipartFile; - -import java.nio.charset.StandardCharsets; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @@ -31,25 +22,18 @@ class UploadFormUseCaseTest { private UploadFormUseCase uploadFormUseCase; @Mock - private UploadFileService uploadFileService; + private FileService fileService; @Test void 원서_서류를_업로드한다() { // given User user = UserFixture.createUser(); - Form form = FormFixture.createForm(FormType.REGULAR); - MockMultipartFile image = new MockMultipartFile( - "file", - "file.pdf", - MediaType.APPLICATION_PDF_VALUE, - "<>".getBytes(StandardCharsets.UTF_8) - ); - given(uploadFileService.execute(any(MultipartFile.class), any(String.class), any(String.class), any(FileValidator.class))).willReturn(new UploadResponse("https://host.com/image.pdf")); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createFormUrlResponse()); // when - uploadFormUseCase.execute(user, image); + uploadFormUseCase.execute(user); // then - verify(uploadFileService, times(1)).execute(any(MultipartFile.class), any(String.class), any(String.class), any(FileValidator.class)); + verify(fileService, times(1)).getPresignedUrl(any(String.class), any(String.class)); } } \ No newline at end of file diff --git a/src/test/java/com/bamdoliro/maru/application/form/UploadIdentificationPictureUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/UploadIdentificationPictureUseCaseTest.java index 3bdecf9a..0b7cc669 100644 --- a/src/test/java/com/bamdoliro/maru/application/form/UploadIdentificationPictureUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/form/UploadIdentificationPictureUseCaseTest.java @@ -1,29 +1,17 @@ package com.bamdoliro.maru.application.form; -import com.bamdoliro.maru.domain.form.domain.Form; -import com.bamdoliro.maru.domain.form.domain.type.FormType; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.infrastructure.s3.UploadFileService; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; -import com.bamdoliro.maru.infrastructure.s3.exception.ImageSizeMismatchException; -import com.bamdoliro.maru.infrastructure.s3.validator.FileValidator; -import com.bamdoliro.maru.shared.fixture.FormFixture; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.shared.fixture.SharedFixture; import com.bamdoliro.maru.shared.fixture.UserFixture; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.web.multipart.MultipartFile; -import java.nio.charset.StandardCharsets; - -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -34,47 +22,18 @@ class UploadIdentificationPictureUseCaseTest { private UploadIdentificationPictureUseCase uploadIdentificationPictureUseCase; @Mock - private UploadFileService uploadFileService; + private FileService fileService; @Test void 증명_사진을_업로드한다() { // given User user = UserFixture.createUser(); - Form form = FormFixture.createForm(FormType.MEISTER_TALENT); - String fileName = "identification-picture.png"; - MockMultipartFile image = new MockMultipartFile( - "image", - fileName, - MediaType.IMAGE_PNG_VALUE, - "<>".getBytes(StandardCharsets.UTF_8) - ); - given(uploadFileService.execute(any(MultipartFile.class), any(String.class), any(String.class), any(FileValidator.class))).willReturn(new UploadResponse("https://host.com/image.png")); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createIdentificationPictureUrlResponse()); // when - uploadIdentificationPictureUseCase.execute(user, image); + uploadIdentificationPictureUseCase.execute(user); // then - verify(uploadFileService, times(1)).execute(any(MultipartFile.class), any(String.class), any(String.class), any(FileValidator.class)); - } - - @Test - void 증명_사진의_크기가_크면_에러가_발생한다() { - // given - User user = UserFixture.createUser(); - Form form = FormFixture.createForm(FormType.MEISTER_TALENT); - String fileName = "identification-picture-big.png"; - MockMultipartFile image = new MockMultipartFile( - "image", - fileName, - MediaType.IMAGE_PNG_VALUE, - "<>".getBytes(StandardCharsets.UTF_8) - ); - willThrow(ImageSizeMismatchException.class).given(uploadFileService).execute(any(MultipartFile.class), any(String.class), any(String.class), any(FileValidator.class)); - - // when and then - assertThrows(ImageSizeMismatchException.class, - () -> uploadIdentificationPictureUseCase.execute(user, image)); - - verify(uploadFileService, times(1)).execute(any(MultipartFile.class), any(String.class), any(String.class), any(FileValidator.class)); + verify(fileService, times(1)).getPresignedUrl(any(String.class), any(String.class)); } } \ No newline at end of file diff --git a/src/test/java/com/bamdoliro/maru/application/message/SendMessageUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/message/SendMessageUseCaseTest.java index 281922f4..86fb1dff 100644 --- a/src/test/java/com/bamdoliro/maru/application/message/SendMessageUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/message/SendMessageUseCaseTest.java @@ -43,7 +43,7 @@ public class SendMessageUseCaseTest { //given Form form = FormFixture.createForm(FormType.REGULAR); - form.submit("https://maru.bamdoliro.com/pdf/1"); + form.submit(); given(formRepository.findByStatus(FormStatus.FINAL_SUBMITTED)).willReturn(List.of(form)); SendMessageByStatusRequest request = new SendMessageByStatusRequest("부산소마고 공지사항", "테스트입니다", FormStatus.FINAL_SUBMITTED); diff --git a/src/test/java/com/bamdoliro/maru/application/notice/CreateNoticeUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/notice/CreateNoticeUseCaseTest.java index 10bd5a1d..dd1c5bf4 100644 --- a/src/test/java/com/bamdoliro/maru/application/notice/CreateNoticeUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/notice/CreateNoticeUseCaseTest.java @@ -12,6 +12,8 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.UUID; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @@ -31,7 +33,7 @@ class CreateNoticeUseCaseTest { void 유저가_공지사항을_생성한다() { // given Notice notice = NoticeFixture.createNotice(); - NoticeRequest request = new NoticeRequest(notice.getTitle(), notice.getContent(), null); + NoticeRequest request = new NoticeRequest(notice.getTitle(), notice.getContent(), UUID.randomUUID().toString()); ArgumentCaptor captor = ArgumentCaptor.forClass(Notice.class); given(noticeRepository.save(any(Notice.class))).willReturn(notice); @@ -42,8 +44,9 @@ class CreateNoticeUseCaseTest { // then verify(noticeRepository, times(1)).save(captor.capture()); Notice savedNotice = captor.getValue(); + assertEquals(response.getId(), savedNotice.getId()); assertEquals(request.getTitle(), savedNotice.getTitle()); assertEquals(request.getContent(), savedNotice.getContent()); - assertEquals(response.getId(), savedNotice.getId()); + assertEquals(request.getFileName(), savedNotice.getFileName() != null ? savedNotice.getFileName() : null); } } \ No newline at end of file diff --git a/src/test/java/com/bamdoliro/maru/application/notice/UpdateNoticeUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/notice/UpdateNoticeUseCaseTest.java index 0bc4fd32..e4b07599 100644 --- a/src/test/java/com/bamdoliro/maru/application/notice/UpdateNoticeUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/notice/UpdateNoticeUseCaseTest.java @@ -26,12 +26,11 @@ class UpdateNoticeUseCaseTest { @Mock private NoticeFacade noticeFacade; - @Test void 유저가_공지사항을_수정한다() { //given Notice notice = NoticeFixture.createNotice(); - NoticeRequest request = new NoticeRequest("제목 바뀌나?", "내용도 바뀌나?", null); + NoticeRequest request = new NoticeRequest("제목 바뀌나?", "내용도 바뀌나?", "파일도 바뀌나?.pdf"); given(noticeFacade.getNotice(notice.getId())).willReturn(notice); @@ -42,13 +41,14 @@ class UpdateNoticeUseCaseTest { verify(noticeFacade, times(1)).getNotice(notice.getId()); assertEquals(request.getTitle(), notice.getTitle()); assertEquals(request.getContent(), notice.getContent()); + assertEquals(request.getFileName(), notice.getFileName() != null ? notice.getFileName() : null); } @Test void 유저가_공지사항을_수정할_때_공지사항이_없으면_에러가_발생한다() { // given Notice notice = NoticeFixture.createNotice(); - NoticeRequest request = new NoticeRequest("제목 바뀌나?", "내용도 바뀌나?", null); + NoticeRequest request = new NoticeRequest("제목 바뀌나?", "내용도 바뀌나?", "파일도 바뀌나?.pdf"); given(noticeFacade.getNotice(notice.getId())).willThrow(NoticeNotFoundException.class); @@ -58,5 +58,6 @@ class UpdateNoticeUseCaseTest { verify(noticeFacade, times(1)).getNotice(notice.getId()); assertNotEquals(request.getTitle(), notice.getTitle()); assertNotEquals(request.getContent(), notice.getContent()); + assertNotEquals(request.getFileName(), notice.getFileName() != null ? notice.getFileName() : null); } } diff --git a/src/test/java/com/bamdoliro/maru/application/notice/UploadFileUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/notice/UploadFileUseCaseTest.java index c93587f4..cc3f701b 100644 --- a/src/test/java/com/bamdoliro/maru/application/notice/UploadFileUseCaseTest.java +++ b/src/test/java/com/bamdoliro/maru/application/notice/UploadFileUseCaseTest.java @@ -1,20 +1,13 @@ package com.bamdoliro.maru.application.notice; -import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.infrastructure.s3.UploadFileService; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; -import com.bamdoliro.maru.infrastructure.s3.validator.FileValidator; -import com.bamdoliro.maru.shared.fixture.UserFixture; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.presentation.notice.dto.request.UploadFileRequest; +import com.bamdoliro.maru.shared.fixture.SharedFixture; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.web.multipart.MultipartFile; - -import java.nio.charset.StandardCharsets; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @@ -28,24 +21,18 @@ public class UploadFileUseCaseTest { private UploadFileUseCase uploadFileUseCase; @Mock - private UploadFileService uploadFileService; + private FileService fileService; @Test void 공지사항_파일을_업로드한다() { // given - User user = UserFixture.createAdminUser(); - MockMultipartFile file = new MockMultipartFile( - "file", - "file.pdf", - MediaType.APPLICATION_PDF_VALUE, - "<>".getBytes(StandardCharsets.UTF_8) - ); - given(uploadFileService.execute(any(MultipartFile.class), any(String.class), any(String.class), any(FileValidator.class))).willReturn(new UploadResponse("https://host.com/notice.pdf")); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createNoticeFileUrlResponse()); + UploadFileRequest request = new UploadFileRequest("notice-file.pdf"); // when - uploadFileUseCase.execute(file); + uploadFileUseCase.execute(request); // then - verify(uploadFileService, times(1)).execute(any(MultipartFile.class), any(String.class), any(String.class), any(FileValidator.class)); + verify(fileService, times(1)).getPresignedUrl(any(String.class), any(String.class)); } } diff --git a/src/test/java/com/bamdoliro/maru/domain/form/domain/FormTest.java b/src/test/java/com/bamdoliro/maru/domain/form/domain/FormTest.java index 98e94666..23cb7886 100644 --- a/src/test/java/com/bamdoliro/maru/domain/form/domain/FormTest.java +++ b/src/test/java/com/bamdoliro/maru/domain/form/domain/FormTest.java @@ -15,7 +15,7 @@ void isReceived() { Form form = FormFixture.createForm(FormType.REGULAR); assertFalse(form.isReceived()); - form.submit(""); + form.submit(); assertFalse(form.isReceived()); form.approve(); @@ -48,7 +48,7 @@ void isFirstPassed() { Form form = FormFixture.createForm(FormType.REGULAR); assertNull(form.isFirstPassed()); - form.submit(""); + form.submit(); assertNull(form.isFirstPassed()); form.approve(); @@ -81,7 +81,7 @@ void isPassed() { Form form = FormFixture.createForm(FormType.REGULAR); assertNull(form.isPassed()); - form.submit(""); + form.submit(); assertNull(form.isPassed()); form.approve(); diff --git a/src/test/java/com/bamdoliro/maru/infrastructure/s3/UploadFileServiceTest.java b/src/test/java/com/bamdoliro/maru/infrastructure/s3/FileServiceTest.java similarity index 75% rename from src/test/java/com/bamdoliro/maru/infrastructure/s3/UploadFileServiceTest.java rename to src/test/java/com/bamdoliro/maru/infrastructure/s3/FileServiceTest.java index 169a2bb7..0ddb66ae 100644 --- a/src/test/java/com/bamdoliro/maru/infrastructure/s3/UploadFileServiceTest.java +++ b/src/test/java/com/bamdoliro/maru/infrastructure/s3/FileServiceTest.java @@ -2,8 +2,9 @@ import com.amazonaws.AmazonServiceException; import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; import com.amazonaws.services.s3.model.PutObjectRequest; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; +import com.bamdoliro.maru.infrastructure.s3.dto.response.UrlResponse; import com.bamdoliro.maru.infrastructure.s3.exception.EmptyFileException; import com.bamdoliro.maru.infrastructure.s3.exception.FailedToSaveException; import com.bamdoliro.maru.infrastructure.s3.exception.FileSizeLimitExceededException; @@ -27,8 +28,7 @@ import java.nio.charset.StandardCharsets; import static com.bamdoliro.maru.shared.constants.FileConstant.MB; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; @@ -37,10 +37,10 @@ import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) -class UploadFileServiceTest { +class FileServiceTest { @InjectMocks - private UploadFileService uploadFileService; + private FileService fileService; @Mock private S3Properties s3Properties; @@ -58,14 +58,14 @@ class UploadFileServiceTest { given(amazonS3Client.getUrl(any(String.class), any(String.class))).willReturn(new URL(url)); // when - UploadResponse response = uploadFileService.execute(image, "folder", "form-uuid", file -> { - }); + UrlResponse response = fileService.execute(image, "folder", "form-uuid", file -> {}); // then - assertEquals(url, response.getUrl()); - verify(s3Properties, times(2)).getBucket(); + assertEquals(url, response.getDownloadUrl()); + assertEquals(url, response.getUploadUrl()); + verify(s3Properties, times(3)).getBucket(); verify(amazonS3Client, times(1)).putObject(any(PutObjectRequest.class)); - verify(amazonS3Client, times(1)).getUrl(any(String.class), any(String.class)); + verify(amazonS3Client, times(2)).getUrl(any(String.class), any(String.class)); } @Test @@ -74,7 +74,7 @@ class UploadFileServiceTest { MockMultipartFile image = new MockMultipartFile("image", "image.png", MediaType.IMAGE_PNG_VALUE, "".getBytes(StandardCharsets.UTF_8)); // when and then - assertThrows(EmptyFileException.class, () -> uploadFileService.execute(image, "folder", "form-uuid", file -> { + assertThrows(EmptyFileException.class, () -> fileService.execute(image, "folder", "form-uuid", file -> { })); verify(s3Properties, never()).getBucket(); @@ -90,7 +90,7 @@ class UploadFileServiceTest { willThrow(AmazonServiceException.class).given(amazonS3Client).putObject(any(PutObjectRequest.class)); // when and then - assertThrows(FailedToSaveException.class, () -> uploadFileService.execute(image, "folder", "form-uuid", file -> { + assertThrows(FailedToSaveException.class, () -> fileService.execute(image, "folder", "form-uuid", file -> { })); verify(s3Properties, times(1)).getBucket(); @@ -111,7 +111,7 @@ class UploadFileServiceTest { // when and then assertThrows( FileSizeLimitExceededException.class, - () -> uploadFileService.execute(image, "folder", "form-uuid", file -> { + () -> fileService.execute(image, "folder", "form-uuid", file -> { if (file.getSize() > 2 * MB) { throw new FileSizeLimitExceededException(); } @@ -141,7 +141,7 @@ class UploadFileServiceTest { given(amazonS3Client.getUrl(any(String.class), any(String.class))).willReturn(new URL(url)); // when - UploadResponse response = uploadFileService.execute(image, "folder", "form-uuid", file -> { + UrlResponse response = fileService.execute(image, "folder", "form-uuid", file -> { try { BufferedImage bufferedImage = ImageIO.read(file.getInputStream()); if (!(bufferedImage.getWidth() == 117 && bufferedImage.getHeight() == 156)) { @@ -153,10 +153,11 @@ class UploadFileServiceTest { }); // then - assertEquals(url, response.getUrl()); - verify(s3Properties, times(2)).getBucket(); + assertEquals(url, response.getDownloadUrl()); + assertEquals(url, response.getUploadUrl()); + verify(s3Properties, times(3)).getBucket(); verify(amazonS3Client, times(1)).putObject(any(PutObjectRequest.class)); - verify(amazonS3Client, times(1)).getUrl(any(String.class), any(String.class)); + verify(amazonS3Client, times(2)).getUrl(any(String.class), any(String.class)); } @Test @@ -173,7 +174,7 @@ class UploadFileServiceTest { // when and then assertThrows( ImageSizeMismatchException.class, - () -> uploadFileService.execute(image, "folder", "form-uuid", file -> { + () -> fileService.execute(image, "folder", "form-uuid", file -> { try { BufferedImage bufferedImage = ImageIO.read(file.getInputStream()); if (!(bufferedImage.getWidth() == 117 && bufferedImage.getHeight() == 156)) { @@ -191,6 +192,32 @@ class UploadFileServiceTest { verify(amazonS3Client, never()).getUrl(any(String.class), any(String.class)); } + @Test + void presigned_URL을_생성한다() throws Exception { + // given + String url = "https://bucket.s3.ap-northeast-2.amazonaws.com/random-uuid-image.png"; + given(amazonS3Client.generatePresignedUrl(any(GeneratePresignedUrlRequest.class))).willReturn(new URL(url)); + + // when + UrlResponse response = fileService.getPresignedUrl("folder", "uuid"); + + // then + verify(amazonS3Client, times(2)).generatePresignedUrl(any(GeneratePresignedUrlRequest.class)); + } + + @Test + void presigned_URL이_만료되면_에러가_발생한다() throws Exception { + // given + String url = "https://bucket.s3.ap-northeast-2.amazonaws.com/random-uuid-image.png"; + given(amazonS3Client.generatePresignedUrl(any(GeneratePresignedUrlRequest.class))).willReturn(new URL(url)); + + // when + UrlResponse response = fileService.getPresignedUrl("folder", "uuid"); + + // then + verify(amazonS3Client, times(2)).generatePresignedUrl(any(GeneratePresignedUrlRequest.class)); + } + private FileInputStream getFileStreamWith(String fileName) throws FileNotFoundException { final String path = String.format("src/test/resources/images/%s", fileName); return new FileInputStream(path); diff --git a/src/test/java/com/bamdoliro/maru/presentation/form/DraftFormControllerTest.java b/src/test/java/com/bamdoliro/maru/presentation/form/DraftFormControllerTest.java index 3e8eb2a9..0f8845b5 100644 --- a/src/test/java/com/bamdoliro/maru/presentation/form/DraftFormControllerTest.java +++ b/src/test/java/com/bamdoliro/maru/presentation/form/DraftFormControllerTest.java @@ -1,12 +1,10 @@ package com.bamdoliro.maru.presentation.form; -import com.bamdoliro.maru.domain.form.domain.type.FormType; import com.bamdoliro.maru.domain.form.exception.DraftFormNotFoundException; import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.presentation.form.dto.request.SubmitFormRequest; import com.bamdoliro.maru.shared.fixture.AuthFixture; import com.bamdoliro.maru.shared.fixture.DraftFormFixture; -import com.bamdoliro.maru.shared.fixture.FormFixture; import com.bamdoliro.maru.shared.fixture.UserFixture; import com.bamdoliro.maru.shared.util.RestDocsTestSupport; import org.junit.jupiter.api.Test; @@ -60,10 +58,6 @@ class DraftFormControllerTest extends RestDocsTestSupport { .type(JsonFieldType.STRING) .optional() .description("<>"), - fieldWithPath("applicant.identificationPictureUri") - .type(JsonFieldType.STRING) - .optional() - .description("증명사진 URI"), fieldWithPath("applicant.name") .type(JsonFieldType.STRING) .optional() diff --git a/src/test/java/com/bamdoliro/maru/presentation/form/FormControllerTest.java b/src/test/java/com/bamdoliro/maru/presentation/form/FormControllerTest.java index 6deec119..f7179446 100644 --- a/src/test/java/com/bamdoliro/maru/presentation/form/FormControllerTest.java +++ b/src/test/java/com/bamdoliro/maru/presentation/form/FormControllerTest.java @@ -7,7 +7,6 @@ import com.bamdoliro.maru.domain.form.exception.*; import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.infrastructure.pdf.exception.FailedToExportPdfException; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; import com.bamdoliro.maru.infrastructure.s3.exception.EmptyFileException; import com.bamdoliro.maru.infrastructure.s3.exception.FailedToSaveException; import com.bamdoliro.maru.infrastructure.s3.exception.FileSizeLimitExceededException; @@ -22,6 +21,7 @@ import com.bamdoliro.maru.presentation.form.dto.response.FormSimpleResponse; import com.bamdoliro.maru.shared.fixture.AuthFixture; import com.bamdoliro.maru.shared.fixture.FormFixture; +import com.bamdoliro.maru.shared.fixture.SharedFixture; import com.bamdoliro.maru.shared.fixture.UserFixture; import com.bamdoliro.maru.shared.util.RestDocsTestSupport; import org.junit.jupiter.api.Test; @@ -35,14 +35,12 @@ import java.util.List; -import static com.bamdoliro.maru.shared.constants.FileConstant.MB; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -92,9 +90,6 @@ class FormControllerTest extends RestDocsTestSupport { fieldWithPath("type") .type(JsonFieldType.STRING) .description("<>"), - fieldWithPath("applicant.identificationPictureUri") - .type(JsonFieldType.STRING) - .description("증명사진 URI"), fieldWithPath("applicant.name") .type(JsonFieldType.STRING) .description("지원자 이름"), @@ -298,18 +293,16 @@ class FormControllerTest extends RestDocsTestSupport { @Test void 원서를_최종_제출한다() throws Exception { - SubmitFinalFormRequest request = new SubmitFinalFormRequest("https://maru.bamdoliro.com/form.pdf"); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - willDoNothing().given(submitFinalFormUseCase).execute(any(User.class), any(SubmitFinalFormRequest.class)); + willDoNothing().given(submitFinalFormUseCase).execute(any(User.class)); mockMvc.perform(patch("/form") .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) - .content(toJson(request)) ) .andExpect(status().isNoContent()) @@ -318,15 +311,10 @@ class FormControllerTest extends RestDocsTestSupport { requestHeaders( headerWithName(HttpHeaders.AUTHORIZATION) .description("Bearer token") - ), - requestFields( - fieldWithPath("formUrl") - .type(JsonFieldType.STRING) - .description("원서 pdf 파일의 url") ) )); - verify(submitFinalFormUseCase, times(1)).execute(any(User.class), any(SubmitFinalFormRequest.class)); + verify(submitFinalFormUseCase, times(1)).execute(any(User.class)); } @Test @@ -336,7 +324,7 @@ class FormControllerTest extends RestDocsTestSupport { given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new FormAlreadySubmittedException()).when(submitFinalFormUseCase).execute(any(User.class), any(SubmitFinalFormRequest.class)); + doThrow(new FormAlreadySubmittedException()).when(submitFinalFormUseCase).execute(any(User.class)); mockMvc.perform(patch("/form") .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) @@ -349,7 +337,7 @@ class FormControllerTest extends RestDocsTestSupport { .andDo(restDocs.document()); - verify(submitFinalFormUseCase, times(1)).execute(any(User.class), any(SubmitFinalFormRequest.class)); + verify(submitFinalFormUseCase, times(1)).execute(any(User.class)); } @Test @@ -707,9 +695,6 @@ class FormControllerTest extends RestDocsTestSupport { fieldWithPath("type") .type(JsonFieldType.STRING) .description("<>"), - fieldWithPath("applicant.identificationPictureUri") - .type(JsonFieldType.STRING) - .description("증명사진 URI"), fieldWithPath("applicant.name") .type(JsonFieldType.STRING) .description("지원자 이름"), @@ -920,23 +905,16 @@ class FormControllerTest extends RestDocsTestSupport { @Test void 증명_사진을_업로드한다() throws Exception { - MockMultipartFile image = new MockMultipartFile( - "image", - "image.png", - MediaType.IMAGE_PNG_VALUE, - "<>".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - given(uploadIdentificationPictureUseCase.execute(user, image)).willReturn(new UploadResponse("https://example.com/image.png")); + given(uploadIdentificationPictureUseCase.execute(user)).willReturn(SharedFixture.createIdentificationPictureUrlResponse()); mockMvc.perform(multipart("/form/identification-picture") - .file(image) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isCreated()) @@ -945,175 +923,129 @@ class FormControllerTest extends RestDocsTestSupport { requestHeaders( headerWithName(HttpHeaders.AUTHORIZATION) .description("Bearer token") - ), - requestParts( - partWithName("image") - .description("증명사진 파일, 3*4cm, 117*156px, 최대 2MB") ) )); - verify(uploadIdentificationPictureUseCase, times(1)).execute(user, image); + verify(uploadIdentificationPictureUseCase, times(1)).execute(user); } @Test void 증명_사진_업로드가_실패한다() throws Exception { - MockMultipartFile image = new MockMultipartFile( - "image", - "image.png", - MediaType.IMAGE_PNG_VALUE, - "<>".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new FailedToSaveException()).when(uploadIdentificationPictureUseCase).execute(user, image); + doThrow(new FailedToSaveException()).when(uploadIdentificationPictureUseCase).execute(user); mockMvc.perform(multipart("/form/identification-picture") - .file(image) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isInternalServerError()) .andDo(restDocs.document()); - verify(uploadIdentificationPictureUseCase, times(1)).execute(user, image); + verify(uploadIdentificationPictureUseCase, times(1)).execute(user); } @Test void 증명_사진을_업로드할_때_사진_크기가_다르면_에러가_발생한다() throws Exception { - MockMultipartFile image = new MockMultipartFile( - "image", - "image.png", - MediaType.IMAGE_PNG_VALUE, - "<>".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new ImageSizeMismatchException()).when(uploadIdentificationPictureUseCase).execute(user, image); + doThrow(new ImageSizeMismatchException()).when(uploadIdentificationPictureUseCase).execute(user); mockMvc.perform(multipart("/form/identification-picture") - .file(image) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isBadRequest()) .andDo(restDocs.document()); - verify(uploadIdentificationPictureUseCase, times(1)).execute(user, image); + verify(uploadIdentificationPictureUseCase, times(1)).execute(user); } @Test void 증명_사진을_업로드할_때_파일이_비었으면_에러가_발생한다() throws Exception { - MockMultipartFile image = new MockMultipartFile( - "image", - "image.png", - MediaType.IMAGE_PNG_VALUE, - "".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new EmptyFileException()).when(uploadIdentificationPictureUseCase).execute(user, image); + doThrow(new EmptyFileException()).when(uploadIdentificationPictureUseCase).execute(user); mockMvc.perform(multipart("/form/identification-picture") - .file(image) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isBadRequest()) .andDo(restDocs.document()); - verify(uploadIdentificationPictureUseCase, times(1)).execute(user, image); + verify(uploadIdentificationPictureUseCase, times(1)).execute(user); } @Test void 증명_사진을_업로드할_때_파일이_용량_제한을_넘으면_에러가_발생한다() throws Exception { - MockMultipartFile image = new MockMultipartFile( - "image", - "image.png", - MediaType.IMAGE_PNG_VALUE, - new byte[(int) (20 * MB + 1)] - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new FileSizeLimitExceededException()).when(uploadIdentificationPictureUseCase).execute(user, image); + doThrow(new FileSizeLimitExceededException()).when(uploadIdentificationPictureUseCase).execute(user); mockMvc.perform(multipart("/form/identification-picture") - .file(image) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isBadRequest()) .andDo(restDocs.document()); - verify(uploadIdentificationPictureUseCase, times(1)).execute(user, image); + verify(uploadIdentificationPictureUseCase, times(1)).execute(user); } @Test void 증명_사진을_업로드할_때_콘텐츠_타입이_다르다면_에러가_발생한다() throws Exception { - MockMultipartFile image = new MockMultipartFile( - "image", - "image.pdf", - MediaType.APPLICATION_PDF_VALUE, - "<>".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new MediaTypeMismatchException()).when(uploadIdentificationPictureUseCase).execute(user, image); + doThrow(new MediaTypeMismatchException()).when(uploadIdentificationPictureUseCase).execute(user); mockMvc.perform(multipart("/form/identification-picture") - .file(image) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isUnsupportedMediaType()) .andDo(restDocs.document()); - verify(uploadIdentificationPictureUseCase, times(1)).execute(user, image); + verify(uploadIdentificationPictureUseCase, times(1)).execute(user); } @Test void 원서_서류를_업로드한다() throws Exception { - MockMultipartFile file = new MockMultipartFile( - "file", - "file.pdf", - MediaType.APPLICATION_PDF_VALUE, - "<>".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - given(uploadFormUseCase.execute(user, file)).willReturn(new UploadResponse("https://example.com/file.pdf")); + given(uploadFormUseCase.execute(user)).willReturn(SharedFixture.createFormUrlResponse()); mockMvc.perform(multipart("/form/form-document") - .file(file) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isCreated()) @@ -1122,32 +1054,21 @@ class FormControllerTest extends RestDocsTestSupport { requestHeaders( headerWithName(HttpHeaders.AUTHORIZATION) .description("Bearer token") - ), - requestParts( - partWithName("file") - .description("원서 및 서류 파일, 최대 10MB") ) )); - verify(uploadFormUseCase, times(1)).execute(user, file); + verify(uploadFormUseCase, times(1)).execute(user); } @Test void 원서_서류_업로드가_실패한다() throws Exception { - MockMultipartFile file = new MockMultipartFile( - "file", - "file.pdf", - MediaType.APPLICATION_PDF_VALUE, - "<>".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new FailedToSaveException()).when(uploadFormUseCase).execute(user, file); + doThrow(new FailedToSaveException()).when(uploadFormUseCase).execute(user); mockMvc.perform(multipart("/form/form-document") - .file(file) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) @@ -1157,25 +1078,18 @@ class FormControllerTest extends RestDocsTestSupport { .andDo(restDocs.document()); - verify(uploadFormUseCase, times(1)).execute(user, file); + verify(uploadFormUseCase, times(1)).execute(user); } @Test void 원서_서류를_업로드할_때_파일이_비었으면_에러가_발생한다() throws Exception { - MockMultipartFile file = new MockMultipartFile( - "file", - "file.pdf", - MediaType.APPLICATION_PDF_VALUE, - "<>".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new EmptyFileException()).when(uploadFormUseCase).execute(user, file); + doThrow(new EmptyFileException()).when(uploadFormUseCase).execute(user); mockMvc.perform(multipart("/form/form-document") - .file(file) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) @@ -1185,63 +1099,49 @@ class FormControllerTest extends RestDocsTestSupport { .andDo(restDocs.document()); - verify(uploadFormUseCase, times(1)).execute(user, file); + verify(uploadFormUseCase, times(1)).execute(user); } @Test void 원서_서류를_업로드할_때_파일이_용량_제한을_넘으면_에러가_발생한다() throws Exception { - MockMultipartFile file = new MockMultipartFile( - "file", - "file.pdf", - MediaType.APPLICATION_PDF_VALUE, - "<>".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new FileSizeLimitExceededException()).when(uploadFormUseCase).execute(user, file); + doThrow(new FileSizeLimitExceededException()).when(uploadFormUseCase).execute(user); mockMvc.perform(multipart("/form/form-document") - .file(file) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isBadRequest()) .andDo(restDocs.document()); - verify(uploadFormUseCase, times(1)).execute(user, file); + verify(uploadFormUseCase, times(1)).execute(user); } @Test void 원서_서류를_업로드할_때_콘텐츠_타입이_다르다면_에러가_발생한다() throws Exception { - MockMultipartFile file = new MockMultipartFile( - "file", - "file.pdf", - MediaType.IMAGE_PNG_VALUE, - "<>".getBytes() - ); User user = UserFixture.createUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new MediaTypeMismatchException()).when(uploadFormUseCase).execute(user, file); + doThrow(new MediaTypeMismatchException()).when(uploadFormUseCase).execute(user); mockMvc.perform(multipart("/form/form-document") - .file(file) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isUnsupportedMediaType()) .andDo(restDocs.document()); - verify(uploadFormUseCase, times(1)).execute(user, file); + verify(uploadFormUseCase, times(1)).execute(user); } @Test @@ -1370,6 +1270,9 @@ class FormControllerTest extends RestDocsTestSupport { .optional(), parameterWithName("type") .description("<>") + .optional(), + parameterWithName("sort") + .description("정렬 기준 (최종 점수 오름차순인 경우 total-score-asc, 최종 점수 내림차순인 경우 total-score-desc, null인 경우 수험번호 오름차순 조회)") .optional() ) )); diff --git a/src/test/java/com/bamdoliro/maru/presentation/notice/NoticeControllerTest.java b/src/test/java/com/bamdoliro/maru/presentation/notice/NoticeControllerTest.java index d3b1d539..baf28ab2 100644 --- a/src/test/java/com/bamdoliro/maru/presentation/notice/NoticeControllerTest.java +++ b/src/test/java/com/bamdoliro/maru/presentation/notice/NoticeControllerTest.java @@ -2,12 +2,12 @@ import com.bamdoliro.maru.domain.notice.exception.NoticeNotFoundException; import com.bamdoliro.maru.domain.user.domain.User; -import com.bamdoliro.maru.infrastructure.s3.dto.response.UploadResponse; import com.bamdoliro.maru.infrastructure.s3.exception.FailedToSaveException; -import com.bamdoliro.maru.infrastructure.s3.exception.FileSizeLimitExceededException; import com.bamdoliro.maru.presentation.notice.dto.request.NoticeRequest; +import com.bamdoliro.maru.presentation.notice.dto.request.UploadFileRequest; import com.bamdoliro.maru.presentation.notice.dto.response.NoticeResponse; import com.bamdoliro.maru.presentation.notice.dto.response.NoticeSimpleResponse; +import com.bamdoliro.maru.presentation.notice.dto.response.UploadFileResponse; import com.bamdoliro.maru.shared.fixture.AuthFixture; import com.bamdoliro.maru.shared.fixture.NoticeFixture; import com.bamdoliro.maru.shared.fixture.SharedFixture; @@ -17,13 +17,10 @@ import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.payload.JsonFieldType; -import java.nio.charset.StandardCharsets; import java.util.List; -import static com.bamdoliro.maru.shared.constants.FileConstant.MB; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.*; @@ -39,7 +36,7 @@ class NoticeControllerTest extends RestDocsTestSupport { @Test void 공지사항을_생성한다() throws Exception { - NoticeRequest request = new NoticeRequest("오늘 급식 맛있엇나용?", "토요일인데요", "http://example.com/file.pdf"); + NoticeRequest request = new NoticeRequest("오늘 급식 맛있엇나용?", "토요일인데요", "notice-file.pdf"); given(createNoticeUseCase.execute(any(NoticeRequest.class))).willReturn(SharedFixture.createIdResponse()); User user = UserFixture.createAdminUser(); @@ -67,9 +64,9 @@ class NoticeControllerTest extends RestDocsTestSupport { fieldWithPath("content") .type(JsonFieldType.STRING) .description("1024글자 이내의 내용"), - fieldWithPath("fileUrl") + fieldWithPath("fileName") .type(JsonFieldType.STRING) - .description("파일 URL 150자 이하 / 파일 없을시 생략") + .description("파일 이름 (파일이 없는 경우 null)") ) )); @@ -79,7 +76,7 @@ class NoticeControllerTest extends RestDocsTestSupport { @Test void 공지사항을_수정한다() throws Exception { Long id = 1L; - NoticeRequest request = new NoticeRequest("이거 맞나", "아님 말고...", "http://example.com/file.pdf"); + NoticeRequest request = new NoticeRequest("이거 맞나", "아님 말고...", "notice-file.pdf"); willDoNothing().given(updateNoticeUseCase).execute(id, request); User user = UserFixture.createAdminUser(); @@ -110,9 +107,9 @@ class NoticeControllerTest extends RestDocsTestSupport { fieldWithPath("content") .type(JsonFieldType.STRING) .description("1024글자 이내의 내용"), - fieldWithPath("fileUrl") + fieldWithPath("fileName") .type(JsonFieldType.STRING) - .description("파일 URL 150자 이하 / 파일 없을시 생략") + .description("파일 이름 (파일이 없는 경우 null)") ) )); } @@ -120,7 +117,7 @@ class NoticeControllerTest extends RestDocsTestSupport { @Test void 공지사항을_수정할_때_공지사항이_없으면_에러가_발생한다() throws Exception { Long id = 1L; - NoticeRequest request = new NoticeRequest("이거 맞나", "아님 말고...", null); + NoticeRequest request = new NoticeRequest("이거 맞나", "아님 말고...", "notice-file.pdf"); willThrow(new NoticeNotFoundException()).given(updateNoticeUseCase).execute(eq(1L), any(NoticeRequest.class)); User user = UserFixture.createAdminUser(); @@ -161,7 +158,7 @@ class NoticeControllerTest extends RestDocsTestSupport { @Test void 공지사항을_불러온다() throws Exception { Long id = 1L; - NoticeResponse response = new NoticeResponse(NoticeFixture.createNotice()); + NoticeResponse response = new NoticeResponse(NoticeFixture.createNotice(), "https://maru.bamdoliro.com/notice-file.pdf"); given(queryNoticeUseCase.execute(id)).willReturn(response); mockMvc.perform(get("/notice/{notice-id}", id) @@ -227,88 +224,54 @@ class NoticeControllerTest extends RestDocsTestSupport { @Test void 공지사항_파일을_업로드한다() throws Exception { - MockMultipartFile file = new MockMultipartFile( - "file", - "file.pdf", - MediaType.APPLICATION_PDF_VALUE, - "<>".getBytes(StandardCharsets.UTF_8) - ); + UploadFileRequest request = new UploadFileRequest("공지사항 파일"); User user = UserFixture.createAdminUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - given(uploadFileUseCase.execute(file)).willReturn(new UploadResponse("https://example.com/notice-file.pdf")); + given(uploadFileUseCase.execute(request)).willReturn(new UploadFileResponse( + SharedFixture.createNoticeFileUrlResponse(), + "공지사항 파일" + )); mockMvc.perform(multipart("/notice/file") - .file(file) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(request)) ) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andDo(restDocs.document( requestHeaders( headerWithName(HttpHeaders.AUTHORIZATION) .description("Bearer token") ), - requestParts( - partWithName("file") - .description("모든 파일, 최대 20MB") + requestFields( + fieldWithPath("fileName") + .type(JsonFieldType.STRING) + .description("파일 이름") ) )); } @Test void 공지사항_파일_업로드가_실패한다() throws Exception { - MockMultipartFile file = new MockMultipartFile( - "file", - "file.pdf", - MediaType.APPLICATION_PDF_VALUE, - "<>".getBytes(StandardCharsets.UTF_8) - ); + UploadFileRequest request = new UploadFileRequest("공지사항 파일"); User user = UserFixture.createAdminUser(); given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new FailedToSaveException()).when(uploadFileUseCase).execute(file); + willThrow(new FailedToSaveException()).given(uploadFileUseCase).execute(any(UploadFileRequest.class)); mockMvc.perform(multipart("/notice/file") - .file(file) .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(request)) ) .andExpect(status().isInternalServerError()) .andDo(restDocs.document()); - verify(uploadFileUseCase, times(1)).execute(file); - } - - @Test - void 공지사항_파일을_업로드할_때_파일이_용량_제한을_넘으면_에러가_발생한다() throws Exception { - MockMultipartFile file = new MockMultipartFile( - "file", - "file.pdf", - MediaType.IMAGE_PNG_VALUE, - new byte[(int) (20 * MB + 1)] - ); - User user = UserFixture.createAdminUser(); - - given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); - given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); - doThrow(new FileSizeLimitExceededException()).when(uploadFileUseCase).execute(file); - - mockMvc.perform(multipart("/notice/file") - .file(file) - .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) - .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) - ) - - .andExpect(status().isBadRequest()) - - .andDo(restDocs.document()); - - verify(uploadFileUseCase, times(1)).execute(file); + verify(uploadFileUseCase, times(1)).execute(any(UploadFileRequest.class)); } } \ No newline at end of file diff --git a/src/test/java/com/bamdoliro/maru/shared/fixture/FormFixture.java b/src/test/java/com/bamdoliro/maru/shared/fixture/FormFixture.java index 5c6aebb9..073fc2ad 100644 --- a/src/test/java/com/bamdoliro/maru/shared/fixture/FormFixture.java +++ b/src/test/java/com/bamdoliro/maru/shared/fixture/FormFixture.java @@ -46,6 +46,7 @@ import java.time.LocalDate; import java.util.List; import java.util.Random; +import java.util.UUID; import java.util.stream.Collectors; import static com.bamdoliro.maru.shared.util.RandomUtil.randomNumber; @@ -55,7 +56,6 @@ public class FormFixture { public static Form createForm(FormType type) { Form form = new Form( new Applicant( - "https://maru.com/photo.png", "김밤돌", new PhoneNumber("01012345678"), LocalDate.of(2005, 4, 15), @@ -121,7 +121,6 @@ public static Form createForm(FormType type) { public static Form createMaleForm(FormType type) { Form form = new Form( new Applicant( - "https://maru.com/photo.png", "김밤돌", new PhoneNumber("01012345678"), LocalDate.of(2005, 4, 15), @@ -187,7 +186,6 @@ public static Form createMaleForm(FormType type) { public static Form createRandomForm(User user) { return new Form( new Applicant( - "https://maru.com/photo.png", "김밤돌", new PhoneNumber("01085852525"), LocalDate.of(2005, 4, 15), @@ -265,7 +263,6 @@ public static Form createRandomForm(User user) { public static Form createRandomQualificationExaminationForm(User user) { return new Form( new Applicant( - "https://maru.com/photo.png", "김밤돌", new PhoneNumber("01012345678"), LocalDate.of(2005, 4, 15), @@ -333,7 +330,6 @@ private static FormType randomFormType() { public static Form createQualificationExaminationForm(FormType type) { return new Form( new Applicant( - "https://maru.com/photo.png", "김밤돌", new PhoneNumber("01012345678"), LocalDate.of(2005, 4, 15), @@ -572,7 +568,6 @@ public static FormResponse createFormResponse() { public static ApplicantRequest createApplicantRequest() { return new ApplicantRequest( - "https://maru.com/photo.png", "김밤돌", "01012345678", LocalDate.of(2005, 4, 15), @@ -608,13 +603,16 @@ public static FormUrlVo createFormUrlVo() { return new FormUrlVo( (long) randomNumber(1000, 5000), "김밤돌", - "https://maru.bamdoliro.com/form.pdf" + UUID.randomUUID() ); } public static FormUrlResponse createFormUrlResponse() { + FormUrlVo formUrlVo = createFormUrlVo(); return new FormUrlResponse( - createFormUrlVo() + formUrlVo.getExaminationNumber(), + formUrlVo.getName(), + "https://maru.bamdoliro.com/form.pdf" ); } } \ No newline at end of file diff --git a/src/test/java/com/bamdoliro/maru/shared/fixture/SharedFixture.java b/src/test/java/com/bamdoliro/maru/shared/fixture/SharedFixture.java index eae275f4..120ebccb 100644 --- a/src/test/java/com/bamdoliro/maru/shared/fixture/SharedFixture.java +++ b/src/test/java/com/bamdoliro/maru/shared/fixture/SharedFixture.java @@ -1,5 +1,6 @@ package com.bamdoliro.maru.shared.fixture; +import com.bamdoliro.maru.infrastructure.s3.dto.response.UrlResponse; import com.bamdoliro.maru.shared.response.IdResponse; public class SharedFixture { @@ -7,4 +8,25 @@ public class SharedFixture { public static IdResponse createIdResponse() { return new IdResponse(1L); } + + public static UrlResponse createNoticeFileUrlResponse() { + return new UrlResponse( + "https://maru.bamdoliro.com/notice-file.pdf", + "https://maru.bamdoliro.com/notice-file.pdf" + ); + } + + public static UrlResponse createIdentificationPictureUrlResponse() { + return new UrlResponse( + "https://maru.bamdoliro.com/identification-picture.png", + "https://maru.bamdoliro.com/identification-picture.png" + ); + } + + public static UrlResponse createFormUrlResponse() { + return new UrlResponse( + "https://maru.bamdoliro.com/form.pdf", + "https://maru.bamdoliro.com/form.pdf" + ); + } }