From f756542a768f004838b17490305d8fb397037202 Mon Sep 17 00:00:00 2001 From: SeungCheol Date: Thu, 21 Jul 2022 22:08:42 +0900 Subject: [PATCH 1/6] =?UTF-8?q?chore:=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../moamoa/member/domain/Member.java | 7 ++ .../moamoa/study/domain/Participant.java | 19 ++++++ .../moamoa/study/domain/study/Study.java | 67 ++++++++++++++++++- .../study/domain/studytag/AttachedTag.java | 19 ++++++ .../study/service/StudyDetailService.java | 4 +- 5 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/domain/Participant.java create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/AttachedTag.java diff --git a/backend/src/main/java/com/woowacourse/moamoa/member/domain/Member.java b/backend/src/main/java/com/woowacourse/moamoa/member/domain/Member.java index f526f5fd7..9e80fd8ea 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/member/domain/Member.java +++ b/backend/src/main/java/com/woowacourse/moamoa/member/domain/Member.java @@ -6,6 +6,10 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import com.woowacourse.moamoa.study.domain.study.Study; +import java.util.ArrayList; +import java.util.List; +import javax.persistence.OneToMany; import lombok.Getter; import lombok.NoArgsConstructor; @@ -26,6 +30,9 @@ public class Member { private String profileUrl; + @OneToMany(mappedBy = "owner") + private List establishedStudies = new ArrayList<>(); + public Member(final Long githubId, final String username, final String imageUrl, final String profileUrl) { this(null, githubId, username, imageUrl, profileUrl); } diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/Participant.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Participant.java new file mode 100644 index 000000000..80df522c9 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Participant.java @@ -0,0 +1,19 @@ +package com.woowacourse.moamoa.study.domain; + +import static lombok.AccessLevel.PROTECTED; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Embeddable +@NoArgsConstructor(access = PROTECTED) +@AllArgsConstructor +public class Participant { + + @Column(name = "member_id", nullable = false) + private Long memberId; +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java index 052898939..c41ea59a4 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java @@ -5,6 +5,8 @@ import static lombok.AccessLevel.PROTECTED; import com.woowacourse.moamoa.member.domain.Member; +import com.woowacourse.moamoa.study.domain.Participant; +import com.woowacourse.moamoa.study.domain.studytag.AttachedTag; import com.woowacourse.moamoa.study.domain.studytag.StudyTag; import java.time.LocalDateTime; import java.util.ArrayList; @@ -22,7 +24,6 @@ import lombok.NoArgsConstructor; @Entity -@Getter @NoArgsConstructor(access = PROTECTED) @AllArgsConstructor public class Study { @@ -68,4 +69,68 @@ public Study(final Long id, final String title, final String excerpt, this.thumbnail = thumbnail; this.status = status; } + + public Long getId() { + return id; + } + + public String getTitle() { + return title; + } + + public String getExcerpt() { + return excerpt; + } + + public String getThumbnail() { + return thumbnail; + } + + public String getStatus() { + return status; + } + + public String getDescription() { + return description; + } + + public Integer getCurrentMemberCount() { + return currentMemberCount; + } + + public Integer getMaxMemberCount() { + return maxMemberCount; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public LocalDateTime getEnrollmentEndDate() { + return enrollmentEndDate; + } + + public LocalDateTime getStartDate() { + return startDate; + } + + public LocalDateTime getEndDate() { + return endDate; + } + + public Member getOwner() { + return owner; + } + + public List getParticipants() { + return participants; + } + + public List getAttachedTags() { + return attachedTags; + } + + public List getStudyTags() { + return studyTags; + } } diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/AttachedTag.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/AttachedTag.java new file mode 100644 index 000000000..8de4609a1 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/AttachedTag.java @@ -0,0 +1,19 @@ +package com.woowacourse.moamoa.study.domain.studytag; + +import static lombok.AccessLevel.PROTECTED; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Embeddable +@NoArgsConstructor(access = PROTECTED) +@AllArgsConstructor +@Getter +public class AttachedTag { + + @Column(name = "tag_id", nullable = false) + private Long tagId; +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java index 4ec74ab06..238b2b77a 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java @@ -5,10 +5,10 @@ import com.woowacourse.moamoa.member.domain.Member; import com.woowacourse.moamoa.member.domain.repository.MemberRepository; import com.woowacourse.moamoa.member.service.response.MemberResponse; -import com.woowacourse.moamoa.study.domain.study.Participant; +import com.woowacourse.moamoa.study.domain.Participant; import com.woowacourse.moamoa.study.domain.study.Study; import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; -import com.woowacourse.moamoa.study.domain.study.AttachedTag; +import com.woowacourse.moamoa.study.domain.studytag.AttachedTag; import com.woowacourse.moamoa.study.exception.StudyNotExistException; import com.woowacourse.moamoa.study.service.response.StudyDetailResponse; import com.woowacourse.moamoa.tag.domain.Tag; From 64c324ce1d4f1cd0cadebdd8cac9f46bff59cb92 Mon Sep 17 00:00:00 2001 From: SeungCheol Date: Thu, 21 Jul 2022 22:10:46 +0900 Subject: [PATCH 2/6] =?UTF-8?q?refactor:=20Github=20OAuth=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/GithubOAuthClient.java | 91 ++++++++++++++ .../moamoa/auth/service/AuthService.java | 2 +- .../oauthclient/GithubOAuthClient.java | 68 ---------- .../auth/service/oauthclient/OAuthClient.java | 2 +- .../response/GithubProfileResponse.java | 2 +- .../response/OAuthAccessTokenResponse.java | 39 ------ .../service/request/AccessTokenRequest.java | 2 + .../common/advice/CommonControllerAdvice.java | 2 +- .../exception/UnauthorizedException.java | 2 +- .../moamoa/member/service/MemberService.java | 2 +- .../exception/InvalidMemberException.java | 4 +- .../acceptance/auth/AuthAcceptanceTest.java | 116 +++++++++++++++++- .../auth/controller/FakeAuthController.java | 68 ---------- .../moamoa/auth/service/OAuthClientTest.java | 10 +- 14 files changed, 220 insertions(+), 190 deletions(-) create mode 100644 backend/src/main/java/com/woowacourse/moamoa/auth/infrastructure/GithubOAuthClient.java delete mode 100644 backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/GithubOAuthClient.java rename backend/src/main/java/com/woowacourse/moamoa/auth/service/{ => oauthclient}/response/GithubProfileResponse.java (92%) delete mode 100644 backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/response/OAuthAccessTokenResponse.java rename backend/src/main/java/com/woowacourse/moamoa/{auth/service/oauthclient => common}/exception/UnauthorizedException.java (69%) rename backend/src/main/java/com/woowacourse/moamoa/{auth/service/oauthclient => member/service}/exception/InvalidMemberException.java (55%) delete mode 100644 backend/src/test/java/com/woowacourse/moamoa/auth/controller/FakeAuthController.java diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/infrastructure/GithubOAuthClient.java b/backend/src/main/java/com/woowacourse/moamoa/auth/infrastructure/GithubOAuthClient.java new file mode 100644 index 000000000..a7735e238 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/auth/infrastructure/GithubOAuthClient.java @@ -0,0 +1,91 @@ +package com.woowacourse.moamoa.auth.infrastructure; + +import com.woowacourse.moamoa.auth.service.oauthclient.OAuthClient; +import com.woowacourse.moamoa.common.exception.UnauthorizedException; +import com.woowacourse.moamoa.auth.service.request.AccessTokenRequest; +import com.woowacourse.moamoa.auth.service.oauthclient.response.GithubProfileResponse; +import java.util.Map; +import java.util.Objects; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +@Component +public class GithubOAuthClient implements OAuthClient { + + private static final String ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token"; + private static final String PROFILE_URL = "https://api.github.com/user"; + + private final String clientId; + private final String clientSecret; + private final RestTemplate restTemplate; + + public GithubOAuthClient( + @Value("${oauth2.github.client-id}") final String clientId, + @Value("${oauth2.github.client-secret}") final String clientSecret, + final RestTemplate restTemplate) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.restTemplate = restTemplate; + } + + /* + Github에서 유효하지 않는 Authorization Code로 Access Token을 요청 시 200 응답이 오고, + body로 error, error_description, error_uri가 담겨서 온다. + 에러 처리를 위해서는 body의 필드를 확인하여 처리해야 하므로 Map을 사용했다. + Access Token 요청이 정상적인 경우 body로 access_token, token_type, scope를 응답한다. + */ + @Override + public String getAccessToken(final String code) { + final AccessTokenRequest accessTokenRequest = new AccessTokenRequest(clientId, clientSecret, code); + + final HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); + + final HttpEntity httpEntity = new HttpEntity<>(accessTokenRequest, headers); + + try { + final ResponseEntity response = restTemplate.exchange( + ACCESS_TOKEN_URL, HttpMethod.POST, httpEntity, Map.class + ); + + if (response.getStatusCode().is2xxSuccessful()) { + Map body = (Map) Objects.requireNonNull(response.getBody()); + if (body.containsKey("error")) { + throw new UnauthorizedException("Access Token을 가져올 수 없습니다."); + } + return String.valueOf(body.get("access_token")); + } + throw new UnauthorizedException("Access Token을 가져올 수 없습니다."); + } catch (HttpClientErrorException e) { + throw new UnauthorizedException("Access Token을 가져올 수 없습니다."); + } + } + + @Override + public GithubProfileResponse getProfile(final String accessToken) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Authorization", "token " + accessToken); + + HttpEntity httpEntity = new HttpEntity<>(headers); + + try { + final ResponseEntity response = restTemplate.exchange( + PROFILE_URL, HttpMethod.GET, httpEntity, GithubProfileResponse.class); + + if (response.getStatusCode().is2xxSuccessful()) { + return Objects.requireNonNull(response.getBody()); + } + throw new UnauthorizedException("Github에서 사용자 정보를 가져올 수 없습니다."); + } catch (HttpClientErrorException e) { + throw new UnauthorizedException("Github에서 사용자 정보를 가져올 수 없습니다."); + } + } +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/service/AuthService.java b/backend/src/main/java/com/woowacourse/moamoa/auth/service/AuthService.java index c17a22bc8..d51b3a717 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/service/AuthService.java +++ b/backend/src/main/java/com/woowacourse/moamoa/auth/service/AuthService.java @@ -2,7 +2,7 @@ import com.woowacourse.moamoa.auth.infrastructure.JwtTokenProvider; import com.woowacourse.moamoa.auth.service.oauthclient.OAuthClient; -import com.woowacourse.moamoa.auth.service.response.GithubProfileResponse; +import com.woowacourse.moamoa.auth.service.oauthclient.response.GithubProfileResponse; import com.woowacourse.moamoa.auth.service.response.TokenResponse; import com.woowacourse.moamoa.member.service.MemberService; import lombok.RequiredArgsConstructor; diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/GithubOAuthClient.java b/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/GithubOAuthClient.java deleted file mode 100644 index b05fdaf83..000000000 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/GithubOAuthClient.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.woowacourse.moamoa.auth.service.oauthclient; -import com.woowacourse.moamoa.auth.service.oauthclient.exception.InvalidMemberException; -import com.woowacourse.moamoa.auth.service.request.AccessTokenRequest; -import com.woowacourse.moamoa.auth.service.response.GithubProfileResponse; -import com.woowacourse.moamoa.auth.service.oauthclient.response.OAuthAccessTokenResponse; -import java.util.Objects; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -@Component -public class GithubOAuthClient implements OAuthClient { - - private static final String ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token"; - private static final String PROFILE_URL = "https://api.github.com/user"; - - private final String clientId; - private final String clientSecret; - private final RestTemplate restTemplate; - - public GithubOAuthClient( - @Value("${oauth2.github.client-id}") final String clientId, - @Value("${oauth2.github.client-secret}") final String clientSecret, - final RestTemplate restTemplate) { - this.clientId = clientId; - this.clientSecret = clientSecret; - this.restTemplate = restTemplate; - } - - @Override - public String getAccessToken(final String code) { - final AccessTokenRequest accessTokenRequest = new AccessTokenRequest(clientId, clientSecret, code); - - final HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); - - final HttpEntity httpEntity = new HttpEntity<>(accessTokenRequest, headers); - final OAuthAccessTokenResponse accessTokenResponse = restTemplate.exchange( - ACCESS_TOKEN_URL, HttpMethod.POST, httpEntity, OAuthAccessTokenResponse.class - ).getBody(); - - if (Objects.isNull(accessTokenResponse)) { - throw new InvalidMemberException("Access Token을 가져올 수 없습니다."); - } - return accessTokenResponse.getAccessToken(); - } - - @Override - public GithubProfileResponse getProfile(final String accessToken) { - HttpHeaders headers = new HttpHeaders(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); - headers.add("Authorization", "token " + accessToken); - - HttpEntity httpEntity = new HttpEntity<>(headers); - - final GithubProfileResponse githubProfileResponse = restTemplate.exchange(PROFILE_URL, HttpMethod.GET, httpEntity, - GithubProfileResponse.class).getBody(); - - if (Objects.isNull(githubProfileResponse)) { - throw new InvalidMemberException("사용자 프로필 정보를 가져올 수 없습니다."); - } - return githubProfileResponse; - } -} diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/OAuthClient.java b/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/OAuthClient.java index 414e8094f..e0c8fd31b 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/OAuthClient.java +++ b/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/OAuthClient.java @@ -1,6 +1,6 @@ package com.woowacourse.moamoa.auth.service.oauthclient; -import com.woowacourse.moamoa.auth.service.response.GithubProfileResponse; +import com.woowacourse.moamoa.auth.service.oauthclient.response.GithubProfileResponse; public interface OAuthClient { diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/service/response/GithubProfileResponse.java b/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/response/GithubProfileResponse.java similarity index 92% rename from backend/src/main/java/com/woowacourse/moamoa/auth/service/response/GithubProfileResponse.java rename to backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/response/GithubProfileResponse.java index 384e6245f..6a7364c14 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/service/response/GithubProfileResponse.java +++ b/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/response/GithubProfileResponse.java @@ -1,4 +1,4 @@ -package com.woowacourse.moamoa.auth.service.response; +package com.woowacourse.moamoa.auth.service.oauthclient.response; import com.fasterxml.jackson.annotation.JsonProperty; import com.woowacourse.moamoa.member.domain.Member; diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/response/OAuthAccessTokenResponse.java b/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/response/OAuthAccessTokenResponse.java deleted file mode 100644 index e6882c1e4..000000000 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/response/OAuthAccessTokenResponse.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.woowacourse.moamoa.auth.service.oauthclient.response; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class OAuthAccessTokenResponse { - - @JsonProperty("access_token") - private String accessToken; - - @JsonProperty("token_type") - private String tokenType; - - private String scope; - - private OAuthAccessTokenResponse() { - } - - public OAuthAccessTokenResponse( - String accessToken, - String tokenType, - String scope - ) { - this.accessToken = accessToken; - this.tokenType = tokenType; - this.scope = scope; - } - - public String getAccessToken() { - return accessToken; - } - - public String getTokenType() { - return tokenType; - } - - public String getScope() { - return scope; - } -} diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/service/request/AccessTokenRequest.java b/backend/src/main/java/com/woowacourse/moamoa/auth/service/request/AccessTokenRequest.java index b1f960217..6e0d26fc2 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/service/request/AccessTokenRequest.java +++ b/backend/src/main/java/com/woowacourse/moamoa/auth/service/request/AccessTokenRequest.java @@ -1,9 +1,11 @@ package com.woowacourse.moamoa.auth.service.request; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor +@EqualsAndHashCode public class AccessTokenRequest { @JsonProperty("client_id") diff --git a/backend/src/main/java/com/woowacourse/moamoa/common/advice/CommonControllerAdvice.java b/backend/src/main/java/com/woowacourse/moamoa/common/advice/CommonControllerAdvice.java index 0af45fe38..6dd638b3f 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/common/advice/CommonControllerAdvice.java +++ b/backend/src/main/java/com/woowacourse/moamoa/common/advice/CommonControllerAdvice.java @@ -1,6 +1,6 @@ package com.woowacourse.moamoa.common.advice; -import com.woowacourse.moamoa.auth.service.oauthclient.exception.UnauthorizedException; +import com.woowacourse.moamoa.common.exception.UnauthorizedException; import com.woowacourse.moamoa.common.advice.response.ErrorResponse; import com.woowacourse.moamoa.common.exception.InvalidFormatException; import org.springframework.http.HttpStatus; diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/exception/UnauthorizedException.java b/backend/src/main/java/com/woowacourse/moamoa/common/exception/UnauthorizedException.java similarity index 69% rename from backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/exception/UnauthorizedException.java rename to backend/src/main/java/com/woowacourse/moamoa/common/exception/UnauthorizedException.java index 023aa8bf1..78d3be55a 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/exception/UnauthorizedException.java +++ b/backend/src/main/java/com/woowacourse/moamoa/common/exception/UnauthorizedException.java @@ -1,4 +1,4 @@ -package com.woowacourse.moamoa.auth.service.oauthclient.exception; +package com.woowacourse.moamoa.common.exception; public class UnauthorizedException extends RuntimeException { diff --git a/backend/src/main/java/com/woowacourse/moamoa/member/service/MemberService.java b/backend/src/main/java/com/woowacourse/moamoa/member/service/MemberService.java index cf189f2c1..b7fb0ff0b 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/member/service/MemberService.java +++ b/backend/src/main/java/com/woowacourse/moamoa/member/service/MemberService.java @@ -1,6 +1,6 @@ package com.woowacourse.moamoa.member.service; -import com.woowacourse.moamoa.auth.service.oauthclient.exception.InvalidMemberException; +import com.woowacourse.moamoa.member.service.exception.InvalidMemberException; import com.woowacourse.moamoa.member.domain.Member; import com.woowacourse.moamoa.member.domain.repository.MemberRepository; import com.woowacourse.moamoa.member.service.response.MemberResponse; diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/exception/InvalidMemberException.java b/backend/src/main/java/com/woowacourse/moamoa/member/service/exception/InvalidMemberException.java similarity index 55% rename from backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/exception/InvalidMemberException.java rename to backend/src/main/java/com/woowacourse/moamoa/member/service/exception/InvalidMemberException.java index 50c8ba886..76e8e58c4 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/service/oauthclient/exception/InvalidMemberException.java +++ b/backend/src/main/java/com/woowacourse/moamoa/member/service/exception/InvalidMemberException.java @@ -1,4 +1,6 @@ -package com.woowacourse.moamoa.auth.service.oauthclient.exception; +package com.woowacourse.moamoa.member.service.exception; + +import com.woowacourse.moamoa.common.exception.UnauthorizedException; public class InvalidMemberException extends UnauthorizedException { public InvalidMemberException(final String message) { diff --git a/backend/src/test/java/com/woowacourse/acceptance/auth/AuthAcceptanceTest.java b/backend/src/test/java/com/woowacourse/acceptance/auth/AuthAcceptanceTest.java index 8dbdf331d..f2b25b8f6 100644 --- a/backend/src/test/java/com/woowacourse/acceptance/auth/AuthAcceptanceTest.java +++ b/backend/src/test/java/com/woowacourse/acceptance/auth/AuthAcceptanceTest.java @@ -1,24 +1,134 @@ package com.woowacourse.acceptance.auth; import static org.hamcrest.Matchers.notNullValue; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.content; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.woowacourse.acceptance.AcceptanceTest; +import com.woowacourse.moamoa.auth.service.oauthclient.response.GithubProfileResponse; +import com.woowacourse.moamoa.auth.service.request.AccessTokenRequest; import io.restassured.RestAssured; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; public class AuthAcceptanceTest extends AcceptanceTest { + @Value("${oauth2.github.client-id}") + private String clientId; + + @Value("${oauth2.github.client-secret}") + private String clientSecret; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private RestTemplate restTemplate; + + private MockRestServiceServer mockServer; + + @BeforeEach + void setMockServer() { + mockServer = MockRestServiceServer.createServer(restTemplate); + } + @DisplayName("Authorization code를 받아서 token을 발급한다.") @Test - void getJwtToken() { + void getJwtToken() throws JsonProcessingException { + final String authorizationCode = "Authorization Code"; + final String accessToken = "access-token"; + mockingGithubServerForGetAccessToken(authorizationCode, Map.of("access_token", accessToken, + "token_type", "bearer", + "scope", "")); + mockingGithubServerForGetProfile(accessToken, HttpStatus.OK, + new GithubProfileResponse(1L, "sc0116", "https://image", "github.com")); + RestAssured.given().log().all() - .param("code", "Authorization code") + .param("code", authorizationCode) .when() - .post("/api/fake/login/token") + .post("/api/login/token") .then().log().all() .statusCode(HttpStatus.OK.value()) .body("token", notNullValue()); } + + @Test + @DisplayName("유효하지 않은 Authorization Code인 경우 401을 반환한다.") + void get401ByInvalidAuthorizationCode() throws JsonProcessingException { + final Map response = Map.of("error", "has error", + "error_description", "error description", + "error_uri", "error uri"); + final String invalidAuthorizationCode = "Invalid Authorization Code"; + mockingGithubServerForGetAccessToken(invalidAuthorizationCode, response); + + RestAssured.given().log().all() + .param("code", invalidAuthorizationCode) + .when() + .post("/api/login/token") + .then().log().all() + .statusCode(HttpStatus.UNAUTHORIZED.value()); + } + + @Test + @DisplayName("유효하지 않는 Access Token인 경우 401을 반환한다.") + void get401ByInvalidAccessToken() throws JsonProcessingException { + final String authorizationCode = "Authorization Code"; + final String accessToken = "access_token"; + final Map accessTokenResponse = Map.of(accessToken, "access-token", + "token_type", "bearer", + "scope", ""); + + mockingGithubServerForGetAccessToken(authorizationCode, accessTokenResponse); + mockingGithubServerForGetProfile("access-token", HttpStatus.UNAUTHORIZED); + + RestAssured.given().log().all() + .param("code", authorizationCode) + .when() + .post("/api/login/token") + .then().log().all() + .statusCode(HttpStatus.UNAUTHORIZED.value()); + } + + private void mockingGithubServerForGetAccessToken(final String authorizationCode, + final Map accessTokenResponse) + throws JsonProcessingException { + mockServer.expect(requestTo("https://github.com/login/oauth/access_token")) + .andExpect(method(HttpMethod.POST)) + .andExpect(content().json(objectMapper.writeValueAsString( + new AccessTokenRequest(clientId, clientSecret, authorizationCode)))) + .andRespond(withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(accessTokenResponse))); + } + + private void mockingGithubServerForGetProfile(final String accessToken, final HttpStatus status) + throws JsonProcessingException { + mockingGithubServerForGetProfile(accessToken, status, null); + } + + private void mockingGithubServerForGetProfile(final String accessToken, final HttpStatus status, + final GithubProfileResponse response) + throws JsonProcessingException { + + mockServer.expect(requestTo("https://api.github.com/user")) + .andExpect(method(HttpMethod.GET)) + .andExpect(header("Authorization", "token " + accessToken)) + .andRespond(withStatus(status) + .contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(response))); + } } diff --git a/backend/src/test/java/com/woowacourse/moamoa/auth/controller/FakeAuthController.java b/backend/src/test/java/com/woowacourse/moamoa/auth/controller/FakeAuthController.java deleted file mode 100644 index abe254015..000000000 --- a/backend/src/test/java/com/woowacourse/moamoa/auth/controller/FakeAuthController.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.woowacourse.moamoa.auth.controller; - -import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.woowacourse.moamoa.auth.service.AuthService; -import com.woowacourse.moamoa.auth.service.response.GithubProfileResponse; -import com.woowacourse.moamoa.auth.service.oauthclient.response.OAuthAccessTokenResponse; -import com.woowacourse.moamoa.auth.service.response.TokenResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestTemplate; - - -@RestController -public class FakeAuthController { - - @Autowired - private ObjectMapper objectMapper = new ObjectMapper(); - - @Autowired - private RestTemplate restTemplate; - - @Autowired - private AuthService authService; - - @PostMapping("/api/fake/login/token") - public ResponseEntity fakeLogin() throws JsonProcessingException { - MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate); - - final OAuthAccessTokenResponse accessToken = new OAuthAccessTokenResponse("access-token", "", ""); - - mockServer.expect(requestTo("https://github.com/login/oauth/access_token")) - .andExpect(method(HttpMethod.POST)) - .andRespond(withStatus(HttpStatus.OK) - .contentType(MediaType.APPLICATION_JSON) - .body(objectMapper.writeValueAsString(accessToken))); - - final GithubProfileResponse profileResponse = new GithubProfileResponse(1L, "jjanggu", "https://image", "github.com"); - - mockServer.expect(requestTo("https://api.github.com/user")) - .andExpect(method(HttpMethod.GET)) - .andRespond(withStatus(HttpStatus.OK) - .contentType(MediaType.APPLICATION_JSON) - .body(objectMapper.writeValueAsString(profileResponse))); - - final TokenResponse jwtToken = new TokenResponse("jwt-token"); - - mockServer.expect(requestTo("http://localhost:8080/api/login/token")) - .andExpect(method(HttpMethod.POST)) - .andRespond(withStatus(HttpStatus.OK) - .contentType(MediaType.APPLICATION_JSON) - .body(objectMapper.writeValueAsString(jwtToken))); - - final TokenResponse expected = authService.createToken("jwt"); - - return ResponseEntity.ok().body(expected); - } -} diff --git a/backend/src/test/java/com/woowacourse/moamoa/auth/service/OAuthClientTest.java b/backend/src/test/java/com/woowacourse/moamoa/auth/service/OAuthClientTest.java index eb2ff4d4c..abd39afbb 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/auth/service/OAuthClientTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/auth/service/OAuthClientTest.java @@ -9,8 +9,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.woowacourse.moamoa.MoamoaApplication; import com.woowacourse.moamoa.auth.service.oauthclient.OAuthClient; -import com.woowacourse.moamoa.auth.service.response.GithubProfileResponse; -import com.woowacourse.moamoa.auth.service.oauthclient.response.OAuthAccessTokenResponse; +import com.woowacourse.moamoa.auth.service.oauthclient.response.GithubProfileResponse; +import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -48,19 +48,19 @@ void setUp() { @DisplayName("Authorization code를 받아서 access token을 발급한다.") @Test void getAccessToken() throws JsonProcessingException { - final OAuthAccessTokenResponse token = new OAuthAccessTokenResponse("jwt-token", "", ""); + final Map accessTokenResponse = Map.of("access_token", "access-token", "token_type", "bearer", "scope", ""); mockServer.expect(requestTo("https://github.com/login/oauth/access_token")) .andExpect(method(HttpMethod.POST)) .andRespond(withStatus(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON) - .body(objectMapper.writeValueAsString(token))); + .body(objectMapper.writeValueAsString(accessTokenResponse))); final String accessToken = oAuthClient.getAccessToken("code"); mockServer.verify(); - assertThat(token.getAccessToken()).isEqualTo(accessToken); + assertThat(accessTokenResponse.get("access_token")).isEqualTo(accessToken); } @DisplayName("token을 받아서 사용자 프로필을 조회한다.") From d8d0471b2cb1ca20f90d0323b499d6e7358da6f1 Mon Sep 17 00:00:00 2001 From: SeungCheol Date: Thu, 21 Jul 2022 22:13:44 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EC=9C=A0=ED=9A=A8=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=A0=95=EB=B3=B4=EB=A1=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=84=B0=EB=94=94=20=EA=B0=9C=EC=84=A4=20=EC=8B=9C=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../moamoa/auth/config/AuthConfiguration.java | 2 +- .../controller/AuthenticationInterceptor.java | 13 +- .../controller/CreatingStudyController.java | 23 +++ ...r.java => SearchingStudiesController.java} | 2 +- .../request/AttachedTagsRequest.java | 14 ++ .../request/StudyDetailsRequest.java | 28 ++++ .../request/StudyPeriodRequest.java | 24 +++ .../acceptance/AcceptanceTest.java | 87 +++++++++++ .../acceptance/auth/AuthAcceptanceTest.java | 57 ------- .../study/CreatingStudyAcceptanceTest.java | 146 ++++++++++++++++++ .../study/controller/StudyControllerTest.java | 4 +- 11 files changed, 334 insertions(+), 66 deletions(-) create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java rename backend/src/main/java/com/woowacourse/moamoa/study/controller/{StudyController.java => SearchingStudiesController.java} (98%) create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/controller/request/AttachedTagsRequest.java create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyDetailsRequest.java create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyPeriodRequest.java create mode 100644 backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/config/AuthConfiguration.java b/backend/src/main/java/com/woowacourse/moamoa/auth/config/AuthConfiguration.java index ac1cd1de0..7fd3a82bd 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/config/AuthConfiguration.java +++ b/backend/src/main/java/com/woowacourse/moamoa/auth/config/AuthConfiguration.java @@ -31,7 +31,7 @@ public void addArgumentResolvers(final List resol @Override public void addInterceptors(final InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor) - .excludePathPatterns("/**"); + .addPathPatterns("/**"); } @Bean diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/controller/AuthenticationInterceptor.java b/backend/src/main/java/com/woowacourse/moamoa/auth/controller/AuthenticationInterceptor.java index 8289d1c0a..c8829fe31 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/controller/AuthenticationInterceptor.java +++ b/backend/src/main/java/com/woowacourse/moamoa/auth/controller/AuthenticationInterceptor.java @@ -2,6 +2,7 @@ import com.woowacourse.moamoa.auth.config.AuthenticationExtractor; import com.woowacourse.moamoa.auth.infrastructure.JwtTokenProvider; +import com.woowacourse.moamoa.common.exception.UnauthorizedException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -21,10 +22,12 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp return true; } - String token = AuthenticationExtractor.extract(request); - validateToken(token); + if (request.getMethod().equals("POST") && request.getServletPath().equals("/api/studies")) { + String token = AuthenticationExtractor.extract(request); + validateToken(token); - request.setAttribute("payload", jwtTokenProvider.getPayload(token)); + request.setAttribute("payload", jwtTokenProvider.getPayload(token)); + } return true; } @@ -34,8 +37,8 @@ private boolean isPreflight(HttpServletRequest request) { } private void validateToken(String token) { - if (token == null || token.isEmpty()) { - throw new IllegalStateException("토큰이 존재하지 않습니다."); + if (token == null || !jwtTokenProvider.validateToken(token)) { + throw new UnauthorizedException("유효하지 않은 토큰입니다."); } } } diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java new file mode 100644 index 000000000..76fba7b07 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java @@ -0,0 +1,23 @@ +package com.woowacourse.moamoa.study.controller; + +import com.woowacourse.moamoa.study.controller.request.AttachedTagsRequest; +import com.woowacourse.moamoa.study.controller.request.StudyDetailsRequest; +import com.woowacourse.moamoa.study.controller.request.StudyPeriodRequest; +import javax.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CreatingStudyController { + + @PostMapping("/api/studies") + public ResponseEntity createStudy( + @Valid @RequestBody final StudyDetailsRequest studyDetailsRequest, + @Valid @RequestBody final StudyPeriodRequest studyPeriodRequest, + @Valid @RequestBody final AttachedTagsRequest attachedTagsRequest + ) { + return ResponseEntity.ok().build(); + } +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/StudyController.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/SearchingStudiesController.java similarity index 98% rename from backend/src/main/java/com/woowacourse/moamoa/study/controller/StudyController.java rename to backend/src/main/java/com/woowacourse/moamoa/study/controller/SearchingStudiesController.java index 4b110b7ff..28c33b2b2 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/controller/StudyController.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/controller/SearchingStudiesController.java @@ -20,7 +20,7 @@ @RestController @RequestMapping("/api/studies") @RequiredArgsConstructor -public class StudyController { +public class SearchingStudiesController { private final StudyDetailService studyDetailService; private final StudyTagService studyTagService; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/AttachedTagsRequest.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/AttachedTagsRequest.java new file mode 100644 index 000000000..8155a38c0 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/AttachedTagsRequest.java @@ -0,0 +1,14 @@ +package com.woowacourse.moamoa.study.controller.request; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class AttachedTagsRequest { + + private List tagIds; +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyDetailsRequest.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyDetailsRequest.java new file mode 100644 index 000000000..ac8c60e88 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyDetailsRequest.java @@ -0,0 +1,28 @@ +package com.woowacourse.moamoa.study.controller.request; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class StudyDetailsRequest { + + @NotBlank + private String title; + + @NotBlank + private String excerpt; + + @NotBlank + private String thumbnail; + + @NotBlank + private String description; + + @Min(1) + private Integer maxMemberCount; +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyPeriodRequest.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyPeriodRequest.java new file mode 100644 index 000000000..afbf3d7ed --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyPeriodRequest.java @@ -0,0 +1,24 @@ +package com.woowacourse.moamoa.study.controller.request; + +import java.time.LocalDate; +import javax.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class StudyPeriodRequest { + + @DateTimeFormat(pattern = "yyyy-MM-dd") + @NotNull + private LocalDate startDate; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + private LocalDate enrollmentEndDate; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + private LocalDate endDate; +} diff --git a/backend/src/test/java/com/woowacourse/acceptance/AcceptanceTest.java b/backend/src/test/java/com/woowacourse/acceptance/AcceptanceTest.java index 3ee8d7716..e9c747ae7 100644 --- a/backend/src/test/java/com/woowacourse/acceptance/AcceptanceTest.java +++ b/backend/src/test/java/com/woowacourse/acceptance/AcceptanceTest.java @@ -1,11 +1,30 @@ package com.woowacourse.acceptance; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.content; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.woowacourse.moamoa.MoamoaApplication; +import com.woowacourse.moamoa.auth.service.oauthclient.response.GithubProfileResponse; +import com.woowacourse.moamoa.auth.service.request.AccessTokenRequest; import io.restassured.RestAssured; +import java.util.Map; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; @SpringBootTest( webEnvironment = WebEnvironment.RANDOM_PORT, @@ -15,8 +34,76 @@ public class AcceptanceTest { @LocalServerPort protected int port; + @Autowired + private RestTemplate restTemplate; + + @Autowired + private ObjectMapper objectMapper; + + @Value("${oauth2.github.client-id}") + private String clientId; + + @Value("${oauth2.github.client-secret}") + private String clientSecret; + + private MockRestServiceServer mockServer; + @BeforeEach void setUp() { RestAssured.port = port; } + + @BeforeEach + void mockingGithubServer() { + mockServer = MockRestServiceServer.createServer(restTemplate); + } + + protected String getBearerJwtToken(GithubProfileResponse response) { + final String authorizationCode = "Authorization Code"; + mockingGithubServer(authorizationCode, response); + final String token = RestAssured.given().log().all() + .param("code", authorizationCode) + .when() + .post("/api/login/token") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract().jsonPath().getString("token"); + mockServer.reset(); + return "bearer " + token; + } + + protected void mockingGithubServer(String authorizationCode, GithubProfileResponse response) { + try { + mockingGithubServerForGetAccessToken(authorizationCode, Map.of("access_token", "access-token", + "token_type", "bearer", + "scope", "")); + mockingGithubServerForGetProfile("access-token", HttpStatus.OK, response); + } catch (Exception e) { + Assertions.fail(e.getMessage()); + } + } + + protected void mockingGithubServerForGetAccessToken(final String authorizationCode, + final Map accessTokenResponse) + throws JsonProcessingException { + mockServer.expect(requestTo("https://github.com/login/oauth/access_token")) + .andExpect(method(HttpMethod.POST)) + .andExpect(content().json(objectMapper.writeValueAsString( + new AccessTokenRequest(clientId, clientSecret, authorizationCode)))) + .andRespond(withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(accessTokenResponse))); + } + + protected void mockingGithubServerForGetProfile(final String accessToken, final HttpStatus status, + final GithubProfileResponse response) + throws JsonProcessingException { + + mockServer.expect(requestTo("https://api.github.com/user")) + .andExpect(method(HttpMethod.GET)) + .andExpect(header("Authorization", "token " + accessToken)) + .andRespond(withStatus(status) + .contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(response))); + } } diff --git a/backend/src/test/java/com/woowacourse/acceptance/auth/AuthAcceptanceTest.java b/backend/src/test/java/com/woowacourse/acceptance/auth/AuthAcceptanceTest.java index f2b25b8f6..8bdd1698c 100644 --- a/backend/src/test/java/com/woowacourse/acceptance/auth/AuthAcceptanceTest.java +++ b/backend/src/test/java/com/woowacourse/acceptance/auth/AuthAcceptanceTest.java @@ -1,51 +1,18 @@ package com.woowacourse.acceptance.auth; import static org.hamcrest.Matchers.notNullValue; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.content; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import com.woowacourse.acceptance.AcceptanceTest; import com.woowacourse.moamoa.auth.service.oauthclient.response.GithubProfileResponse; -import com.woowacourse.moamoa.auth.service.request.AccessTokenRequest; import io.restassured.RestAssured; import java.util.Map; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.web.client.RestTemplate; public class AuthAcceptanceTest extends AcceptanceTest { - @Value("${oauth2.github.client-id}") - private String clientId; - - @Value("${oauth2.github.client-secret}") - private String clientSecret; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private RestTemplate restTemplate; - - private MockRestServiceServer mockServer; - - @BeforeEach - void setMockServer() { - mockServer = MockRestServiceServer.createServer(restTemplate); - } - @DisplayName("Authorization code를 받아서 token을 발급한다.") @Test void getJwtToken() throws JsonProcessingException { @@ -103,32 +70,8 @@ void get401ByInvalidAccessToken() throws JsonProcessingException { .statusCode(HttpStatus.UNAUTHORIZED.value()); } - private void mockingGithubServerForGetAccessToken(final String authorizationCode, - final Map accessTokenResponse) - throws JsonProcessingException { - mockServer.expect(requestTo("https://github.com/login/oauth/access_token")) - .andExpect(method(HttpMethod.POST)) - .andExpect(content().json(objectMapper.writeValueAsString( - new AccessTokenRequest(clientId, clientSecret, authorizationCode)))) - .andRespond(withStatus(HttpStatus.OK) - .contentType(MediaType.APPLICATION_JSON) - .body(objectMapper.writeValueAsString(accessTokenResponse))); - } - private void mockingGithubServerForGetProfile(final String accessToken, final HttpStatus status) throws JsonProcessingException { mockingGithubServerForGetProfile(accessToken, status, null); } - - private void mockingGithubServerForGetProfile(final String accessToken, final HttpStatus status, - final GithubProfileResponse response) - throws JsonProcessingException { - - mockServer.expect(requestTo("https://api.github.com/user")) - .andExpect(method(HttpMethod.GET)) - .andExpect(header("Authorization", "token " + accessToken)) - .andRespond(withStatus(status) - .contentType(MediaType.APPLICATION_JSON) - .body(objectMapper.writeValueAsString(response))); - } } diff --git a/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java b/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java new file mode 100644 index 000000000..b368de0aa --- /dev/null +++ b/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java @@ -0,0 +1,146 @@ +package com.woowacourse.acceptance.study; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.woowacourse.acceptance.AcceptanceTest; +import com.woowacourse.moamoa.auth.service.oauthclient.response.GithubProfileResponse; +import io.restassured.RestAssured; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +public class CreatingStudyAcceptanceTest extends AcceptanceTest { + + @DisplayName("유효하지 않은 토큰으로 스터디 개설 시 401을 반환한다.") + @ParameterizedTest + @ValueSource(strings = {"Invalid Token", "bearer Invalid Token"}) + void get401WhenUsingInvalidToken(String invalidToken) { + RestAssured + .given().log().all() + .header(HttpHeaders.AUTHORIZATION, invalidToken) + .when().log().all() + .post("/api/studies") + .then().log().all() + .statusCode(HttpStatus.UNAUTHORIZED.value()); + } + + @DisplayName("헤더에 토큰 없이 스터디 개설 시 401을 반환한다.") + @Test + void get401WhenUsingEmptyToken() { + RestAssured + .given().log().all() + .when().log().all() + .post("/api/studies") + .then().log().all() + .statusCode(HttpStatus.UNAUTHORIZED.value()); + } + + @DisplayName("스터디 개설 시 필수 데이터가 없는 경우 400을 반환한다.") + @ParameterizedTest + @MethodSource("provideBlankForRequiredFields") + void get400WhenSetBlankToRequiredField(Map param) { + final String jwtToken = getBearerJwtToken( + new GithubProfileResponse(1L, "jjanggu", "https://image", "github.com")); + + RestAssured + .given().log().all() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, jwtToken) + .body(param) + .when().log().all() + .post("/api/studies") + .then().log().all() + .statusCode(HttpStatus.BAD_REQUEST.value()); + } + + public static Stream provideBlankForRequiredFields() { + return Stream.of( + Arguments.of(Map.of( + "title", "", "excerpt", "자바를 공부하는 스터디", "thumbnail", "image", + "description", "스터디 상세 설명입니다.", "startDate", "2022-07-12")), + Arguments.of(Map.of( + "excerpt", "자바를 공부하는 스터디", "thumbnail", "image", + "description", "스터디 상세 설명입니다.", "startDate", "2022-07-12")), + Arguments.of(Map.of( + "title", "제목", "excerpt", " \t ", "thumbnail", "image", + "description", "스터디 상세 설명입니다.", "startDate", "2022-07-12")), + Arguments.of(Map.of( + "title", "제목", "excerpt", "자바를 공부하는 스터디", "thumbnail", " \n ", + "description", "스터디 상세 설명입니다.", "startDate", "2022-07-12")), + Arguments.of(Map.of( + "title", "제목", "excerpt", "자바를 공부하는 스터디", "thumbnail", "image", + "description", " \t \n ", "startDate", "2022-07-12")), + Arguments.of(Map.of( + "title", "제목", "excerpt", "자바를 공부하는 스터디", "thumbnail", "image", + "description", "스터디 상세 설명입니다.", "startDate", "")), + Arguments.of(Map.of( + "title", "제목", "excerpt", "자바를 공부하는 스터디", "thumbnail", "image", + "description", "스터디 상세 설명입니다.", "startDate", "startdate")) + ); + } + + @DisplayName("스터디 생성 시 선택 데이터의 형식이 유효하지 않는 경우 400을 반환한다.") + @ParameterizedTest + @MethodSource("provideInvalidFormatForOptionalFields") + void get400WhenSetInvalidFormatToOptionalFields(Map optionalBody) { + final String jwtToken = getBearerJwtToken( + new GithubProfileResponse(1L, "jjanggu", "https://image", "github.com")); + + Map requiredBody = Map.of("title", "제목", "excerpt", "자바를 공부하는 스터디", + "thumbnail", "image", "description", "스터디 상세 설명입니다.", "startDate", "2022-07-20"); + final HashMap body = new HashMap<>(requiredBody); + body.putAll(optionalBody); + + RestAssured + .given().log().all() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, jwtToken) + .body(body) + .when().log().all() + .post("/api/studies") + .then().log().all() + .statusCode(HttpStatus.BAD_REQUEST.value()); + } + + public static Stream provideInvalidFormatForOptionalFields() { + return Stream.of( + Arguments.of(Map.of("maxMemberCount", "one")), + Arguments.of(Map.of("maxMemberCount", "0")), + Arguments.of(Map.of("enrollmentEndDate", "2022-seven-one")), + Arguments.of(Map.of("endDate", "2022 07 11")), + Arguments.of(Map.of("tagIds", List.of("one", "two"))) + ); + } + + @Test + @DisplayName("정상적인 스터디 생성") + void createStudy() { + final String jwtToken = getBearerJwtToken( + new GithubProfileResponse(1L, "jjanggu", "https://image", "github.com")); + + final String locationHeader = RestAssured.given().log().all() + .header(HttpHeaders.AUTHORIZATION, jwtToken) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(Map.of("title", "제목", "excerpt", "자바를 공부하는 스터디", "thumbnail", "image", + "description", "스터디 상세 설명입니다.", "startDate", "2022-07-12")) + .when().log().all() + .post("/api/studies") + .then().log().all() + .statusCode(HttpStatus.CREATED.value()) + .extract().header(HttpHeaders.LOCATION); + + assertThat(locationHeader).matches(Pattern.compile("/api/studies/\\d+")); + } + +} diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java index b447c7df0..dcf851a98 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java @@ -32,11 +32,11 @@ public class StudyControllerTest { @Autowired private MemberRepository memberRepository; - private StudyController studyController; + private SearchingStudiesController studyController; @BeforeEach void setUp() { - studyController = new StudyController(new StudyDetailService(studyRepository, memberRepository, tagRepository), + studyController = new SearchingStudiesController(new StudyDetailService(studyRepository, memberRepository, tagRepository), new StudyTagService(studyTagRepository, studyRepository, tagRepository)); } From 00a8ea84249bcfc7fdb50797c399fee35a968def Mon Sep 17 00:00:00 2001 From: SeungCheol Date: Thu, 21 Jul 2022 22:23:12 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EC=8A=A4=ED=84=B0=EB=94=94=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthenticationArgumentResolver.java | 10 +- .../controller/CreatingStudyController.java | 22 ++-- .../request/AttachedTagsRequest.java | 14 --- .../controller/request/OpenStudyRequest.java | 66 +++++++++++ .../request/StudyDetailsRequest.java | 28 ----- .../request/StudyPeriodRequest.java | 24 ---- .../moamoa/study/domain/Participant.java | 20 ++++ .../exception/InvalidPeriodException.java | 5 + .../moamoa/study/domain/study/Details.java | 75 +++++++++++++ .../study/domain/study/Participants.java | 72 ++++++++++++ .../moamoa/study/domain/study/Period.java | 64 +++++++++++ .../moamoa/study/domain/study/Study.java | 105 +++++++----------- .../study/repository/StudyRepository.java | 4 +- .../CustomStudyTagRepositoryImpl.java | 2 +- .../study/service/CreateStudyService.java | 48 ++++++++ .../study/service/StudyDetailService.java | 2 +- .../moamoa/study/service/StudyTagService.java | 2 +- .../service/response/StudyDetailResponse.java | 20 ++-- .../study/service/response/StudyResponse.java | 4 +- .../acceptance/AcceptanceTest.java | 3 + .../study/CreatingStudyAcceptanceTest.java | 2 +- .../CreatingStudyControllerTest.java | 80 +++++++++++++ .../study/controller/StudyControllerTest.java | 2 +- .../moamoa/study/domain/study/PeriodTest.java | 50 +++++++++ .../moamoa/study/domain/study/StudyTest.java | 39 +++++++ .../study/repository/StudyRepositoryTest.java | 10 +- .../CustomStudyTagRepositoryTest.java | 19 ++-- backend/src/test/resources/data.sql | 10 +- backend/src/test/resources/init.sql | 0 29 files changed, 627 insertions(+), 175 deletions(-) delete mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/controller/request/AttachedTagsRequest.java create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/controller/request/OpenStudyRequest.java delete mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyDetailsRequest.java delete mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyPeriodRequest.java create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/domain/exception/InvalidPeriodException.java create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Details.java create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participants.java create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java create mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/service/CreateStudyService.java create mode 100644 backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java create mode 100644 backend/src/test/java/com/woowacourse/moamoa/study/domain/study/PeriodTest.java create mode 100644 backend/src/test/java/com/woowacourse/moamoa/study/domain/study/StudyTest.java create mode 100644 backend/src/test/resources/init.sql diff --git a/backend/src/main/java/com/woowacourse/moamoa/auth/controller/AuthenticationArgumentResolver.java b/backend/src/main/java/com/woowacourse/moamoa/auth/controller/AuthenticationArgumentResolver.java index cd30bffdd..9e72329d8 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/auth/controller/AuthenticationArgumentResolver.java +++ b/backend/src/main/java/com/woowacourse/moamoa/auth/controller/AuthenticationArgumentResolver.java @@ -1,7 +1,10 @@ package com.woowacourse.moamoa.auth.controller; +import com.woowacourse.moamoa.auth.config.AuthenticationExtractor; import com.woowacourse.moamoa.auth.config.AuthenticationPrincipal; +import com.woowacourse.moamoa.auth.infrastructure.JwtTokenProvider; import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -12,6 +15,9 @@ @Component public class AuthenticationArgumentResolver implements HandlerMethodArgumentResolver { + @Autowired + private JwtTokenProvider jwtTokenProvider; + @Override public boolean supportsParameter(final MethodParameter parameter) { return parameter.hasParameterAnnotation(AuthenticationPrincipal.class); @@ -21,6 +27,8 @@ public boolean supportsParameter(final MethodParameter parameter) { public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer, final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { final HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); - return request.getServletContext().getAttribute("payload"); + + final String token = AuthenticationExtractor.extract(request); + return Long.valueOf(jwtTokenProvider.getPayload(token)); } } diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java index 76fba7b07..4d110bc9a 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java @@ -1,23 +1,31 @@ package com.woowacourse.moamoa.study.controller; -import com.woowacourse.moamoa.study.controller.request.AttachedTagsRequest; -import com.woowacourse.moamoa.study.controller.request.StudyDetailsRequest; -import com.woowacourse.moamoa.study.controller.request.StudyPeriodRequest; +import com.woowacourse.moamoa.auth.config.AuthenticationPrincipal; +import com.woowacourse.moamoa.study.controller.request.OpenStudyRequest; +import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.service.CreateStudyService; +import java.net.URI; import javax.validation.Valid; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController +@RequiredArgsConstructor +@Getter public class CreatingStudyController { + private final CreateStudyService createStudyService; + @PostMapping("/api/studies") public ResponseEntity createStudy( - @Valid @RequestBody final StudyDetailsRequest studyDetailsRequest, - @Valid @RequestBody final StudyPeriodRequest studyPeriodRequest, - @Valid @RequestBody final AttachedTagsRequest attachedTagsRequest + @AuthenticationPrincipal final Long githubId, + @Valid @RequestBody(required = false) final OpenStudyRequest openStudyRequest ) { - return ResponseEntity.ok().build(); + final Study study = createStudyService.createStudy(githubId, openStudyRequest); + return ResponseEntity.created(URI.create("/api/studies/" + study.getId())).build(); } } diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/AttachedTagsRequest.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/AttachedTagsRequest.java deleted file mode 100644 index 8155a38c0..000000000 --- a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/AttachedTagsRequest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.woowacourse.moamoa.study.controller.request; - -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -public class AttachedTagsRequest { - - private List tagIds; -} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/OpenStudyRequest.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/OpenStudyRequest.java new file mode 100644 index 000000000..a7c14f965 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/OpenStudyRequest.java @@ -0,0 +1,66 @@ +package com.woowacourse.moamoa.study.controller.request; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Builder +public class OpenStudyRequest { + + @NotBlank + private String title; + + @NotBlank + private String excerpt; + + @NotBlank + private String thumbnail; + + @NotBlank + private String description; + + @Min(1) + private Integer maxMemberCount; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + @NotNull + private LocalDate startDate; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + private LocalDate enrollmentEndDate; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + private LocalDate endDate; + + private List tagIds = List.of(); + + public LocalDateTime getStartDateTime() { + return LocalDateTime.of(startDate, LocalTime.of(0, 0)); + } + + public LocalDateTime getEnrollmentEndDateTime() { + if (enrollmentEndDate == null) { + return null; + } + return LocalDateTime.of(enrollmentEndDate, LocalTime.of(0, 0)); + } + + public LocalDateTime getEndDateTime() { + if (endDate == null) { + return null; + } + return LocalDateTime.of(endDate, LocalTime.of(0, 0)); + } +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyDetailsRequest.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyDetailsRequest.java deleted file mode 100644 index ac8c60e88..000000000 --- a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyDetailsRequest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.woowacourse.moamoa.study.controller.request; - -import javax.validation.constraints.Min; -import javax.validation.constraints.NotBlank; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -public class StudyDetailsRequest { - - @NotBlank - private String title; - - @NotBlank - private String excerpt; - - @NotBlank - private String thumbnail; - - @NotBlank - private String description; - - @Min(1) - private Integer maxMemberCount; -} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyPeriodRequest.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyPeriodRequest.java deleted file mode 100644 index afbf3d7ed..000000000 --- a/backend/src/main/java/com/woowacourse/moamoa/study/controller/request/StudyPeriodRequest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.woowacourse.moamoa.study.controller.request; - -import java.time.LocalDate; -import javax.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.springframework.format.annotation.DateTimeFormat; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -public class StudyPeriodRequest { - - @DateTimeFormat(pattern = "yyyy-MM-dd") - @NotNull - private LocalDate startDate; - - @DateTimeFormat(pattern = "yyyy-MM-dd") - private LocalDate enrollmentEndDate; - - @DateTimeFormat(pattern = "yyyy-MM-dd") - private LocalDate endDate; -} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/Participant.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Participant.java index 80df522c9..d28c02d4f 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/Participant.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Participant.java @@ -2,18 +2,38 @@ import static lombok.AccessLevel.PROTECTED; +import java.util.Objects; import javax.persistence.Column; import javax.persistence.Embeddable; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.ToString; @Getter @Embeddable @NoArgsConstructor(access = PROTECTED) @AllArgsConstructor +@ToString public class Participant { @Column(name = "member_id", nullable = false) private Long memberId; + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Participant that = (Participant) o; + return Objects.equals(memberId, that.memberId); + } + + @Override + public int hashCode() { + return Objects.hash(memberId); + } } diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/exception/InvalidPeriodException.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/exception/InvalidPeriodException.java new file mode 100644 index 000000000..79b61ede3 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/exception/InvalidPeriodException.java @@ -0,0 +1,5 @@ +package com.woowacourse.moamoa.study.domain.exception; + +public class InvalidPeriodException extends RuntimeException { + +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Details.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Details.java new file mode 100644 index 000000000..1d93f7377 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Details.java @@ -0,0 +1,75 @@ +package com.woowacourse.moamoa.study.domain.study; + +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Embeddable; + +@Embeddable +public class Details { + + @Column(nullable = false) + private String title; + + @Column(nullable = false) + private String excerpt; + + @Column(nullable = false) + private String thumbnail; + + @Column(nullable = false) + private String status; + + @Column(nullable = false) + private String description; + + protected Details() { + } + + public Details(final String title, final String excerpt, final String thumbnail, final String status, + final String description) { + this.title = title; + this.excerpt = excerpt; + this.thumbnail = thumbnail; + this.status = status; + this.description = description; + } + + public String getTitle() { + return title; + } + + public String getExcerpt() { + return excerpt; + } + + public String getThumbnail() { + return thumbnail; + } + + public String getStatus() { + return status; + } + + public String getDescription() { + return description; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Details details = (Details) o; + return Objects.equals(title, details.title) && Objects.equals(excerpt, details.excerpt) + && Objects.equals(thumbnail, details.thumbnail) && Objects.equals(status, + details.status) && Objects.equals(description, details.description); + } + + @Override + public int hashCode() { + return Objects.hash(title, excerpt, thumbnail, status, description); + } +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participants.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participants.java new file mode 100644 index 000000000..f95239f36 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participants.java @@ -0,0 +1,72 @@ +package com.woowacourse.moamoa.study.domain.study; + +import com.woowacourse.moamoa.study.domain.Participant; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import lombok.ToString; + +@Embeddable +@ToString +public class Participants { + + @Column(name = "current_member_count") + private int size; + + @Column(name = "max_member_count") + private Integer max; + + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "study_member", joinColumns = @JoinColumn(name = "study_id")) + private List participants = new ArrayList<>(); + + protected Participants() { + } + + public Participants(final Integer size, final Integer max, + final List participants) { + this.size = size; + this.max = max; + this.participants = participants; + } + + public int getSize() { + return size; + } + + public int getMax() { + return max; + } + + public List getParticipants() { + return new ArrayList<>(participants); + } + + public static Participants createByMaxSize(final Integer maxSize) { + return new Participants(1, maxSize, new ArrayList<>()); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Participants that = (Participants) o; + return size == that.size && Objects.equals(max, that.max) && Objects.equals(getParticipants(), + that.getParticipants()); + } + + @Override + public int hashCode() { + return Objects.hash(size, max, participants); + } +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java new file mode 100644 index 000000000..bcd84d6dd --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java @@ -0,0 +1,64 @@ +package com.woowacourse.moamoa.study.domain.study; + +import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; +import java.time.LocalDateTime; +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Embeddable; + +@Embeddable +public class Period { + + private LocalDateTime enrollmentEndDate; + + @Column(nullable = false) + private LocalDateTime startDate; + + private LocalDateTime endDate; + + public Period() { + } + + public Period(final LocalDateTime enrollmentEndDate, final LocalDateTime startDate, final LocalDateTime endDate) { + if (startDate.isAfter(endDate) || enrollmentEndDate.isAfter(endDate)) { + throw new InvalidPeriodException(); + } + this.enrollmentEndDate = enrollmentEndDate; + this.startDate = startDate; + this.endDate = endDate; + } + + public boolean isBefore(final LocalDateTime createAt) { + return startDate.isBefore(createAt) || enrollmentEndDate.isBefore(createAt); + } + + public LocalDateTime getEnrollmentEndDate() { + return enrollmentEndDate; + } + + public LocalDateTime getStartDate() { + return startDate; + } + + public LocalDateTime getEndDate() { + return endDate; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Period period = (Period) o; + return Objects.equals(enrollmentEndDate, period.enrollmentEndDate) && Objects.equals(startDate, + period.startDate) && Objects.equals(endDate, period.endDate); + } + + @Override + public int hashCode() { + return Objects.hash(enrollmentEndDate, startDate, endDate); + } +} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java index c41ea59a4..0bf0b12fc 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java @@ -2,57 +2,50 @@ import static javax.persistence.FetchType.LAZY; import static javax.persistence.GenerationType.IDENTITY; -import static lombok.AccessLevel.PROTECTED; import com.woowacourse.moamoa.member.domain.Member; -import com.woowacourse.moamoa.study.domain.Participant; +import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; import com.woowacourse.moamoa.study.domain.studytag.AttachedTag; import com.woowacourse.moamoa.study.domain.studytag.StudyTag; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import javax.persistence.CollectionTable; +import javax.persistence.Column; import javax.persistence.ElementCollection; +import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; @Entity -@NoArgsConstructor(access = PROTECTED) -@AllArgsConstructor public class Study { @Id @GeneratedValue(strategy = IDENTITY) private Long id; - private String title; - private String excerpt; - private String thumbnail; - private String status; - private String description; - private Integer currentMemberCount; - private Integer maxMemberCount; + @Embedded + private Details details; + @Embedded + private Participants participants; + + @Embedded + private Period period; + + @CreatedDate + @Column(updatable = false) private LocalDateTime createdAt; - private LocalDateTime enrollmentEndDate; - private LocalDateTime startDate; - private LocalDateTime endDate; @ManyToOne(fetch = LAZY) @JoinColumn(name = "owner_id") private Member owner; - @ElementCollection - @CollectionTable(name = "study_member", joinColumns = @JoinColumn(name = "study_id")) - private List participants = new ArrayList<>(); - @ElementCollection @CollectionTable(name = "study_tag", joinColumns = @JoinColumn(name = "study_id")) private List attachedTags = new ArrayList<>(); @@ -60,77 +53,61 @@ public class Study { @OneToMany(mappedBy = "study") private List studyTags = new ArrayList<>(); - public Study(final Long id, final String title, final String excerpt, - final String thumbnail, final String status - ) { + public Study(final Long id, final Details details) { this.id = id; - this.title = title; - this.excerpt = excerpt; - this.thumbnail = thumbnail; - this.status = status; - } - - public Long getId() { - return id; - } - - public String getTitle() { - return title; + this.details = details; } - public String getExcerpt() { - return excerpt; + public Study(final Details details, final Participants participants, final Member owner, + final Period period, final List attachedTags) { + this(null, details, participants, period, owner, attachedTags); } - public String getThumbnail() { - return thumbnail; - } + public Study(final Long id, + final Details details, final Participants participants, + final Period period, final Member owner, + final List attachedTags) { + this.id = id; + this.details = details; + this.participants = participants; + this.period = period; + this.createdAt = LocalDateTime.now(); + this.owner = owner; + this.attachedTags = attachedTags; - public String getStatus() { - return status; + if (period.isBefore(createdAt)) { + throw new InvalidPeriodException(); + } } - public String getDescription() { - return description; + protected Study() { } - public Integer getCurrentMemberCount() { - return currentMemberCount; + public Long getId() { + return id; } - public Integer getMaxMemberCount() { - return maxMemberCount; + public Details getDetails() { + return details; } public LocalDateTime getCreatedAt() { return createdAt; } - public LocalDateTime getEnrollmentEndDate() { - return enrollmentEndDate; - } - - public LocalDateTime getStartDate() { - return startDate; - } - - public LocalDateTime getEndDate() { - return endDate; + public Period getPeriod() { + return period; } public Member getOwner() { return owner; } - public List getParticipants() { + public Participants getParticipants() { return participants; } public List getAttachedTags() { return attachedTags; } - - public List getStudyTags() { - return studyTags; - } } diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepository.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepository.java index 49410f0ef..10427617b 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepository.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepository.java @@ -7,9 +7,11 @@ public interface StudyRepository { + Study save(Study study); + Slice findAll(Pageable pageable); - Slice findByTitleContainingIgnoreCase(String title, Pageable pageable); + Slice findByDetailsTitleContainingIgnoreCase(String title, Pageable pageable); Optional findById(Long id); } diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryImpl.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryImpl.java index d703035e4..2a05ccd1d 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryImpl.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryImpl.java @@ -48,7 +48,7 @@ public StudySlice searchBy(StudySearchCondition condition, Pageable pageable) { } private Predicate studyTitleEq(final String title) { - return hasText(title) ? studyTag.study.title.containsIgnoreCase(title) : null; + return hasText(title) ? studyTag.study.details.title.containsIgnoreCase(title) : null; } private List findFilteredStudy(final List tags) { diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/CreateStudyService.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/CreateStudyService.java new file mode 100644 index 000000000..1766f109c --- /dev/null +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/CreateStudyService.java @@ -0,0 +1,48 @@ +package com.woowacourse.moamoa.study.service; + +import com.woowacourse.moamoa.member.domain.Member; +import com.woowacourse.moamoa.member.domain.repository.MemberRepository; +import com.woowacourse.moamoa.study.controller.request.OpenStudyRequest; +import com.woowacourse.moamoa.study.domain.study.Details; +import com.woowacourse.moamoa.study.domain.study.Participants; +import com.woowacourse.moamoa.study.domain.study.Period; +import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; +import com.woowacourse.moamoa.study.domain.studytag.AttachedTag; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +public class CreateStudyService { + + private final MemberRepository memberRepository; + private final StudyRepository studyRepository; + + public CreateStudyService(final MemberRepository memberRepository, + final StudyRepository studyRepository) { + this.memberRepository = memberRepository; + this.studyRepository = studyRepository; + } + + public Study createStudy(final Long githubId, final OpenStudyRequest openStudyRequest) { + final List attachedTags = openStudyRequest.getTagIds() + .stream() + .map(AttachedTag::new) + .collect(Collectors.toList()); + + final Member member = memberRepository.findByGithubId(githubId).get(); + + final Participants participants = Participants.createByMaxSize(openStudyRequest.getMaxMemberCount()); + final Details details = new Details(openStudyRequest.getTitle(), openStudyRequest.getExcerpt(), + openStudyRequest.getThumbnail(), "OPEN", openStudyRequest.getDescription()); + final Period period = new Period(openStudyRequest.getEnrollmentEndDateTime(), + openStudyRequest.getStartDateTime(), + openStudyRequest.getEndDateTime()); + + return studyRepository.save(new Study(details, participants, member, period, attachedTags)); + } + +} \ No newline at end of file diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java index 238b2b77a..e5f4206b3 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java @@ -49,7 +49,7 @@ private List getParticipantsResponse(final Study study) { } private List getParticipantIds(final Study study) { - final List participants = study.getParticipants(); + final List participants = study.getParticipants().getParticipants(); return participants.stream() .map(Participant::getMemberId) .collect(toList()); diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyTagService.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyTagService.java index 12df7eccf..ffa4c4609 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyTagService.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyTagService.java @@ -46,7 +46,7 @@ public StudiesResponse searchBy(final String title, final TagRequest tagRequest, } private StudiesResponse searchWithoutFilter(final String title, final Pageable pageable) { - final Slice slice = studyRepository.findByTitleContainingIgnoreCase(title.trim(), pageable); + final Slice slice = studyRepository.findByDetailsTitleContainingIgnoreCase(title.trim(), pageable); final List studies = slice .map(StudyResponse::new) .getContent(); diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyDetailResponse.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyDetailResponse.java index 2c7b782f5..27909e4ee 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyDetailResponse.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyDetailResponse.java @@ -36,17 +36,17 @@ public class StudyDetailResponse { public StudyDetailResponse(final Study study, final MemberResponse owner, final List membersResponse, final List attachedTags) { this.id = study.getId(); - this.title = study.getTitle(); - this.excerpt = study.getExcerpt(); - this.thumbnail = study.getThumbnail(); - this.status = study.getStatus(); - this.description = study.getDescription(); - this.currentMemberCount = study.getCurrentMemberCount(); - this.maxMemberCount = study.getMaxMemberCount(); + this.title = study.getDetails().getTitle(); + this.excerpt = study.getDetails().getExcerpt(); + this.thumbnail = study.getDetails().getThumbnail(); + this.status = study.getDetails().getStatus(); + this.description = study.getDetails().getDescription(); + this.currentMemberCount = study.getParticipants().getSize(); + this.maxMemberCount = study.getParticipants().getMax(); this.createdAt = getNullableDate(study.getCreatedAt()); - this.enrollmentEndDate = getNullableDate(study.getEnrollmentEndDate()); - this.startDate = getNullableDate(study.getStartDate()); - this.endDate = getNullableDate(study.getEndDate()); + this.enrollmentEndDate = getNullableDate(study.getPeriod().getEnrollmentEndDate()); + this.startDate = getNullableDate(study.getPeriod().getStartDate()); + this.endDate = getNullableDate(study.getPeriod().getEndDate()); this.owner = owner; this.members = membersResponse; this.tags = attachedTags; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyResponse.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyResponse.java index 59752fc9f..41eef0d34 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyResponse.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyResponse.java @@ -17,7 +17,7 @@ public class StudyResponse { private String status; public StudyResponse(final Study study) { - this(study.getId(), study.getTitle(), study.getExcerpt(), study.getThumbnail(), - study.getStatus()); + this(study.getId(), study.getDetails().getTitle(), study.getDetails().getExcerpt(), study.getDetails().getThumbnail(), + study.getDetails().getStatus()); } } diff --git a/backend/src/test/java/com/woowacourse/acceptance/AcceptanceTest.java b/backend/src/test/java/com/woowacourse/acceptance/AcceptanceTest.java index e9c747ae7..185427ed2 100644 --- a/backend/src/test/java/com/woowacourse/acceptance/AcceptanceTest.java +++ b/backend/src/test/java/com/woowacourse/acceptance/AcceptanceTest.java @@ -23,6 +23,8 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.web.client.RestTemplate; @@ -30,6 +32,7 @@ webEnvironment = WebEnvironment.RANDOM_PORT, classes = {MoamoaApplication.class} ) +@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD) public class AcceptanceTest { @LocalServerPort protected int port; diff --git a/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java b/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java index b368de0aa..20c3959b0 100644 --- a/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java +++ b/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java @@ -133,7 +133,7 @@ void createStudy() { .header(HttpHeaders.AUTHORIZATION, jwtToken) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .body(Map.of("title", "제목", "excerpt", "자바를 공부하는 스터디", "thumbnail", "image", - "description", "스터디 상세 설명입니다.", "startDate", "2022-07-12")) + "description", "스터디 상세 설명입니다.", "startDate", "2022-07-12", "endDate", "")) .when().log().all() .post("/api/studies") .then().log().all() diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java new file mode 100644 index 000000000..1dd10738e --- /dev/null +++ b/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java @@ -0,0 +1,80 @@ +package com.woowacourse.moamoa.study.controller; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.woowacourse.moamoa.common.RepositoryTest; +import com.woowacourse.moamoa.member.domain.repository.MemberRepository; +import com.woowacourse.moamoa.study.controller.request.OpenStudyRequest; +import com.woowacourse.moamoa.study.domain.study.Details; +import com.woowacourse.moamoa.study.domain.study.Participants; +import com.woowacourse.moamoa.study.domain.study.Period; +import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; +import com.woowacourse.moamoa.study.service.CreateStudyService; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +@RepositoryTest +public class CreatingStudyControllerTest { + + @Autowired + private StudyRepository studyRepository; + + @Autowired + private MemberRepository memberRepository; + + @DisplayName("스터디를 생성하여 저장한다.") + @Test + void openStudy() { + // given + CreatingStudyController sut = new CreatingStudyController(new CreateStudyService(memberRepository, studyRepository)); + final OpenStudyRequest openStudyRequest = OpenStudyRequest.builder() + .title("Java") + .excerpt("java excerpt") + .thumbnail("java image") + .description("자바 스터디 상세설명 입니다.") + .startDate(LocalDate.of(2022, 7, 10)) + .endDate(LocalDate.of(2022, 10, 10)) + .enrollmentEndDate(LocalDate.of(2022, 8, 10)) + .maxMemberCount(10) + .tagIds(List.of(1L, 2L)) + .build(); + + // when + final ResponseEntity response = sut.createStudy(1L, openStudyRequest); + + // then + final String id = response.getHeaders().getLocation().getPath().replace("/api/studies/", ""); + Long studyId = Long.valueOf(id); + Optional study = studyRepository.findById(studyId); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED); + assertThat(study).isNotEmpty(); + assertThat(study.get().getDetails()).isEqualTo(new Details("Java", "java excerpt", "java image", "OPEN", "자바 스터디 상세설명 입니다.")); + assertThat(study.get().getParticipants()).isEqualTo(Participants.createByMaxSize(10)); + assertThat(study.get().getOwner().getGithubId()).isEqualTo(1L); + assertThat(study.get().getCreatedAt()).isNotNull(); + assertThat(study.get().getPeriod()).isEqualTo( + new Period(LocalDateTime.of(2022, 8, 10, 0, 0), + LocalDateTime.of(2022, 7, 10, 0, 0), + LocalDateTime.of(2022, 10, 10, 0, 0))); + assertThat(study.get().getAttachedTags()) + .extracting("tagId").containsAnyElementsOf(openStudyRequest.getTagIds()); + } + + @DisplayName("") + @Test + void createStudyByInvalidPeriod() { + // given + + // when + + // then + } +} diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java index dcf851a98..054129bc8 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java @@ -5,12 +5,12 @@ import com.woowacourse.moamoa.common.RepositoryTest; import com.woowacourse.moamoa.member.domain.repository.MemberRepository; +import com.woowacourse.moamoa.tag.domain.repository.TagRepository; import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; import com.woowacourse.moamoa.study.domain.studytag.repository.StudyTagRepository; import com.woowacourse.moamoa.study.service.StudyDetailService; import com.woowacourse.moamoa.study.service.StudyTagService; import com.woowacourse.moamoa.study.service.response.StudiesResponse; -import com.woowacourse.moamoa.tag.domain.repository.TagRepository; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/PeriodTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/PeriodTest.java new file mode 100644 index 000000000..3ad687a54 --- /dev/null +++ b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/PeriodTest.java @@ -0,0 +1,50 @@ +package com.woowacourse.moamoa.study.domain.study; + +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; +import java.time.LocalDateTime; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class PeriodTest { + + @DisplayName("시작일자는 종료일자보다 클 수 없다.") + @Test + void startDateMustBeforeEndDate() { + assertThatThrownBy(() -> new Period(LocalDateTime.of(2022, 7, 7, 0, 0), + LocalDateTime.of(2022, 7, 10, 0, 0), + LocalDateTime.of(2022, 7, 9, 0, 0))) + .isInstanceOf(InvalidPeriodException.class); + } + + @DisplayName("모집완료는 종료일자보다 클 수 없다.") + @Test + void enrollmentDateMustBeforeEndDate() { + assertThatThrownBy(() -> new Period(LocalDateTime.of(2022, 7, 10, 0, 0), + LocalDateTime.of(2022, 7, 8, 0, 0), + LocalDateTime.of(2022, 7, 9, 0, 0))) + .isInstanceOf(InvalidPeriodException.class); + } + + @DisplayName("모집완료는 시작일자와 연관된 조건이 없다.") + @ParameterizedTest + @MethodSource("provideEnrollmentEndDateAndStartDate") + void enrollmentEndDateAndStartDateNotHasRelatedConditions(LocalDateTime enrollmentEndDate, LocalDateTime startDate) { + assertThatNoException().isThrownBy(() -> new Period(enrollmentEndDate, startDate, + LocalDateTime.of(2022, 7, 15, 0, 0))); + } + + private static Stream provideEnrollmentEndDateAndStartDate() { + return Stream.of( + Arguments.of(LocalDateTime.of(2022, 7, 10, 0, 0), LocalDateTime.of(2022, 7, 9, 0, 0)), + Arguments.of(LocalDateTime.of(2022, 7, 10, 0, 0), LocalDateTime.of(2022, 7, 10, 0, 0)), + Arguments.of(LocalDateTime.of(2022, 7, 10, 0, 0), LocalDateTime.of(2022, 7, 11, 0, 0)) + ); + } +} diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/StudyTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/StudyTest.java new file mode 100644 index 000000000..2996ed0bc --- /dev/null +++ b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/StudyTest.java @@ -0,0 +1,39 @@ +package com.woowacourse.moamoa.study.domain.study; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.woowacourse.moamoa.member.domain.Member; +import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class StudyTest { + + @DisplayName("생성일자는 스터디 시작일자보다 클 수 없다.") + @Test + void createdAtMustBeforeStartDate() { + final Details details = new Details("title", "excerpt", "thumbnail", "OPEN", "description"); + final Participants participants = Participants.createByMaxSize(10); + final Member member = new Member(1L, "username", "image", "profile"); + + assertThatThrownBy(() -> new Study(details, participants, member, + new Period(LocalDateTime.now().plusDays(10), LocalDateTime.now().minusDays(1), + LocalDateTime.now().plusDays(20)), List.of())) + .isInstanceOf(InvalidPeriodException.class); + } + + @DisplayName("생성일자는 모집완료일자보다 클 수 없다.") + @Test + void createdAtMustBeforeEnrollmentEndDate() { + final Details details = new Details("title", "excerpt", "thumbnail", "OPEN", "description"); + final Participants participants = Participants.createByMaxSize(10); + final Member member = new Member(1L, "username", "image", "profile"); + + assertThatThrownBy(() -> new Study(details, participants, member, + new Period(LocalDateTime.now().minusDays(1), LocalDateTime.now().plusDays(4), + LocalDateTime.now().plusDays(20)), List.of())) + .isInstanceOf(InvalidPeriodException.class); + } +} diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepositoryTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepositoryTest.java index fcdca890d..a8a9cbf41 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepositoryTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepositoryTest.java @@ -35,20 +35,20 @@ public void findAllByPageable(Pageable pageable, List expectedTuples, assertThat(slice.getContent()) .hasSize(expectedTuples.size()) .filteredOn(study -> study.getId() != null) - .extracting("title", "excerpt", "thumbnail", "status") + .extracting("details.title", "details.excerpt", "details.thumbnail", "details.status") .containsExactlyElementsOf(expectedTuples); } @DisplayName("키워드와 함께 페이징 정보를 사용해 스터디 목록 조회") @Test public void findByTitleContaining() { - final Slice slice = studyRepository.findByTitleContainingIgnoreCase("java", PageRequest.of(0, 3)); + final Slice slice = studyRepository.findByDetailsTitleContainingIgnoreCase("java", PageRequest.of(0, 3)); assertThat(slice.hasNext()).isFalse(); assertThat(slice.getContent()) .hasSize(2) .filteredOn(study -> study.getId() != null) - .extracting("title", "excerpt", "thumbnail", "status") + .extracting("details.title", "details.excerpt", "details.thumbnail", "details.status") .containsExactly( tuple("Java 스터디", "자바 설명", "java thumbnail", "OPEN"), tuple("javaScript 스터디", "자바스크립트 설명", "javascript thumbnail", "OPEN")); @@ -57,13 +57,13 @@ public void findByTitleContaining() { @DisplayName("빈 키워드와 함께 페이징 정보를 사용해 스터디 목록 조회") @Test public void findByBlankTitle() { - final Slice slice = studyRepository.findByTitleContainingIgnoreCase("", PageRequest.of(0, 5)); + final Slice slice = studyRepository.findByDetailsTitleContainingIgnoreCase("", PageRequest.of(0, 5)); assertThat(slice.hasNext()).isFalse(); assertThat(slice.getContent()) .hasSize(5) .filteredOn(study -> study.getId() != null) - .extracting("title", "excerpt", "thumbnail", "status") + .extracting("details.title", "details.excerpt", "details.thumbnail", "details.status") .containsExactly( tuple("Java 스터디", "자바 설명", "java thumbnail", "OPEN"), tuple("React 스터디", "리액트 설명", "react thumbnail", "OPEN"), diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryTest.java index 188e496dc..814d8461f 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; +import com.woowacourse.moamoa.study.domain.study.Details; import com.woowacourse.moamoa.tag.domain.Tag; import com.woowacourse.moamoa.tag.domain.repository.TagRepository; import com.woowacourse.moamoa.study.domain.study.Study; @@ -42,11 +43,11 @@ public void searchByFilter() { assertThat(studies).hasSize(3) .filteredOn(study -> study.getId() != null) - .extracting("title", "excerpt", "thumbnail", "status") + .extracting("details") .contains( - tuple("Java 스터디", "자바 설명", "java thumbnail", "OPEN"), - tuple("HTTP 스터디", "HTTP 설명", "http thumbnail", "CLOSE"), - tuple("알고리즘 스터디", "알고리즘 설명", "algorithm thumbnail", "CLOSE") + new Details("Java 스터디", "자바 설명", "java thumbnail", "OPEN", "그린론의 우당탕탕 자바 스터디입니다."), + new Details("HTTP 스터디", "HTTP 설명", "http thumbnail", "CLOSE", "디우의 HTTP 정복하기"), + new Details("알고리즘 스터디", "알고리즘 설명", "algorithm thumbnail", "CLOSE", "알고리즘을 TDD로 풀자의 베루스입니다.") ); assertThat(hasNext).isFalse(); } @@ -71,7 +72,7 @@ public void searchByFilterSameCategory() { assertThat(hasNext).isFalse(); assertThat(studies).hasSize(5) .filteredOn(study -> study.getId() != null) - .extracting("title", "excerpt", "thumbnail", "status") + .extracting("details.title", "details.excerpt", "details.thumbnail", "details.status") .contains( tuple("Java 스터디", "자바 설명", "java thumbnail", "OPEN"), tuple("React 스터디", "리액트 설명", "react thumbnail", "OPEN"), @@ -99,7 +100,7 @@ public void searchByFilterWithAnotherCategory() { assertThat(studies).hasSize(1) .filteredOn(study -> study.getId() != null) - .extracting("title", "excerpt", "thumbnail", "status") + .extracting("details.title", "details.excerpt", "details.thumbnail", "details.status") .contains(tuple("Java 스터디", "자바 설명", "java thumbnail", "OPEN")); assertThat(hasNext).isFalse(); } @@ -122,7 +123,7 @@ public void searchByTitleAndFilter() { assertThat(studies).hasSize(1) .filteredOn(study -> study.getId() != null) - .extracting("title", "excerpt", "thumbnail", "status") + .extracting("details.title", "details.excerpt", "details.thumbnail", "details.status") .contains(tuple("Java 스터디", "자바 설명", "java thumbnail", "OPEN")); assertThat(hasNext).isFalse(); } @@ -148,7 +149,7 @@ public void searchByFilters() { assertThat(studies).hasSize(5) .filteredOn(study -> study.getId() != null) - .extracting("title", "excerpt", "thumbnail", "status") + .extracting("details.title", "details.excerpt", "details.thumbnail", "details.status") .contains( tuple("Java 스터디", "자바 설명", "java thumbnail", "OPEN"), tuple("React 스터디", "리액트 설명", "react thumbnail", "OPEN"), @@ -180,7 +181,7 @@ public void searchByFiltersWithTitle() { assertThat(studies).hasSize(2) .filteredOn(study -> study.getId() != null) - .extracting("title", "excerpt", "thumbnail", "status") + .extracting("details.title", "details.excerpt", "details.thumbnail", "details.status") .contains( tuple("Java 스터디", "자바 설명", "java thumbnail", "OPEN"), tuple("javaScript 스터디", "자바스크립트 설명", "javascript thumbnail", "OPEN") diff --git a/backend/src/test/resources/data.sql b/backend/src/test/resources/data.sql index dc7bb58c8..b6e09eec6 100644 --- a/backend/src/test/resources/data.sql +++ b/backend/src/test/resources/data.sql @@ -8,19 +8,19 @@ INSERT INTO member(github_id, username, image_url, profile_url) VALUES (4L, 'verus', 'https://image', 'github.com'); INSERT INTO study(id, title, excerpt, thumbnail, status, description, current_member_count, max_member_count, created_at, owner_id) -VALUES (1, 'Java 스터디', '자바 설명', 'java thumbnail', 'OPEN', '그린론의 우당탕탕 자바 스터디입니다.', 3, 10, '2021-11-08T11:58:20.551705', 2); +VALUES (null, 'Java 스터디', '자바 설명', 'java thumbnail', 'OPEN', '그린론의 우당탕탕 자바 스터디입니다.', 3, 10, '2021-11-08T11:58:20.551705', 2); INSERT INTO study(id, title, excerpt, thumbnail, status, description, current_member_count, max_member_count, created_at, enrollment_end_date, start_date, end_date, owner_id) -VALUES (2, 'React 스터디', '리액트 설명', 'react thumbnail', 'OPEN', '디우의 뤼액트 스터디입니다.', 4, 5, '2021-11-08T11:58:20.551705', '2021-11-09T11:58:20.551705', '2021-11-10T11:58:20.551705', '2021-12-08T11:58:20.551705', 3); +VALUES (null, 'React 스터디', '리액트 설명', 'react thumbnail', 'OPEN', '디우의 뤼액트 스터디입니다.', 4, 5, '2021-11-08T11:58:20.551705', '2021-11-09T11:58:20.551705', '2021-11-10T11:58:20.551705', '2021-12-08T11:58:20.551705', 3); INSERT INTO study(id, title, excerpt, thumbnail, status, description, current_member_count, max_member_count, created_at, owner_id) -VALUES (3, 'javaScript 스터디', '자바스크립트 설명', 'javascript thumbnail', 'OPEN', '그린론의 자바스크립트 접해보기', 3, 20, '2021-11-08T11:58:20.551705', 2); +VALUES (null, 'javaScript 스터디', '자바스크립트 설명', 'javascript thumbnail', 'OPEN', '그린론의 자바스크립트 접해보기', 3, 20, '2021-11-08T11:58:20.551705', 2); INSERT INTO study(id, title, excerpt, thumbnail, status, description, max_member_count, created_at, owner_id) -VALUES (4, 'HTTP 스터디', 'HTTP 설명', 'http thumbnail', 'CLOSE', '디우의 HTTP 정복하기', 5, '2021-11-08T11:58:20.551705', 3); +VALUES (null, 'HTTP 스터디', 'HTTP 설명', 'http thumbnail', 'CLOSE', '디우의 HTTP 정복하기', 5, '2021-11-08T11:58:20.551705', 3); INSERT INTO study(id, title, excerpt, thumbnail, status, description, max_member_count, created_at, owner_id) -VALUES (5, '알고리즘 스터디', '알고리즘 설명', 'algorithm thumbnail', 'CLOSE', '알고리즘을 TDD로 풀자의 베루스입니다.', 2, '2021-11-08T11:58:20.551705', 4); +VALUES (null, '알고리즘 스터디', '알고리즘 설명', 'algorithm thumbnail', 'CLOSE', '알고리즘을 TDD로 풀자의 베루스입니다.', 2, '2021-11-08T11:58:20.551705', 4); INSERT INTO category(id, name) VALUES (1, 'generation'); INSERT INTO category(id, name) VALUES (2, 'area'); diff --git a/backend/src/test/resources/init.sql b/backend/src/test/resources/init.sql new file mode 100644 index 000000000..e69de29bb From 625c90102f177e7b1588fafd3d6328a9305ef3f9 Mon Sep 17 00:00:00 2001 From: SeungCheol Date: Thu, 21 Jul 2022 21:56:10 +0900 Subject: [PATCH 5/6] =?UTF-8?q?test:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20Perio?= =?UTF-8?q?d=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/moamoa/study/domain/study/Period.java | 4 ++-- .../acceptance/study/CreatingStudyAcceptanceTest.java | 5 ++++- .../study/controller/CreatingStudyControllerTest.java | 11 ++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java index bcd84d6dd..b74b98987 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java @@ -20,7 +20,7 @@ public Period() { } public Period(final LocalDateTime enrollmentEndDate, final LocalDateTime startDate, final LocalDateTime endDate) { - if (startDate.isAfter(endDate) || enrollmentEndDate.isAfter(endDate)) { + if ((endDate != null && startDate.isAfter(endDate)) || (enrollmentEndDate != null && enrollmentEndDate.isAfter(endDate))) { throw new InvalidPeriodException(); } this.enrollmentEndDate = enrollmentEndDate; @@ -29,7 +29,7 @@ public Period(final LocalDateTime enrollmentEndDate, final LocalDateTime startDa } public boolean isBefore(final LocalDateTime createAt) { - return startDate.isBefore(createAt) || enrollmentEndDate.isBefore(createAt); + return startDate.isBefore(createAt) || (enrollmentEndDate != null && enrollmentEndDate.isBefore(createAt)); } public LocalDateTime getEnrollmentEndDate() { diff --git a/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java b/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java index 20c3959b0..ce7e9a35a 100644 --- a/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java +++ b/backend/src/test/java/com/woowacourse/acceptance/study/CreatingStudyAcceptanceTest.java @@ -5,6 +5,8 @@ import com.woowacourse.acceptance.AcceptanceTest; import com.woowacourse.moamoa.auth.service.oauthclient.response.GithubProfileResponse; import io.restassured.RestAssured; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -133,7 +135,8 @@ void createStudy() { .header(HttpHeaders.AUTHORIZATION, jwtToken) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .body(Map.of("title", "제목", "excerpt", "자바를 공부하는 스터디", "thumbnail", "image", - "description", "스터디 상세 설명입니다.", "startDate", "2022-07-12", "endDate", "")) + "description", "스터디 상세 설명입니다.", "startDate", LocalDate.now().plusDays(5).format( + DateTimeFormatter.ofPattern("yyyy-MM-dd")), "endDate", "")) .when().log().all() .post("/api/studies") .then().log().all() diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java index 1dd10738e..09663dfe8 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java @@ -12,7 +12,6 @@ import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; import com.woowacourse.moamoa.study.service.CreateStudyService; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.DisplayName; @@ -40,9 +39,9 @@ void openStudy() { .excerpt("java excerpt") .thumbnail("java image") .description("자바 스터디 상세설명 입니다.") - .startDate(LocalDate.of(2022, 7, 10)) - .endDate(LocalDate.of(2022, 10, 10)) - .enrollmentEndDate(LocalDate.of(2022, 8, 10)) + .startDate(LocalDate.now().plusDays(3)) + .endDate(LocalDate.now().plusDays(4)) + .enrollmentEndDate(LocalDate.now().plusDays(2)) .maxMemberCount(10) .tagIds(List.of(1L, 2L)) .build(); @@ -61,9 +60,7 @@ void openStudy() { assertThat(study.get().getOwner().getGithubId()).isEqualTo(1L); assertThat(study.get().getCreatedAt()).isNotNull(); assertThat(study.get().getPeriod()).isEqualTo( - new Period(LocalDateTime.of(2022, 8, 10, 0, 0), - LocalDateTime.of(2022, 7, 10, 0, 0), - LocalDateTime.of(2022, 10, 10, 0, 0))); + new Period(openStudyRequest.getEnrollmentEndDateTime(), openStudyRequest.getStartDateTime(), openStudyRequest.getEndDateTime())); assertThat(study.get().getAttachedTags()) .extracting("tagId").containsAnyElementsOf(openStudyRequest.getTagIds()); } From 5c06a6ab900706b5346bf8a0d9b391b20bbcd89a Mon Sep 17 00:00:00 2001 From: SeungCheol Date: Fri, 22 Jul 2022 11:11:03 +0900 Subject: [PATCH 6/6] =?UTF-8?q?refactor:=20=EC=8B=A4=ED=8C=A8=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/advice/CommonControllerAdvice.java | 8 ++- .../moamoa/member/domain/Member.java | 2 +- .../controller/CreatingStudyController.java | 2 +- .../study/domain/{study => }/AttachedTag.java | 2 +- .../study/domain/{study => }/Details.java | 2 +- .../domain/{study => }/Participants.java | 3 +- .../study/domain/{study => }/Period.java | 2 +- .../study/domain/{study => }/Study.java | 3 +- .../repository/JpaStudyRepository.java | 4 +- .../repository/StudyRepository.java | 4 +- .../study/domain/study/Participant.java | 19 ------- .../study/domain/studytag/AttachedTag.java | 19 ------- .../study/domain/studytag/StudySlice.java | 2 +- .../study/domain/studytag/StudyTag.java | 2 +- .../CustomStudyTagRepositoryImpl.java | 4 +- .../study/service/CreateStudyService.java | 16 +++--- .../study/service/StudyDetailService.java | 6 +-- .../moamoa/study/service/StudyTagService.java | 4 +- .../service/response/StudyDetailResponse.java | 6 +-- .../study/service/response/StudyResponse.java | 2 +- .../CreatingStudyControllerTest.java | 51 ++++++++++++++++--- .../study/controller/StudyControllerTest.java | 2 +- .../repository/StudyRepositoryTest.java | 4 +- .../moamoa/study/domain/study/PeriodTest.java | 1 + .../moamoa/study/domain/study/StudyTest.java | 4 ++ .../CustomStudyTagRepositoryTest.java | 4 +- .../study/service/StudyDetailServiceTest.java | 4 +- backend/src/test/resources/data.sql | 4 +- 28 files changed, 95 insertions(+), 91 deletions(-) rename backend/src/main/java/com/woowacourse/moamoa/study/domain/{study => }/AttachedTag.java (88%) rename backend/src/main/java/com/woowacourse/moamoa/study/domain/{study => }/Details.java (97%) rename backend/src/main/java/com/woowacourse/moamoa/study/domain/{study => }/Participants.java (94%) rename backend/src/main/java/com/woowacourse/moamoa/study/domain/{study => }/Period.java (97%) rename backend/src/main/java/com/woowacourse/moamoa/study/domain/{study => }/Study.java (96%) rename backend/src/main/java/com/woowacourse/moamoa/study/domain/{study => }/repository/JpaStudyRepository.java (57%) rename backend/src/main/java/com/woowacourse/moamoa/study/domain/{study => }/repository/StudyRepository.java (75%) delete mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participant.java delete mode 100644 backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/AttachedTag.java rename backend/src/test/java/com/woowacourse/moamoa/study/domain/{study => }/repository/StudyRepositoryTest.java (97%) diff --git a/backend/src/main/java/com/woowacourse/moamoa/common/advice/CommonControllerAdvice.java b/backend/src/main/java/com/woowacourse/moamoa/common/advice/CommonControllerAdvice.java index 6dd638b3f..37ffdefd4 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/common/advice/CommonControllerAdvice.java +++ b/backend/src/main/java/com/woowacourse/moamoa/common/advice/CommonControllerAdvice.java @@ -3,6 +3,7 @@ import com.woowacourse.moamoa.common.exception.UnauthorizedException; import com.woowacourse.moamoa.common.advice.response.ErrorResponse; import com.woowacourse.moamoa.common.exception.InvalidFormatException; +import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -11,8 +12,11 @@ @RestControllerAdvice public class CommonControllerAdvice { - @ExceptionHandler - public ResponseEntity handleBadRequest(final InvalidFormatException e) { + @ExceptionHandler({ + InvalidFormatException.class, + InvalidPeriodException.class + }) + public ResponseEntity handleBadRequest(final Exception e) { return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); } diff --git a/backend/src/main/java/com/woowacourse/moamoa/member/domain/Member.java b/backend/src/main/java/com/woowacourse/moamoa/member/domain/Member.java index 9e80fd8ea..299295742 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/member/domain/Member.java +++ b/backend/src/main/java/com/woowacourse/moamoa/member/domain/Member.java @@ -6,7 +6,7 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import java.util.ArrayList; import java.util.List; import javax.persistence.OneToMany; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java b/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java index 4d110bc9a..1c4c9deda 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/controller/CreatingStudyController.java @@ -2,7 +2,7 @@ import com.woowacourse.moamoa.auth.config.AuthenticationPrincipal; import com.woowacourse.moamoa.study.controller.request.OpenStudyRequest; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import com.woowacourse.moamoa.study.service.CreateStudyService; import java.net.URI; import javax.validation.Valid; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/AttachedTag.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/AttachedTag.java similarity index 88% rename from backend/src/main/java/com/woowacourse/moamoa/study/domain/study/AttachedTag.java rename to backend/src/main/java/com/woowacourse/moamoa/study/domain/AttachedTag.java index 46da435f7..9c27e56e9 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/AttachedTag.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/AttachedTag.java @@ -1,4 +1,4 @@ -package com.woowacourse.moamoa.study.domain.study; +package com.woowacourse.moamoa.study.domain; import static lombok.AccessLevel.PROTECTED; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Details.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Details.java similarity index 97% rename from backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Details.java rename to backend/src/main/java/com/woowacourse/moamoa/study/domain/Details.java index 1d93f7377..fa04a3832 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Details.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Details.java @@ -1,4 +1,4 @@ -package com.woowacourse.moamoa.study.domain.study; +package com.woowacourse.moamoa.study.domain; import java.util.Objects; import javax.persistence.Column; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participants.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Participants.java similarity index 94% rename from backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participants.java rename to backend/src/main/java/com/woowacourse/moamoa/study/domain/Participants.java index f95239f36..e94f13984 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participants.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Participants.java @@ -1,6 +1,5 @@ -package com.woowacourse.moamoa.study.domain.study; +package com.woowacourse.moamoa.study.domain; -import com.woowacourse.moamoa.study.domain.Participant; import java.util.ArrayList; import java.util.List; import java.util.Objects; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Period.java similarity index 97% rename from backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java rename to backend/src/main/java/com/woowacourse/moamoa/study/domain/Period.java index b74b98987..2c9141168 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Period.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Period.java @@ -1,4 +1,4 @@ -package com.woowacourse.moamoa.study.domain.study; +package com.woowacourse.moamoa.study.domain; import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; import java.time.LocalDateTime; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Study.java similarity index 96% rename from backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java rename to backend/src/main/java/com/woowacourse/moamoa/study/domain/Study.java index 0bf0b12fc..76f004724 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Study.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/Study.java @@ -1,11 +1,10 @@ -package com.woowacourse.moamoa.study.domain.study; +package com.woowacourse.moamoa.study.domain; import static javax.persistence.FetchType.LAZY; import static javax.persistence.GenerationType.IDENTITY; import com.woowacourse.moamoa.member.domain.Member; import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; -import com.woowacourse.moamoa.study.domain.studytag.AttachedTag; import com.woowacourse.moamoa.study.domain.studytag.StudyTag; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/JpaStudyRepository.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/repository/JpaStudyRepository.java similarity index 57% rename from backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/JpaStudyRepository.java rename to backend/src/main/java/com/woowacourse/moamoa/study/domain/repository/JpaStudyRepository.java index 39cb41d0a..c4e6bee2c 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/JpaStudyRepository.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/repository/JpaStudyRepository.java @@ -1,6 +1,6 @@ -package com.woowacourse.moamoa.study.domain.study.repository; +package com.woowacourse.moamoa.study.domain.repository; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import org.springframework.data.jpa.repository.JpaRepository; public interface JpaStudyRepository extends JpaRepository, StudyRepository { diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepository.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/repository/StudyRepository.java similarity index 75% rename from backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepository.java rename to backend/src/main/java/com/woowacourse/moamoa/study/domain/repository/StudyRepository.java index 10427617b..2759b39b0 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepository.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/repository/StudyRepository.java @@ -1,6 +1,6 @@ -package com.woowacourse.moamoa.study.domain.study.repository; +package com.woowacourse.moamoa.study.domain.repository; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import java.util.Optional; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participant.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participant.java deleted file mode 100644 index aae0dc4d3..000000000 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/study/Participant.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.woowacourse.moamoa.study.domain.study; - -import static lombok.AccessLevel.PROTECTED; - -import javax.persistence.Column; -import javax.persistence.Embeddable; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Embeddable -@NoArgsConstructor(access = PROTECTED) -@AllArgsConstructor -public class Participant { - - @Column(name = "member_id", nullable = false) - private Long memberId; -} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/AttachedTag.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/AttachedTag.java deleted file mode 100644 index 8de4609a1..000000000 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/AttachedTag.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.woowacourse.moamoa.study.domain.studytag; - -import static lombok.AccessLevel.PROTECTED; - -import javax.persistence.Column; -import javax.persistence.Embeddable; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Embeddable -@NoArgsConstructor(access = PROTECTED) -@AllArgsConstructor -@Getter -public class AttachedTag { - - @Column(name = "tag_id", nullable = false) - private Long tagId; -} diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/StudySlice.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/StudySlice.java index 82e9b24d2..9bf6ba71a 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/StudySlice.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/StudySlice.java @@ -1,6 +1,6 @@ package com.woowacourse.moamoa.study.domain.studytag; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import java.util.List; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/StudyTag.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/StudyTag.java index 037bea3cc..9671431c6 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/StudyTag.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/StudyTag.java @@ -4,7 +4,7 @@ import static lombok.AccessLevel.PROTECTED; import com.woowacourse.moamoa.tag.domain.Tag; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryImpl.java b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryImpl.java index 2a05ccd1d..7ebd56dee 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryImpl.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryImpl.java @@ -1,6 +1,6 @@ package com.woowacourse.moamoa.study.domain.studytag.repository; -import static com.woowacourse.moamoa.study.domain.study.QStudy.study; +import static com.woowacourse.moamoa.study.domain.QStudy.study; import static com.woowacourse.moamoa.study.domain.studytag.QStudyTag.studyTag; import static com.woowacourse.moamoa.tag.domain.QTag.tag; import static java.util.stream.Collectors.toList; @@ -9,7 +9,7 @@ import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Predicate; import com.querydsl.jpa.impl.JPAQueryFactory; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import com.woowacourse.moamoa.study.domain.studytag.StudySearchCondition; import com.woowacourse.moamoa.study.domain.studytag.StudySlice; import com.woowacourse.moamoa.tag.domain.Category; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/CreateStudyService.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/CreateStudyService.java index 1766f109c..9735edf50 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/CreateStudyService.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/CreateStudyService.java @@ -1,14 +1,15 @@ package com.woowacourse.moamoa.study.service; +import com.woowacourse.moamoa.common.exception.UnauthorizedException; import com.woowacourse.moamoa.member.domain.Member; import com.woowacourse.moamoa.member.domain.repository.MemberRepository; import com.woowacourse.moamoa.study.controller.request.OpenStudyRequest; -import com.woowacourse.moamoa.study.domain.study.Details; -import com.woowacourse.moamoa.study.domain.study.Participants; -import com.woowacourse.moamoa.study.domain.study.Period; -import com.woowacourse.moamoa.study.domain.study.Study; -import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; -import com.woowacourse.moamoa.study.domain.studytag.AttachedTag; +import com.woowacourse.moamoa.study.domain.AttachedTag; +import com.woowacourse.moamoa.study.domain.Details; +import com.woowacourse.moamoa.study.domain.Participants; +import com.woowacourse.moamoa.study.domain.Period; +import com.woowacourse.moamoa.study.domain.Study; +import com.woowacourse.moamoa.study.domain.repository.StudyRepository; import java.util.List; import java.util.stream.Collectors; import org.springframework.stereotype.Service; @@ -33,8 +34,7 @@ public Study createStudy(final Long githubId, final OpenStudyRequest openStudyRe .map(AttachedTag::new) .collect(Collectors.toList()); - final Member member = memberRepository.findByGithubId(githubId).get(); - + final Member member = memberRepository.findByGithubId(githubId).orElseThrow(() -> new UnauthorizedException("")); final Participants participants = Participants.createByMaxSize(openStudyRequest.getMaxMemberCount()); final Details details = new Details(openStudyRequest.getTitle(), openStudyRequest.getExcerpt(), openStudyRequest.getThumbnail(), "OPEN", openStudyRequest.getDescription()); diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java index e5f4206b3..785254306 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyDetailService.java @@ -6,9 +6,9 @@ import com.woowacourse.moamoa.member.domain.repository.MemberRepository; import com.woowacourse.moamoa.member.service.response.MemberResponse; import com.woowacourse.moamoa.study.domain.Participant; -import com.woowacourse.moamoa.study.domain.study.Study; -import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; -import com.woowacourse.moamoa.study.domain.studytag.AttachedTag; +import com.woowacourse.moamoa.study.domain.AttachedTag; +import com.woowacourse.moamoa.study.domain.Study; +import com.woowacourse.moamoa.study.domain.repository.StudyRepository; import com.woowacourse.moamoa.study.exception.StudyNotExistException; import com.woowacourse.moamoa.study.service.response.StudyDetailResponse; import com.woowacourse.moamoa.tag.domain.Tag; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyTagService.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyTagService.java index ffa4c4609..bd5db15f7 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyTagService.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/StudyTagService.java @@ -6,8 +6,8 @@ import com.woowacourse.moamoa.tag.domain.repository.TagRepository; import com.woowacourse.moamoa.tag.exception.TagNotExistException; import com.woowacourse.moamoa.study.controller.request.TagRequest; -import com.woowacourse.moamoa.study.domain.study.Study; -import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; +import com.woowacourse.moamoa.study.domain.Study; +import com.woowacourse.moamoa.study.domain.repository.StudyRepository; import com.woowacourse.moamoa.study.service.response.StudiesResponse; import com.woowacourse.moamoa.study.service.response.StudyResponse; import com.woowacourse.moamoa.study.domain.studytag.StudySearchCondition; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyDetailResponse.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyDetailResponse.java index 27909e4ee..428a043fb 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyDetailResponse.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyDetailResponse.java @@ -1,7 +1,7 @@ package com.woowacourse.moamoa.study.service.response; import com.woowacourse.moamoa.member.service.response.MemberResponse; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import com.woowacourse.moamoa.tag.query.response.TagResponse; import java.time.LocalDateTime; import java.util.List; @@ -44,9 +44,9 @@ public StudyDetailResponse(final Study study, final MemberResponse owner, final this.currentMemberCount = study.getParticipants().getSize(); this.maxMemberCount = study.getParticipants().getMax(); this.createdAt = getNullableDate(study.getCreatedAt()); - this.enrollmentEndDate = getNullableDate(study.getPeriod().getEnrollmentEndDate()); + this.enrollmentEndDate = getNullableDate(study.getPeriod() == null ? null : study.getPeriod().getEnrollmentEndDate()); this.startDate = getNullableDate(study.getPeriod().getStartDate()); - this.endDate = getNullableDate(study.getPeriod().getEndDate()); + this.endDate = getNullableDate(study.getPeriod().getEndDate() == null ? null : study.getPeriod().getEndDate()); this.owner = owner; this.members = membersResponse; this.tags = attachedTags; diff --git a/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyResponse.java b/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyResponse.java index 41eef0d34..d3166ab51 100644 --- a/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyResponse.java +++ b/backend/src/main/java/com/woowacourse/moamoa/study/service/response/StudyResponse.java @@ -1,6 +1,6 @@ package com.woowacourse.moamoa.study.service.response; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java index 09663dfe8..07abadf59 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/controller/CreatingStudyControllerTest.java @@ -1,15 +1,18 @@ package com.woowacourse.moamoa.study.controller; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.woowacourse.moamoa.common.RepositoryTest; +import com.woowacourse.moamoa.common.exception.UnauthorizedException; import com.woowacourse.moamoa.member.domain.repository.MemberRepository; import com.woowacourse.moamoa.study.controller.request.OpenStudyRequest; -import com.woowacourse.moamoa.study.domain.study.Details; -import com.woowacourse.moamoa.study.domain.study.Participants; -import com.woowacourse.moamoa.study.domain.study.Period; -import com.woowacourse.moamoa.study.domain.study.Study; -import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; +import com.woowacourse.moamoa.study.domain.Details; +import com.woowacourse.moamoa.study.domain.Participants; +import com.woowacourse.moamoa.study.domain.Period; +import com.woowacourse.moamoa.study.domain.Study; +import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; +import com.woowacourse.moamoa.study.domain.repository.StudyRepository; import com.woowacourse.moamoa.study.service.CreateStudyService; import java.time.LocalDate; import java.util.List; @@ -65,13 +68,45 @@ void openStudy() { .extracting("tagId").containsAnyElementsOf(openStudyRequest.getTagIds()); } - @DisplayName("") + @DisplayName("유효하지 않은 스터디 기간으로 생성 시 예외 발생") @Test void createStudyByInvalidPeriod() { - // given + CreatingStudyController sut = new CreatingStudyController(new CreateStudyService(memberRepository, studyRepository)); + final OpenStudyRequest openStudyRequest = OpenStudyRequest.builder() + .title("Java") + .excerpt("java excerpt") + .thumbnail("java image") + .description("자바 스터디 상세설명 입니다.") + .startDate(LocalDate.now().minusDays(1)) + .endDate(LocalDate.now().plusDays(4)) + .enrollmentEndDate(LocalDate.now().plusDays(2)) + .maxMemberCount(10) + .tagIds(List.of(1L, 2L)) + .build(); // when + assertThatThrownBy(() -> sut.createStudy(1L, openStudyRequest)) + .isInstanceOf(InvalidPeriodException.class); + } - // then + @DisplayName("존재하지 않은 사용자로 생성 시 예외 발생") + @Test + void createStudyByNotFoundUser() { + CreatingStudyController sut = new CreatingStudyController(new CreateStudyService(memberRepository, studyRepository)); + final OpenStudyRequest openStudyRequest = OpenStudyRequest.builder() + .title("Java") + .excerpt("java excerpt") + .thumbnail("java image") + .description("자바 스터디 상세설명 입니다.") + .startDate(LocalDate.now().plusDays(1)) + .endDate(LocalDate.now().plusDays(4)) + .enrollmentEndDate(LocalDate.now().plusDays(2)) + .maxMemberCount(10) + .tagIds(List.of(1L, 2L)) + .build(); + + // when + assertThatThrownBy(() -> sut.createStudy(100L, openStudyRequest)) // 존재하지 않는 사용자로 추가 시 예외 발생 + .isInstanceOf(UnauthorizedException.class); } } diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java index 054129bc8..e6d26cad7 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/controller/StudyControllerTest.java @@ -6,7 +6,7 @@ import com.woowacourse.moamoa.common.RepositoryTest; import com.woowacourse.moamoa.member.domain.repository.MemberRepository; import com.woowacourse.moamoa.tag.domain.repository.TagRepository; -import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; +import com.woowacourse.moamoa.study.domain.repository.StudyRepository; import com.woowacourse.moamoa.study.domain.studytag.repository.StudyTagRepository; import com.woowacourse.moamoa.study.service.StudyDetailService; import com.woowacourse.moamoa.study.service.StudyTagService; diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepositoryTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/domain/repository/StudyRepositoryTest.java similarity index 97% rename from backend/src/test/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepositoryTest.java rename to backend/src/test/java/com/woowacourse/moamoa/study/domain/repository/StudyRepositoryTest.java index a8a9cbf41..e89227e54 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/repository/StudyRepositoryTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/domain/repository/StudyRepositoryTest.java @@ -1,9 +1,9 @@ -package com.woowacourse.moamoa.study.domain.study.repository; +package com.woowacourse.moamoa.study.domain.repository; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import java.util.List; import java.util.stream.Stream; import org.assertj.core.groups.Tuple; diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/PeriodTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/PeriodTest.java index 3ad687a54..0c7b6c83d 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/PeriodTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/PeriodTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThatNoException; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.woowacourse.moamoa.study.domain.Period; import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; import java.time.LocalDateTime; import java.util.stream.Stream; diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/StudyTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/StudyTest.java index 2996ed0bc..420e85ef1 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/StudyTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/domain/study/StudyTest.java @@ -3,6 +3,10 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.woowacourse.moamoa.member.domain.Member; +import com.woowacourse.moamoa.study.domain.Details; +import com.woowacourse.moamoa.study.domain.Participants; +import com.woowacourse.moamoa.study.domain.Period; +import com.woowacourse.moamoa.study.domain.Study; import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException; import java.time.LocalDateTime; import java.util.List; diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryTest.java index 814d8461f..38a003e48 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/domain/studytag/repository/CustomStudyTagRepositoryTest.java @@ -3,10 +3,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; -import com.woowacourse.moamoa.study.domain.study.Details; +import com.woowacourse.moamoa.study.domain.Details; import com.woowacourse.moamoa.tag.domain.Tag; import com.woowacourse.moamoa.tag.domain.repository.TagRepository; -import com.woowacourse.moamoa.study.domain.study.Study; +import com.woowacourse.moamoa.study.domain.Study; import com.woowacourse.moamoa.study.domain.studytag.StudySearchCondition; import com.woowacourse.moamoa.study.domain.studytag.StudySlice; import java.util.List; diff --git a/backend/src/test/java/com/woowacourse/moamoa/study/service/StudyDetailServiceTest.java b/backend/src/test/java/com/woowacourse/moamoa/study/service/StudyDetailServiceTest.java index acf41a239..15b0ec2a6 100644 --- a/backend/src/test/java/com/woowacourse/moamoa/study/service/StudyDetailServiceTest.java +++ b/backend/src/test/java/com/woowacourse/moamoa/study/service/StudyDetailServiceTest.java @@ -6,7 +6,7 @@ import com.woowacourse.moamoa.common.RepositoryTest; import com.woowacourse.moamoa.member.domain.repository.MemberRepository; import com.woowacourse.moamoa.member.service.response.MemberResponse; -import com.woowacourse.moamoa.study.domain.study.repository.StudyRepository; +import com.woowacourse.moamoa.study.domain.repository.StudyRepository; import com.woowacourse.moamoa.study.service.response.StudyDetailResponse; import com.woowacourse.moamoa.tag.domain.repository.TagRepository; import com.woowacourse.moamoa.tag.query.response.TagResponse; @@ -50,7 +50,7 @@ public void getStudyDetails() { .extracting("title", "excerpt", "thumbnail", "status", "description", "currentMemberCount", "maxMemberCount", "createdAt", "enrollmentEndDate", "startDate", "endDate") .containsExactly("Java 스터디", "자바 설명", "java thumbnail", "OPEN", "그린론의 우당탕탕 자바 스터디입니다.", 3, 10, - "2021-11-08", "", "", ""); + "2021-11-08", "", "2021-12-08", ""); assertThat(owner).extracting("githubId", "username", "imageUrl", "profileUrl") .containsExactly(2L, "greenlawn", "https://image", "github.com"); diff --git a/backend/src/test/resources/data.sql b/backend/src/test/resources/data.sql index b6e09eec6..89ad57fe2 100644 --- a/backend/src/test/resources/data.sql +++ b/backend/src/test/resources/data.sql @@ -7,8 +7,8 @@ VALUES (3L, 'dwoo', 'https://image', 'github.com'); INSERT INTO member(github_id, username, image_url, profile_url) VALUES (4L, 'verus', 'https://image', 'github.com'); -INSERT INTO study(id, title, excerpt, thumbnail, status, description, current_member_count, max_member_count, created_at, owner_id) -VALUES (null, 'Java 스터디', '자바 설명', 'java thumbnail', 'OPEN', '그린론의 우당탕탕 자바 스터디입니다.', 3, 10, '2021-11-08T11:58:20.551705', 2); +INSERT INTO study(id, title, excerpt, thumbnail, status, description, current_member_count, max_member_count, created_at, start_date, owner_id) +VALUES (null, 'Java 스터디', '자바 설명', 'java thumbnail', 'OPEN', '그린론의 우당탕탕 자바 스터디입니다.', 3, 10, '2021-11-08T11:58:20.551705', '2021-12-08T11:58:20.657123', 2); INSERT INTO study(id, title, excerpt, thumbnail, status, description, current_member_count, max_member_count, created_at, enrollment_end_date, start_date, end_date, owner_id) VALUES (null, 'React 스터디', '리액트 설명', 'react thumbnail', 'OPEN', '디우의 뤼액트 스터디입니다.', 4, 5, '2021-11-08T11:58:20.551705', '2021-11-09T11:58:20.551705', '2021-11-10T11:58:20.551705', '2021-12-08T11:58:20.551705', 3);