diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index ee9d501..d3a5956 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -28,6 +28,10 @@ jobs: run: echo "${{ secrets.MYSQL_SETTING }}" > /home/runner/work/UniBond-server/UniBond-server/unibond/src/main/resources/application-MYSQL.properties shell: bash + - name: add S3 connection settings + run: echo "${{ secrets.S3_SETTING }}" > /home/runner/work/UniBond-server/UniBond-server/unibond/src/main/resources/application-S3.properties + shell: bash + - name: Permission for gradlew run: chmod +x ./unibond/gradlew shell: bash diff --git a/appspec.yml b/appspec.yml index a44d815..de138ff 100644 --- a/appspec.yml +++ b/appspec.yml @@ -17,4 +17,4 @@ hooks: AfterInstall: - location: scripts/deploy.sh timeout: 60 - runas: ubuntu + runas: root diff --git a/unibond/.gitignore b/unibond/.gitignore index a17f5e4..79c249a 100644 --- a/unibond/.gitignore +++ b/unibond/.gitignore @@ -17,6 +17,7 @@ wrapper ## key files application-MYSQL.properties +application-S3.properties ### STS ### .apt_generated diff --git a/unibond/BOOT-INF/classes/static/docs/experience-community.html b/unibond/BOOT-INF/classes/static/docs/experience-community.html index 86ec029..2cb2cfc 100644 --- a/unibond/BOOT-INF/classes/static/docs/experience-community.html +++ b/unibond/BOOT-INF/classes/static/docs/experience-community.html @@ -454,6 +454,12 @@

Experience Community API

  • Response
  • +
  • Upload Posts on Experience Community + +
  • @@ -480,15 +486,71 @@

    Response

    HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
    -Content-Length: 902
    +Content-Length: 3572
     
     {
       "isSuccess" : true,
       "code" : 1000,
       "message" : "요청에 성공하였습니다.",
       "result" : {
    -    "numberOfElements" : 2,
    +    "numberOfElements" : 9,
         "postPreviewList" : [ {
    +      "createdDate" : "2023-12-27T03:06:30.422445",
    +      "ownerProfileImg" : "https://unibond-img-bucket.s3.ap-northeast-2.amazonaws.com/user/KakaoTalk_20230528_222457241_05.png",
    +      "ownerNick" : "5jizzi",
    +      "disease" : "13번 염색체 장완 21-22 부분의 결손",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
    +      "createdDate" : "2023-12-27T02:59:28.089643",
    +      "ownerProfileImg" : "https://unibond-img-bucket.s3.ap-northeast-2.amazonaws.com/user/KakaoTalk_20230528_222457241_05.png",
    +      "ownerNick" : "5jizzi",
    +      "disease" : "13번 염색체 장완 21-22 부분의 결손",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
    +      "createdDate" : "2023-12-27T02:51:55.763824",
    +      "ownerProfileImg" : "https://unibond-img-bucket.s3.ap-northeast-2.amazonaws.com/user/KakaoTalk_20230528_222457241_05.png",
    +      "ownerNick" : "5jizzi",
    +      "disease" : "13번 염색체 장완 21-22 부분의 결손",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
    +      "createdDate" : "2023-12-26T04:09:10.8631",
    +      "ownerProfileImg" : "http://testimg1",
    +      "ownerNick" : "jisoo2",
    +      "disease" : "5번 염색체 장완의 결손 증후군",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
    +      "createdDate" : "2023-12-26T04:09:05.784257",
    +      "ownerProfileImg" : "http://testimg1",
    +      "ownerNick" : "jisoo2",
    +      "disease" : "5번 염색체 장완의 결손 증후군",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
    +      "createdDate" : "2023-12-26T04:02:22.167476",
    +      "ownerProfileImg" : "http://testimg1",
    +      "ownerNick" : "jisoo2",
    +      "disease" : "5번 염색체 장완의 결손 증후군",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
    +      "createdDate" : "2023-12-26T03:43:15.496574",
    +      "ownerProfileImg" : "http://testimg1",
    +      "ownerNick" : "jisoo2",
    +      "disease" : "5번 염색체 장완의 결손 증후군",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
           "createdDate" : "2023-11-16T14:18:33.129163",
           "ownerProfileImg" : "http://testimg3",
           "ownerNick" : "jiwoon",
    @@ -507,12 +569,263 @@ 

    Response

    } ], "lastPage" : true, "totalPages" : 1, - "totalElements" : 2, + "totalElements" : 9, "size" : 30 } }
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    isSuccess

    Boolean

    성공 여부

    code

    Number

    결과 코드

    message

    String

    결과 메세지

    result

    Object

    결과 데이터

    result.numberOfElements

    Number

    반환된 게시글 수

    result.postPreviewList

    Array

    게시글 리스트

    result.postPreviewList[].createdDate

    String

    게시글 작성 일자

    result.postPreviewList[].ownerProfileImg

    String

    게시글 작성자 프로필 사진

    result.postPreviewList[].ownerNick

    String

    게시글 작성자 닉네임

    result.postPreviewList[].disease

    String

    게시글 작성자의 질병

    result.postPreviewList[].contentPreview

    String

    게시글 미리보기

    result.postPreviewList[].boardType

    String

    게시글 종류

    result.postPreviewList[].isEnd

    Boolean

    미리보기로 제공된 게시글의 내용이 마지막인지 여부

    result.lastPage

    Boolean

    현재 마지막 페이지인지 여부

    result.totalPages

    Number

    총 페이지 수

    result.totalElements

    Number

    총 원소 개수

    result.size

    Number

    현재 페이지 사이즈

    + + + +
    +

    Upload Posts on Experience Community

    +
    +
    +

    경험 기록 게시판에 게시물 업로드하기

    +
    +
    +

    Request

    +
    +
    +
    $ http --multipart POST 'http://localhost:8080/api/v1/community/experience' \
    +    'Authorization:29' \
    +    'postImg'@'test_profile_img.png' \
    +    'request'@'request'
    +
    +
    +
    +
    +
    $ curl 'http://localhost:8080/api/v1/community/experience' -i -X POST \
    +    -H 'Content-Type: multipart/form-data;charset=UTF-8' \
    +    -H 'Authorization: 29' \
    +    -F 'postImg=@test_profile_img.png;type=multipart/form-data' \
    +    -F 'request=@request;type=application/json'
    +
    +
    + ++++ + + + + + + + + + + + + +
    NameDescription

    Authorization

    Basic auth credentials

    + ++++ + + + + + + + + + + + + + + + + +
    PartDescription

    postImg

    업로드 할 게시물 사진 파일

    request

    게시물 업로드 요청

    + +++++ + + + + + + + + + + + + + + +
    PathTypeDescription

    content

    String

    [request.content] 업로드 할 게시물 내용

    +
    +
      +
    • +

      request의 경우 다음과 같은 예시처럼 json 타입으로 전달합니다.

      +
    • +
    +
    +
    +
    +
    {
    +  "content" : "게시물 업로드 테스트 입니다."
    +}
    +
    +
    +
    +
    +

    Response

    +
    +
    +
    HTTP/1.1 200 OK
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 95
    +
    +{
    +  "isSuccess" : true,
    +  "code" : 1000,
    +  "message" : "요청에 성공하였습니다."
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    isSuccess

    Boolean

    성공 여부

    code

    Number

    결과 코드

    message

    String

    결과 메세지

    @@ -520,7 +833,7 @@

    Response

    diff --git a/unibond/build.gradle b/unibond/build.gradle index 8eb3e21..2d05b93 100644 --- a/unibond/build.gradle +++ b/unibond/build.gradle @@ -28,6 +28,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'javax.validation:validation-api:2.0.1.Final' + // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-aws + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' diff --git a/unibond/src/docs/asciidoc/experience-community.adoc b/unibond/src/docs/asciidoc/experience-community.adoc index 1c0645c..70c332a 100644 --- a/unibond/src/docs/asciidoc/experience-community.adoc +++ b/unibond/src/docs/asciidoc/experience-community.adoc @@ -22,4 +22,30 @@ include::{snippets}/get-experience-community/http-request.adoc[] === Response include::{snippets}/get-experience-community/http-response.adoc[] -include::{snippets}/get-experience-community/response-fields.adoc[] \ No newline at end of file +include::{snippets}/get-experience-community/response-fields.adoc[] + +== Upload Posts on Experience Community + +경험 기록 게시판에 게시물 업로드하기 + +=== Request + +include::{snippets}/post-experience-community/httpie-request.adoc[] +include::{snippets}/post-experience-community/curl-request.adoc[] + +include::{snippets}/post-experience-community/request-headers.adoc[] +include::{snippets}/post-experience-community/request-parts.adoc[] +include::{snippets}/post-experience-community/request-part-request-fields.adoc[] + +- request의 경우 다음과 같은 예시처럼 json 타입으로 전달합니다. +[source,json] +---- +{ + "content" : "게시물 업로드 테스트 입니다." +} +---- + +=== Response + +include::{snippets}/post-experience-community/http-response.adoc[] +include::{snippets}/post-experience-community/response-fields.adoc[] diff --git a/unibond/src/main/java/com/unibond/unibond/common/PageInfo.java b/unibond/src/main/java/com/unibond/unibond/common/PageInfo.java new file mode 100644 index 0000000..a5899f8 --- /dev/null +++ b/unibond/src/main/java/com/unibond/unibond/common/PageInfo.java @@ -0,0 +1,23 @@ +package com.unibond.unibond.common; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.domain.Page; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class PageInfo { + private Boolean lastPage; + private int totalPages; + private long totalElements; + private int size; + + public PageInfo(Page page) { + this.lastPage = page.isLast(); + this.totalPages = page.getTotalPages(); + this.totalElements = page.getTotalElements(); + this.size = page.getSize(); + } +} diff --git a/unibond/src/main/java/com/unibond/unibond/common/config/S3Config.java b/unibond/src/main/java/com/unibond/unibond/common/config/S3Config.java new file mode 100644 index 0000000..b42e942 --- /dev/null +++ b/unibond/src/main/java/com/unibond/unibond/common/config/S3Config.java @@ -0,0 +1,32 @@ +package com.unibond.unibond.common.config; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3Client amazonS3Client() { + AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); + return (AmazonS3Client) AmazonS3ClientBuilder + .standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(region) + .build(); + } +} diff --git a/unibond/src/main/java/com/unibond/unibond/common/service/S3Uploader.java b/unibond/src/main/java/com/unibond/unibond/common/service/S3Uploader.java new file mode 100644 index 0000000..5a3525f --- /dev/null +++ b/unibond/src/main/java/com/unibond/unibond/common/service/S3Uploader.java @@ -0,0 +1,67 @@ +package com.unibond.unibond.common.service; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.PutObjectRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Optional; +import java.util.UUID; + +@Slf4j +@RequiredArgsConstructor +@Service +public class S3Uploader { + private final AmazonS3Client amazonS3Client; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + public String upload(MultipartFile multipartFile, String dirName) throws IOException { + File uploadFile = convert(multipartFile) + .orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File 전환 실패")); + return upload(uploadFile, dirName); + } + + private String upload(File uploadFile, String dirName) { + String fileName = dirName + "/" + UUID.randomUUID() + uploadFile.getName(); + String uploadImageUrl = putS3(uploadFile, fileName); + + removeNewFile(uploadFile); + return uploadImageUrl; + } + + private String putS3(File uploadFile, String fileName) { + amazonS3Client.putObject( + new PutObjectRequest(bucket, fileName, uploadFile) + .withCannedAcl(CannedAccessControlList.PublicRead) + ); + return amazonS3Client.getUrl(bucket, fileName).toString(); + } + + private void removeNewFile(File targetFile) { + if (targetFile.delete()) { + log.info("파일이 삭제되었습니다."); + } else { + log.info("파일이 삭제되지 못했습니다."); + } + } + + private Optional convert(MultipartFile file) throws IOException { + File convertFile = new File(file.getOriginalFilename()); + if (convertFile.createNewFile()) { + try (FileOutputStream fos = new FileOutputStream(convertFile)) { + fos.write(file.getBytes()); + } + return Optional.of(convertFile); + } + return Optional.empty(); + } +} diff --git a/unibond/src/main/java/com/unibond/unibond/letter_room/controller/LetterRoomController.java b/unibond/src/main/java/com/unibond/unibond/letter_room/controller/LetterRoomController.java index 44eca18..4934778 100644 --- a/unibond/src/main/java/com/unibond/unibond/letter_room/controller/LetterRoomController.java +++ b/unibond/src/main/java/com/unibond/unibond/letter_room/controller/LetterRoomController.java @@ -4,6 +4,8 @@ import com.unibond.unibond.common.BaseResponse; import com.unibond.unibond.letter_room.service.LetterRoomService; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; @RestController @@ -12,6 +14,16 @@ public class LetterRoomController { private final LetterRoomService letterRoomService; + @GetMapping("") + public BaseResponse getAllLetterRooms(@RequestHeader("Authorization") Long loginId, + @PageableDefault(size = 30) Pageable pageable) { + try { + return new BaseResponse<>(letterRoomService.getAllLetterRooms(pageable)); + } catch (BaseException e) { + return new BaseResponse<>(e.getStatus()); + } + } + @GetMapping("/{letterRoomId}") public BaseResponse getAllLetters(@PathVariable("letterRoomId") Long letterRoomId, @RequestHeader("Authorization") Long loginId) { diff --git a/unibond/src/main/java/com/unibond/unibond/letter_room/dto/GetAllLetterRoomsResDto.java b/unibond/src/main/java/com/unibond/unibond/letter_room/dto/GetAllLetterRoomsResDto.java new file mode 100644 index 0000000..7c7d2ac --- /dev/null +++ b/unibond/src/main/java/com/unibond/unibond/letter_room/dto/GetAllLetterRoomsResDto.java @@ -0,0 +1,27 @@ +package com.unibond.unibond.letter_room.dto; + +import com.unibond.unibond.common.PageInfo; +import com.unibond.unibond.letter_room.repository.repo_interface.LetterRoomPreviewRepoInterface; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.domain.Page; + +import java.util.List; +import java.util.stream.Collectors; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class GetAllLetterRoomsResDto { + private List letterRoomList; + private PageInfo pageInfo; + + public GetAllLetterRoomsResDto(Long loginId, Page letterRoomList) { + this.pageInfo = new PageInfo(letterRoomList); + this.letterRoomList = letterRoomList.stream().map( + letterRoomPreviewRepoInterface -> new LetterRoomPreviewResDto(loginId, letterRoomPreviewRepoInterface) + ).collect(Collectors.toList()); + } + +} diff --git a/unibond/src/main/java/com/unibond/unibond/letter_room/dto/LetterRoomPreviewResDto.java b/unibond/src/main/java/com/unibond/unibond/letter_room/dto/LetterRoomPreviewResDto.java new file mode 100644 index 0000000..47268db --- /dev/null +++ b/unibond/src/main/java/com/unibond/unibond/letter_room/dto/LetterRoomPreviewResDto.java @@ -0,0 +1,37 @@ +package com.unibond.unibond.letter_room.dto; + +import com.unibond.unibond.letter_room.repository.repo_interface.LetterRoomPreviewRepoInterface; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class LetterRoomPreviewResDto { + private String senderProfileImg; + private String senderNick; + private Long senderId; + private LocalDateTime recentLetterSentDate; + private Long letterRoomId; + + public LetterRoomPreviewResDto(Long loginId, LetterRoomPreviewRepoInterface letterRoomPreview) { + getSender(loginId, letterRoomPreview); + this.recentLetterSentDate = letterRoomPreview.getRecentLetterCreatedDate(); + this.letterRoomId = letterRoomPreview.getLetterRoomId(); + } + + private void getSender(Long loginId, LetterRoomPreviewRepoInterface letterRoomPreview) { + if (letterRoomPreview.getMember1Id().equals(loginId)) { + this.senderId = letterRoomPreview.getMember2Id(); + this.senderNick = letterRoomPreview.getMember2Nickname(); + this.senderProfileImg = letterRoomPreview.getMember2ProfileImg(); + } else { + this.senderId = letterRoomPreview.getMember1Id(); + this.senderNick = letterRoomPreview.getMember1Nickname(); + this.senderProfileImg = letterRoomPreview.getMember1ProfileImg(); + } + } +} diff --git a/unibond/src/main/java/com/unibond/unibond/letter_room/repository/LetterRoomCustomRepository.java b/unibond/src/main/java/com/unibond/unibond/letter_room/repository/LetterRoomCustomRepository.java new file mode 100644 index 0000000..9e9f5da --- /dev/null +++ b/unibond/src/main/java/com/unibond/unibond/letter_room/repository/LetterRoomCustomRepository.java @@ -0,0 +1,36 @@ +package com.unibond.unibond.letter_room.repository; + +import com.unibond.unibond.letter_room.domain.LetterRoom; +import com.unibond.unibond.letter_room.repository.repo_interface.LetterRoomPreviewRepoInterface; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface LetterRoomCustomRepository extends JpaRepository { + + @Query(value = + "select member1.id as member1Id, " + + "member1.profile_image as member1ProfileImg, " + + "member1.nickname as member1Nickname, " + + "member2.id as member2Id, " + + "member2.profile_image as member2ProfileImg, " + + "member2.nickname as member2Nickname, " + + "MAX(letter.created_date) as recentLetterCreatedDate, " + + "letterRoom.id as letterRoomId " + + "from letter_room as letterRoom " + + "join letter on letterRoom.id = letter.letter_room_id " + + "join member member1 on member1.id = letterRoom.member_id1 " + + "join member member2 on member2.id = letterRoom.member_id2 " + + "where ((member1.id = :member) or (member2.id = :member)) " + + "and letterRoom.status = 'ACTIVE' " + + "and ((letter.letter_status = 'ARRIVED') or letter.sender_id = :member)" + + "group by letterRoom.id, member1.id, member2.id " + + "order by recentLetterCreatedDate desc ", + nativeQuery = true) + Page findLetterRoomsByMember(@Param("member") Long memberId, + Pageable pageable); +} diff --git a/unibond/src/main/java/com/unibond/unibond/letter_room/repository/LetterRoomRepository.java b/unibond/src/main/java/com/unibond/unibond/letter_room/repository/LetterRoomRepository.java index 1f0d03b..7c5ad36 100644 --- a/unibond/src/main/java/com/unibond/unibond/letter_room/repository/LetterRoomRepository.java +++ b/unibond/src/main/java/com/unibond/unibond/letter_room/repository/LetterRoomRepository.java @@ -2,12 +2,14 @@ import com.unibond.unibond.letter_room.domain.LetterRoom; import com.unibond.unibond.member.domain.Member; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import javax.swing.text.html.Option; +import java.util.List; import java.util.Optional; @Repository @@ -27,5 +29,5 @@ Optional findLetterRoomBy2Member(@Param("member1") Member member1, "join fetch l.member2 " + "join fetch l.member2.disease " + "where l.id = :letterRoomId ") - Optional findByIdFetch2Member(@Param("letterRoomId")Long letterRoomId); + Optional findByIdFetch2Member(@Param("letterRoomId") Long letterRoomId); } diff --git a/unibond/src/main/java/com/unibond/unibond/letter_room/repository/repo_interface/LetterRoomPreviewRepoInterface.java b/unibond/src/main/java/com/unibond/unibond/letter_room/repository/repo_interface/LetterRoomPreviewRepoInterface.java new file mode 100644 index 0000000..ad302d3 --- /dev/null +++ b/unibond/src/main/java/com/unibond/unibond/letter_room/repository/repo_interface/LetterRoomPreviewRepoInterface.java @@ -0,0 +1,21 @@ +package com.unibond.unibond.letter_room.repository.repo_interface; + +import java.time.LocalDateTime; + +public interface LetterRoomPreviewRepoInterface { + Long getMember1Id(); + + String getMember1ProfileImg(); + + String getMember1Nickname(); + + Long getMember2Id(); + + String getMember2ProfileImg(); + + String getMember2Nickname(); + + LocalDateTime getRecentLetterCreatedDate(); + + Long getLetterRoomId(); +} diff --git a/unibond/src/main/java/com/unibond/unibond/letter_room/service/LetterRoomService.java b/unibond/src/main/java/com/unibond/unibond/letter_room/service/LetterRoomService.java index 4b21b3a..1657d1d 100644 --- a/unibond/src/main/java/com/unibond/unibond/letter_room/service/LetterRoomService.java +++ b/unibond/src/main/java/com/unibond/unibond/letter_room/service/LetterRoomService.java @@ -1,11 +1,20 @@ package com.unibond.unibond.letter_room.service; import com.unibond.unibond.common.BaseException; +import com.unibond.unibond.common.service.LoginInfoService; import com.unibond.unibond.letter.domain.Letter; import com.unibond.unibond.letter.repository.LetterRepository; +import com.unibond.unibond.letter_room.domain.LetterRoom; +import com.unibond.unibond.letter_room.dto.GetAllLetterRoomsResDto; import com.unibond.unibond.letter_room.dto.GetLetterRoomDetailResDto; +import com.unibond.unibond.letter_room.repository.LetterRoomCustomRepository; +import com.unibond.unibond.letter_room.repository.LetterRoomRepository; +import com.unibond.unibond.letter_room.repository.repo_interface.LetterRoomPreviewRepoInterface; import com.unibond.unibond.member.domain.Member; +import jdk.swing.interop.SwingInterOpUtils; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.List; @@ -15,6 +24,9 @@ @Service @RequiredArgsConstructor public class LetterRoomService { + private final LoginInfoService loginInfoService; + private final LetterRoomCustomRepository letterRoomCustomRepository; + private final LetterRoomRepository letterRoomRepository; private final LetterRepository letterRepository; public GetLetterRoomDetailResDto getAllLetters(Long letterRoomId, Long loginId) throws BaseException { @@ -42,4 +54,15 @@ private Member findAnotherParticipant(Letter letter, Long loginId) throws BaseEx } throw new BaseException(NOT_YOUR_LETTER_ROOM); } + + public GetAllLetterRoomsResDto getAllLetterRooms(Pageable pageable) throws BaseException { + try { + Long loginMemberId = loginInfoService.getLoginMemberId(); + Page letterRooms = letterRoomCustomRepository.findLetterRoomsByMember(loginMemberId, pageable); + return new GetAllLetterRoomsResDto(loginMemberId, letterRooms); + } catch (Exception e) { + System.out.println(e); + throw new BaseException(DATABASE_ERROR); + } + } } diff --git a/unibond/src/main/java/com/unibond/unibond/member/controller/MemberController.java b/unibond/src/main/java/com/unibond/unibond/member/controller/MemberController.java index 79b7ad0..39aace3 100644 --- a/unibond/src/main/java/com/unibond/unibond/member/controller/MemberController.java +++ b/unibond/src/main/java/com/unibond/unibond/member/controller/MemberController.java @@ -9,8 +9,11 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import static com.unibond.unibond.common.BaseResponseStatus.NOT_YOUR_PROFILE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; @RestController @RequiredArgsConstructor @@ -18,10 +21,11 @@ public class MemberController { private final MemberService memberService; - @PostMapping("") - public BaseResponse signup(@RequestBody MemberRegisterReqDto registerReqDto) { + @PostMapping(value = "", consumes = {APPLICATION_JSON_VALUE, MULTIPART_FORM_DATA_VALUE}) + public BaseResponse signup(@RequestPart MemberRegisterReqDto request, + @RequestPart MultipartFile profileImg) { try { - return new BaseResponse<>(memberService.signupMember(registerReqDto)); + return new BaseResponse<>(memberService.signupMember(request, profileImg)); } catch (BaseException e) { return new BaseResponse<>(e.getStatus()); } @@ -47,15 +51,16 @@ public BaseResponse getMemberDetail(@PathVariable("memberId") Long memberId, } } - @PatchMapping("/{memberId}") + @PatchMapping(value = "/{memberId}", consumes = {APPLICATION_JSON_VALUE, MULTIPART_FORM_DATA_VALUE}) public BaseResponse modifyMemberInfo(@PathVariable("memberId") Long memberId, - @RequestBody MemberModifyReqDto reqDto, + @RequestPart MemberModifyReqDto request, + @RequestPart MultipartFile profileImg, @RequestHeader("Authorization") Long loginId) { try { if (!memberId.equals(loginId)) { throw new BaseException(NOT_YOUR_PROFILE); } - return new BaseResponse<>(memberService.modifyMemberInfo(reqDto, loginId)); + return new BaseResponse<>(memberService.modifyMemberInfo(request, profileImg)); } catch (BaseException e) { return new BaseResponse<>(e.getStatus()); } diff --git a/unibond/src/main/java/com/unibond/unibond/member/domain/Member.java b/unibond/src/main/java/com/unibond/unibond/member/domain/Member.java index fa74812..7bc21b9 100644 --- a/unibond/src/main/java/com/unibond/unibond/member/domain/Member.java +++ b/unibond/src/main/java/com/unibond/unibond/member/domain/Member.java @@ -66,8 +66,8 @@ public Member(String profileImage, String nickname, Gender gender, Disease disea this.interestSet = interestSet; } - public void modifyMember(MemberModifyReqDto reqDto, Disease disease) { - this.profileImage = propertyNullCheck(reqDto.getProfileImage(), this.profileImage); + public void modifyMember(MemberModifyReqDto reqDto, Disease disease, String profileImgUrl) { + this.profileImage = propertyNullCheck(profileImgUrl, this.profileImage); this.nickname = propertyNullCheck(reqDto.getNickname(), this.nickname); this.gender = propertyNullCheck(reqDto.getGender(), this.gender); this.disease = propertyNullCheck(disease, this.disease); diff --git a/unibond/src/main/java/com/unibond/unibond/member/dto/MemberModifyReqDto.java b/unibond/src/main/java/com/unibond/unibond/member/dto/MemberModifyReqDto.java index 6e55db6..29d2a2a 100644 --- a/unibond/src/main/java/com/unibond/unibond/member/dto/MemberModifyReqDto.java +++ b/unibond/src/main/java/com/unibond/unibond/member/dto/MemberModifyReqDto.java @@ -2,13 +2,13 @@ import com.unibond.unibond.member.domain.Gender; import lombok.Data; +import org.springframework.web.multipart.MultipartFile; import java.time.LocalDate; import java.util.List; @Data public class MemberModifyReqDto { - private String profileImage; private String nickname; private Gender gender; private Long diseaseId; diff --git a/unibond/src/main/java/com/unibond/unibond/member/dto/MemberRegisterReqDto.java b/unibond/src/main/java/com/unibond/unibond/member/dto/MemberRegisterReqDto.java index 6a68986..89c3f31 100644 --- a/unibond/src/main/java/com/unibond/unibond/member/dto/MemberRegisterReqDto.java +++ b/unibond/src/main/java/com/unibond/unibond/member/dto/MemberRegisterReqDto.java @@ -11,7 +11,6 @@ @Data public class MemberRegisterReqDto { - private String profileImage; private Long diseaseId; private LocalDate diseaseTiming; private Gender gender; @@ -19,10 +18,10 @@ public class MemberRegisterReqDto { private String bio; private List interestList; - public Member toEntity(Disease disease) { + public Member toEntity(Disease disease, String imgUrl) { HashSet interestSet = new HashSet<>(interestList); return Member.builder() - .profileImage(this.profileImage) + .profileImage(imgUrl) .disease(disease) .diagnosisTiming(this.diseaseTiming) .gender(gender) diff --git a/unibond/src/main/java/com/unibond/unibond/member/service/MemberService.java b/unibond/src/main/java/com/unibond/unibond/member/service/MemberService.java index d220126..a476270 100644 --- a/unibond/src/main/java/com/unibond/unibond/member/service/MemberService.java +++ b/unibond/src/main/java/com/unibond/unibond/member/service/MemberService.java @@ -3,6 +3,7 @@ import com.unibond.unibond.common.BaseException; import com.unibond.unibond.common.BaseResponseStatus; import com.unibond.unibond.common.service.LoginInfoService; +import com.unibond.unibond.common.service.S3Uploader; import com.unibond.unibond.disease.domain.Disease; import com.unibond.unibond.disease.repository.DiseaseRepository; import com.unibond.unibond.member.domain.Member; @@ -17,6 +18,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import static com.unibond.unibond.common.BaseResponseStatus.*; @@ -24,13 +26,13 @@ @RequiredArgsConstructor public class MemberService { private final LoginInfoService loginInfoService; - + private final S3Uploader s3Uploader; private final MemberRepository memberRepository; private final DiseaseRepository diseaseRepository; private final PostRepository postRepository; @Transactional - public Long signupMember(MemberRegisterReqDto registerReqDto) throws BaseException { + public Long signupMember(MemberRegisterReqDto registerReqDto, MultipartFile profileImg) throws BaseException { try { if (memberRepository.existsMemberByNickname(registerReqDto.getNickname())) { throw new BaseException(DUPLICATE_MEMBER_NICK); @@ -39,8 +41,8 @@ public Long signupMember(MemberRegisterReqDto registerReqDto) throws BaseExcepti Disease disease = diseaseRepository.findById(registerReqDto.getDiseaseId()).orElseThrow( () -> new BaseException(INVALID_DISEASE_ID) ); - - Member newMember = registerReqDto.toEntity(disease); + String imgUrl = s3Uploader.upload(profileImg, "user"); + Member newMember = registerReqDto.toEntity(disease, imgUrl); Member savedMember = memberRepository.save(newMember); return savedMember.getId(); } catch (BaseException e) { @@ -66,16 +68,22 @@ public BaseResponseStatus checkNickNameDuplicate(String nickname) throws BaseExc } @Transactional - public BaseResponseStatus modifyMemberInfo(MemberModifyReqDto reqDto, Long loginId) throws BaseException { + public BaseResponseStatus modifyMemberInfo(MemberModifyReqDto reqDto, MultipartFile profileImg) throws BaseException { try { - Member member = memberRepository.findById(loginId).orElseThrow(() -> new BaseException(INVALID_MEMBER_ID)); + Member member = loginInfoService.getLoginMember(); Disease disease = null; if (reqDto.getDiseaseId() != null) { disease = diseaseRepository.findById(reqDto.getDiseaseId()) .orElseThrow(() -> new BaseException(INVALID_DISEASE_ID)); } - member.modifyMember(reqDto, disease); + + String imgUrl = null; + if (profileImg != null) { + imgUrl = s3Uploader.upload(profileImg, "user"); + } + + member.modifyMember(reqDto, disease, imgUrl); return SUCCESS; } catch (BaseException e) { throw e; diff --git a/unibond/src/main/java/com/unibond/unibond/post/controller/ExperiencePostController.java b/unibond/src/main/java/com/unibond/unibond/post/controller/ExperiencePostController.java index 36099b8..339260b 100644 --- a/unibond/src/main/java/com/unibond/unibond/post/controller/ExperiencePostController.java +++ b/unibond/src/main/java/com/unibond/unibond/post/controller/ExperiencePostController.java @@ -8,9 +8,12 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import static com.unibond.unibond.common.BaseResponseStatus.SUCCESS; import static com.unibond.unibond.post.domain.BoardType.EXPERIENCE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; @RestController @RequiredArgsConstructor @@ -18,12 +21,13 @@ public class ExperiencePostController { private final PostService postService; - @PostMapping("") + @PostMapping(value = "", consumes = {APPLICATION_JSON_VALUE, MULTIPART_FORM_DATA_VALUE}) public BaseResponse createPost(@RequestHeader("Authorization") Long loginId, - @RequestBody PostUploadReqDto reqDto) { + @RequestPart MultipartFile postImg, + @RequestPart PostUploadReqDto request) { try { - reqDto.setBoardType(EXPERIENCE); - postService.createPost(reqDto); + request.setBoardType(EXPERIENCE); + postService.createPost(request, postImg); return new BaseResponse<>(SUCCESS); } catch (BaseException e) { return new BaseResponse<>(e.getStatus()); diff --git a/unibond/src/main/java/com/unibond/unibond/post/controller/QuestionPostController.java b/unibond/src/main/java/com/unibond/unibond/post/controller/QuestionPostController.java index aa44ed1..908d405 100644 --- a/unibond/src/main/java/com/unibond/unibond/post/controller/QuestionPostController.java +++ b/unibond/src/main/java/com/unibond/unibond/post/controller/QuestionPostController.java @@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.*; import static com.unibond.unibond.common.BaseResponseStatus.SUCCESS; -import static com.unibond.unibond.post.domain.BoardType.EXPERIENCE; import static com.unibond.unibond.post.domain.BoardType.QNA; @RestController @@ -24,7 +23,7 @@ public BaseResponse createPost(@RequestHeader("Authorization") Long loginId, @RequestBody PostUploadReqDto reqDto) { try { reqDto.setBoardType(QNA); - postService.createPost(reqDto); + postService.createPost(reqDto, null); return new BaseResponse<>(SUCCESS); } catch (BaseException e) { return new BaseResponse<>(e.getStatus()); diff --git a/unibond/src/main/java/com/unibond/unibond/post/domain/Post.java b/unibond/src/main/java/com/unibond/unibond/post/domain/Post.java index b34f127..8a91982 100644 --- a/unibond/src/main/java/com/unibond/unibond/post/domain/Post.java +++ b/unibond/src/main/java/com/unibond/unibond/post/domain/Post.java @@ -33,17 +33,17 @@ public class Post extends BaseEntity { private String content; @Builder(builderMethodName = "createExperiencePost") - public Post(Member owner, BoardType boardType, String content) { + public Post(Member owner, BoardType boardType, String content, String postImageUrl) { this.owner = owner; this.boardType = boardType; this.content = content; + this.postImageUrl = postImageUrl; } @Builder(builderMethodName = "createQnAPost") - public Post(Member owner, BoardType boardType, String content, String postImageUrl) { + public Post(Member owner, BoardType boardType, String content) { this.owner = owner; this.boardType = boardType; this.content = content; - this.postImageUrl = postImageUrl; } } diff --git a/unibond/src/main/java/com/unibond/unibond/post/dto/PostUploadReqDto.java b/unibond/src/main/java/com/unibond/unibond/post/dto/PostUploadReqDto.java index cc4d05d..200e0ab 100644 --- a/unibond/src/main/java/com/unibond/unibond/post/dto/PostUploadReqDto.java +++ b/unibond/src/main/java/com/unibond/unibond/post/dto/PostUploadReqDto.java @@ -5,23 +5,22 @@ import com.unibond.unibond.post.domain.Post; import lombok.Data; -import static com.unibond.unibond.post.domain.BoardType.*; +import static com.unibond.unibond.post.domain.BoardType.QNA; @Data public class PostUploadReqDto { - // TODO: IMAGE UPLOAD private String content; // null private Member owner; private BoardType boardType; - public Post toEntity(Member owner) { + public Post toEntity(Member owner, String postImgUrl) { this.owner = owner; if (boardType.equals(QNA)) { return createQNAPost(); } else { - return createExperiencePost(); + return createExperiencePost(postImgUrl); } } @@ -33,12 +32,12 @@ private Post createQNAPost() { .build(); } - private Post createExperiencePost() { - // TODO: 이미지 업로드 구현 뒤 호출 메소드 변경 필요 - return Post.createQnAPost() + private Post createExperiencePost(String postImgUrl) { + return Post.createExperiencePost() .owner(owner) .boardType(boardType) .content(content) + .postImageUrl(postImgUrl) .build(); } } diff --git a/unibond/src/main/java/com/unibond/unibond/post/service/PostService.java b/unibond/src/main/java/com/unibond/unibond/post/service/PostService.java index 8479c80..d7173da 100644 --- a/unibond/src/main/java/com/unibond/unibond/post/service/PostService.java +++ b/unibond/src/main/java/com/unibond/unibond/post/service/PostService.java @@ -4,6 +4,7 @@ import com.unibond.unibond.comment.repository.CommentRepository; import com.unibond.unibond.common.BaseException; import com.unibond.unibond.common.service.LoginInfoService; +import com.unibond.unibond.common.service.S3Uploader; import com.unibond.unibond.member.domain.Member; import com.unibond.unibond.post.domain.BoardType; import com.unibond.unibond.post.domain.Post; @@ -16,6 +17,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import static com.unibond.unibond.common.BaseResponseStatus.DATABASE_ERROR; import static com.unibond.unibond.common.BaseResponseStatus.INVALID_POST_ID; @@ -24,15 +26,19 @@ @RequiredArgsConstructor public class PostService { private final LoginInfoService loginInfoService; - + private final S3Uploader s3Uploader; private final PostRepository postRepository; private final CommentRepository commentRepository; @Transactional - public void createPost(PostUploadReqDto reqDto) throws BaseException { + public void createPost(PostUploadReqDto reqDto, MultipartFile multipartFile) throws BaseException { try { Member loginMember = loginInfoService.getLoginMember(); - Post newPost = reqDto.toEntity(loginMember); + String imgUrl = null; + if (multipartFile != null) { + imgUrl = s3Uploader.upload(multipartFile, "post"); + } + Post newPost = reqDto.toEntity(loginMember, imgUrl); postRepository.save(newPost); } catch (BaseException e) { throw e; diff --git a/unibond/src/main/resources/application.properties b/unibond/src/main/resources/application.properties index 5ea0b65..d9d28db 100644 --- a/unibond/src/main/resources/application.properties +++ b/unibond/src/main/resources/application.properties @@ -5,5 +5,5 @@ spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.open-in-view=false spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update -spring.profiles.include=MYSQL +spring.profiles.include=MYSQL, S3 spring.jpa.properties.hibernate.default_batch_fetch_size=100 \ No newline at end of file diff --git a/unibond/src/main/resources/static/docs/experience-community.html b/unibond/src/main/resources/static/docs/experience-community.html index 8258fc0..0519c7d 100644 --- a/unibond/src/main/resources/static/docs/experience-community.html +++ b/unibond/src/main/resources/static/docs/experience-community.html @@ -454,6 +454,12 @@

    Experience Community API

  • Response
  • +
  • Upload Posts on Experience Community + +
  • @@ -480,15 +486,47 @@

    Response

    HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
    -Content-Length: 1246
    +Content-Length: 2708
     
     {
       "isSuccess" : true,
       "code" : 1000,
       "message" : "요청에 성공하였습니다.",
       "result" : {
    -    "numberOfElements" : 3,
    +    "numberOfElements" : 7,
         "postPreviewList" : [ {
    +      "createdDate" : "2023-12-27T03:16:53.252948",
    +      "ownerProfileImg" : "https://unibond-img-bucket.s3.ap-northeast-2.amazonaws.com/user/KakaoTalk_20230528_222457241_05.png",
    +      "ownerNick" : "5jizzi",
    +      "disease" : "13번 염색체 장완 21-22 부분의 결손",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
    +      "createdDate" : "2023-12-26T04:09:10.8631",
    +      "ownerProfileImg" : "http://testimg1",
    +      "ownerNick" : "jisoo2",
    +      "disease" : "5번 염색체 장완의 결손 증후군",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
    +      "createdDate" : "2023-12-26T04:09:05.784257",
    +      "ownerProfileImg" : "http://testimg1",
    +      "ownerNick" : "jisoo2",
    +      "disease" : "5번 염색체 장완의 결손 증후군",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
    +      "createdDate" : "2023-12-26T04:02:22.167476",
    +      "ownerProfileImg" : "http://testimg1",
    +      "ownerNick" : "jisoo2",
    +      "disease" : "5번 염색체 장완의 결손 증후군",
    +      "contentPreview" : "경험 기록 게시판 게시물 업로드 테스트",
    +      "boardType" : "EXPERIENCE",
    +      "isEnd" : true
    +    }, {
           "createdDate" : "2023-12-26T03:43:15.496574",
           "ownerProfileImg" : "http://testimg1",
           "ownerNick" : "jisoo2",
    @@ -515,7 +553,7 @@ 

    Response

    } ], "lastPage" : true, "totalPages" : 1, - "totalElements" : 3, + "totalElements" : 7, "size" : 30 } }
    @@ -625,11 +663,161 @@

    Response

    +
    +

    Upload Posts on Experience Community

    +
    +
    +

    경험 기록 게시판에 게시물 업로드하기

    +
    +
    +

    Request

    +
    +
    +
    $ http --multipart POST 'http://localhost:8080/api/v1/community/experience' \
    +    'Authorization:29' \
    +    'postImg'@'test-img.jpg' \
    +    'request'@'request'
    +
    +
    +
    +
    +
    $ curl 'http://localhost:8080/api/v1/community/experience' -i -X POST \
    +    -H 'Content-Type: multipart/form-data;charset=UTF-8' \
    +    -H 'Authorization: 29' \
    +    -F 'postImg=@test-img.jpg;type=multipart/form-data' \
    +    -F 'request=@request;type=application/json'
    +
    +
    + ++++ + + + + + + + + + + + + +
    NameDescription

    Authorization

    Basic auth credentials

    + ++++ + + + + + + + + + + + + + + + + +
    PartDescription

    postImg

    업로드 할 게시물 사진 파일

    request

    게시물 업로드 요청

    + +++++ + + + + + + + + + + + + + + +
    PathTypeDescription

    content

    String

    [request.content] 업로드 할 게시물 내용

    +
    +
      +
    • +

      request의 경우 다음과 같은 예시처럼 json 타입으로 전달합니다.

      +
    • +
    +
    +
    +
    +
    {
    +  "content" : "게시물 업로드 테스트 입니다."
    +}
    +
    +
    +
    +
    +

    Response

    +
    +
    +
    HTTP/1.1 200 OK
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 95
    +
    +{
    +  "isSuccess" : true,
    +  "code" : 1000,
    +  "message" : "요청에 성공하였습니다."
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    isSuccess

    Boolean

    성공 여부

    code

    Number

    결과 코드

    message

    String

    결과 메세지

    +
    +
    +
    diff --git a/unibond/src/test/java/com/unibond/unibond/post/controller/ExperiencePostControllerTest.java b/unibond/src/test/java/com/unibond/unibond/post/controller/ExperiencePostControllerTest.java index ca93e47..181e8a9 100644 --- a/unibond/src/test/java/com/unibond/unibond/post/controller/ExperiencePostControllerTest.java +++ b/unibond/src/test/java/com/unibond/unibond/post/controller/ExperiencePostControllerTest.java @@ -1,6 +1,7 @@ package com.unibond.unibond.post.controller; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.transaction.Transactional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,27 +10,33 @@ import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.filter.CharacterEncodingFilter; +import java.io.FileInputStream; import java.util.HashMap; import java.util.Map; import static com.unibond.unibond.common.ApiDocumentUtils.getDocumentRequest; import static com.unibond.unibond.common.ApiDocumentUtils.getDocumentResponse; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; import static org.springframework.restdocs.payload.JsonFieldType.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.partWithName; +import static org.springframework.restdocs.request.RequestDocumentation.requestParts; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -59,8 +66,7 @@ public void setUp(WebApplicationContext webApplicationContext, RestDocumentation @DisplayName("경험 기록 게시판 조회 Test") void getExperienceCommunityPosts() throws Exception { this.mockMvc.perform( - MockMvcRequestBuilders - .get("/api/v1/community/experience") + get("/api/v1/community/experience") .contentType(APPLICATION_JSON) ) .andExpect(status().isOk()) @@ -90,24 +96,46 @@ void getExperienceCommunityPosts() throws Exception { } @Test + @Transactional @DisplayName("경험 기록 게시판 게시물 업로드 Test") void createPost() throws Exception { + String fileName = "test-img"; + + FileInputStream fileInputStream = new FileInputStream("src/test/resources/static/" + fileName + ".jpg"); + MockMultipartFile testImg + = new MockMultipartFile("postImg", fileName + ".jpg", "multipart/form-data", fileInputStream); Map requestMap = new HashMap<>(); requestMap.put("content", "경험 기록 게시판 게시물 업로드 테스트"); - String content = objectMapper.writeValueAsString(requestMap); + MockMultipartFile request + = new MockMultipartFile("request", "request", "application/json", content.getBytes(UTF_8)); this.mockMvc.perform( - MockMvcRequestBuilders - .post("/api/v1/community/experience") - .contentType(APPLICATION_JSON) - .content(content) - .header("Authorization", 3) + multipart("/api/v1/community/experience") + .file(testImg) + .file(request) + .header("Authorization", 29) ) .andExpect(status().isOk()) .andDo(document("post-experience-community", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()))); + getDocumentRequest(), + getDocumentResponse(), + requestHeaders( + headerWithName("Authorization").description("Basic auth credentials") + ), + requestParts( + partWithName("postImg").description("업로드 할 게시물 사진 파일"), + partWithName("request").description("게시물 업로드 요청") + ), + requestPartFields( + "request", fieldWithPath("content").description("[request.content] 업로드 할 게시물 내용") + ), + responseFields( + fieldWithPath("isSuccess").type(BOOLEAN).description("성공 여부"), + fieldWithPath("code").type(NUMBER).description("결과 코드"), + fieldWithPath("message").type(STRING).description("결과 메세지") + ) + )); } } \ No newline at end of file diff --git a/unibond/src/test/resources/static/test-img.jpg b/unibond/src/test/resources/static/test-img.jpg new file mode 100644 index 0000000..a6a23d4 Binary files /dev/null and b/unibond/src/test/resources/static/test-img.jpg differ diff --git a/unibond/src/test/resources/static/test_profile_img.png b/unibond/src/test/resources/static/test_profile_img.png new file mode 100644 index 0000000..855c743 Binary files /dev/null and b/unibond/src/test/resources/static/test_profile_img.png differ