diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index 87ba6ea3..62b4f7fa 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -37,11 +37,3 @@ jobs: if: always() with: junit_files: ${{ github.workspace }}/backend/build/test-results/**/*.xml - - - name: Add coverage to PR - id: jacoco - uses: madrapps/jacoco-report@v1.2 - with: - paths: ${{ github.workspace }}/backend/build/jacoco/index.xml - token: ${{ secrets.GITHUB_TOKEN }} - diff --git a/README.md b/README.md index fcb6244e..edc5df97 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ 달력이 기록을 공유할 때, 달록 🌙 -[](https://dallog.me) [](https://dallog.github.io) [](https://github.com/woowacourse-teams/2022-dallog/releases/tag/v0.1.2) +[](https://dallog.me) [](https://dallog.github.io) [](https://github.com/woowacourse-teams/2022-dallog/releases/tag/v1.0.2) [](https://dallog.me) diff --git a/backend/build.gradle b/backend/build.gradle index a58d4cc9..940d613a 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -24,7 +24,6 @@ repositories { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/backend/src/docs/asciidoc/auth.adoc b/backend/src/docs/asciidoc/auth.adoc index c785188a..c5dc3979 100644 --- a/backend/src/docs/asciidoc/auth.adoc +++ b/backend/src/docs/asciidoc/auth.adoc @@ -2,58 +2,50 @@ === OAuth 로그인 링크 생성 -==== Request +==== HTTP Request -include::{snippets}/auth/link/http-request.adoc[] +include::{snippets}/auth/generateLink/http-request.adoc[] ==== Path Parameters -include::{snippets}/auth/link/path-parameters.adoc[] +include::{snippets}/auth/generateLink/path-parameters.adoc[] ==== Request Parameters -include::{snippets}/auth/link/request-parameters.adoc[] +include::{snippets}/auth/generateLink/request-parameters.adoc[] -==== Response +==== HTTP Response -include::{snippets}/auth/link/http-response.adoc[] +include::{snippets}/auth/generateLink/http-response.adoc[] -==== ResponseFields +==== Response Fields -include::{snippets}/auth/link/response-fields.adoc[] +include::{snippets}/auth/generateLink/response-fields.adoc[] === OAuth 로그인 -==== Request +==== HTTP Request -include::{snippets}/auth/token/http-request.adoc[] +include::{snippets}/auth/generateToken/http-request.adoc[] -==== PathParameters - -include::{snippets}/auth/token/path-parameters.adoc[] - -==== RequestFields - -include::{snippets}/auth/token/request-fields.adoc[] - -==== Response +==== Path Parameters -include::{snippets}/auth/token/http-response.adoc[] +include::{snippets}/auth/generateToken/path-parameters.adoc[] -=== OAuth 로그인 : Resource Server 에러 +==== Request Fields -==== Request +include::{snippets}/auth/generateToken/request-fields.adoc[] -include::{snippets}/auth/exception/token/http-request.adoc[] +==== HTTP Response -==== PathParameters +include::{snippets}/auth/generateToken/http-response.adoc[] -include::{snippets}/auth/exception/token/path-parameters.adoc[] +==== Response Fields -==== RequestFields +include::{snippets}/auth/generateToken/response-fields.adoc[] -include::{snippets}/auth/exception/token/request-fields.adoc[] +=== OAuth 로그인 (Resource Server 에러) -==== Response +==== HTTP Response -include::{snippets}/auth/exception/token/http-response.adoc[] +include::{snippets}/auth/generateToken/failByResourceServerError/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/category.adoc b/backend/src/docs/asciidoc/category.adoc index 91f9ceed..42d9b585 100644 --- a/backend/src/docs/asciidoc/category.adoc +++ b/backend/src/docs/asciidoc/category.adoc @@ -2,144 +2,132 @@ === 카테고리 생성 -==== Request +==== HTTP Request -include::{snippets}/categories/save/http-request.adoc[] +include::{snippets}/category/save/http-request.adoc[] -==== Response +==== Request Fields -include::{snippets}/categories/save/http-response.adoc[] +include::{snippets}/category/save/request-fields.adoc[] -=== 카테고리 생성 (유효하지 않은 카테고리 이름) +==== HTTP Response -==== Request +include::{snippets}/category/save/http-response.adoc[] -include::{snippets}/categories/save/badRequest/http-request.adoc[] +==== Response Fields + +include::{snippets}/category/save/response-fields.adoc[] + +=== 카테고리 생성 (유효하지 않은 카테고리 이름) -==== Response +==== HTTP Response -include::{snippets}/categories/save/badRequest/http-response.adoc[] +include::{snippets}/category/save/failByInvalidNameFormat/http-response.adoc[] === 전체 카테고리 조회 ==== Request -include::{snippets}/categories/findAll/http-request.adoc[] +include::{snippets}/category/findAllByName/allByNoName/http-request.adoc[] -==== RequestParameters +==== Request Parameters -include::{snippets}/categories/findAll/request-parameters.adoc[] +include::{snippets}/category/findAllByName/allByNoName/request-parameters.adoc[] -==== Response +==== HTTP Response -include::{snippets}/categories/findAll/http-response.adoc[] +include::{snippets}/category/findAllByName/allByNoName/http-response.adoc[] -=== 카테고리 제목 조회 +=== 전체 카테고리 목록 이름으로 필터링 -==== Request +==== HTTP Request -include::{snippets}/categories/findAllLikeName/http-request.adoc[] +include::{snippets}/category/findAllByName/fileterByName/http-request.adoc[] -==== RequestParameters +==== Request Parameters -include::{snippets}/categories/findAllLikeName/request-parameters.adoc[] +include::{snippets}/category/findAllByName/fileterByName/request-parameters.adoc[] -==== Response +==== HTTP Response -include::{snippets}/categories/findAllLikeName/http-response.adoc[] +include::{snippets}/category/findAllByName/fileterByName/http-response.adoc[] === 자신이 생성한 카테고리 조회 -==== Request +==== HTTP Request -include::{snippets}/categories/findMine/http-request.adoc[] +include::{snippets}/category/findMineByName/allByNoName/http-request.adoc[] -==== RequestParameters +==== Request Parameters -include::{snippets}/categories/findMine/request-parameters.adoc[] +include::{snippets}/category/findMineByName/allByNoName/request-parameters.adoc[] -==== Response +==== HTTP Response -include::{snippets}/categories/findMine/response-body.adoc[] +include::{snippets}/category/findMineByName/allByNoName/http-response.adoc[] === ID를 통한 카테고리 단건 조회 -==== Request +==== HTTP Request -include::{snippets}/categories/findById/http-request.adoc[] +include::{snippets}/category/findById/http-request.adoc[] -==== PathParameters +==== Path Parameters -include::{snippets}/categories/findById/path-parameters.adoc[] +include::{snippets}/category/findById/path-parameters.adoc[] -==== Response +==== HTTP Response -include::{snippets}/categories/findById/http-response.adoc[] +include::{snippets}/category/findById/http-response.adoc[] === ID를 통한 카테고리 단건 조회 (존재하지 않는 경우) -==== Request - -include::{snippets}/categories/findById/notFound/http-request.adoc[] - -==== Response +==== HTTP Response -include::{snippets}/categories/findById/notFound/http-response.adoc[] +include::{snippets}/category/findById/failByNoCategory/http-response.adoc[] === 카테고리 수정 -==== Request +==== HTTP Request -include::{snippets}/categories/update/http-request.adoc[] +include::{snippets}/category/update/http-request.adoc[] -==== PathParameters +==== Path Parameters -include::{snippets}/categories/update/path-parameters.adoc[] +include::{snippets}/category/update/path-parameters.adoc[] -==== Response +==== HTTP Response -include::{snippets}/categories/update/http-response.adoc[] +include::{snippets}/category/update/http-response.adoc[] === 카테고리 수정 (존재하지 않는 경우) -==== Request - -include::{snippets}/categories/update/notFound/http-request.adoc[] +==== HTTP Response -==== Response - -include::{snippets}/categories/update/notFound/http-response.adoc[] +include::{snippets}/category/update/failByNoCategory/http-response.adoc[] === 카테고리 수정 (유효하지 않은 카테고리 이름) -==== Request - -include::{snippets}/categories/update/badRequest/http-request.adoc[] +==== HTTP Response -==== Response - -include::{snippets}/categories/update/badRequest/http-response.adoc[] +include::{snippets}/category/update/failByInvalidNameFormat/http-response.adoc[] === 카테고리 삭제 -==== Request +==== HTTP Request -include::{snippets}/categories/delete/http-request.adoc[] +include::{snippets}/category/delete/http-request.adoc[] -==== PathParameters +==== Path Parameters -include::{snippets}/categories/delete/path-parameters.adoc[] +include::{snippets}/category/delete/path-parameters.adoc[] -==== Response +==== HTTP Response -include::{snippets}/categories/delete/http-response.adoc[] +include::{snippets}/category/delete/http-response.adoc[] === 카테고리 삭제 (존재하지 않는 경우) -==== Request - -include::{snippets}/categories/delete/notFound/http-request.adoc[] - -==== Response +==== HTTP Response -include::{snippets}/categories/delete/notFound/http-response.adoc[] +include::{snippets}/category/delete/failByNoCategory/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/external-calendar.adoc b/backend/src/docs/asciidoc/external-calendar.adoc index 72e3bc0b..ca2cdeca 100644 --- a/backend/src/docs/asciidoc/external-calendar.adoc +++ b/backend/src/docs/asciidoc/external-calendar.adoc @@ -2,46 +2,30 @@ === 자신의 외부 캘린더 조회 -==== Request +==== HTTP Request -include::{snippets}/external-calendars/get/http-request.adoc[] +include::{snippets}/externalCalendar/getExternalCalendar/http-request.adoc[] -==== Response +==== HTTP Response -include::{snippets}/external-calendars/get/http-response.adoc[] +include::{snippets}/externalCalendar/getExternalCalendar/http-response.adoc[] === 자신의 외부 캘린더 저장 -==== Request +==== HTTP Request -include::{snippets}/external-calendars/save/http-request.adoc[] +include::{snippets}/externalCalendar/save/http-request.adoc[] -==== Request Headers +==== Request Fields -include::{snippets}/external-calendars/save/request-headers.adoc[] +include::{snippets}/externalCalendar/save/request-fields.adoc[] -==== Request Body +==== HTTP Response -include::{snippets}/external-calendars/save/request-fields.adoc[] +include::{snippets}/externalCalendar/save/http-response.adoc[] -==== Response +=== 자신의 외부 캘린더 저장 (중복 저장할 경우) -include::{snippets}/external-calendars/save/http-response.adoc[] +==== HTTP Response -=== 자신의 외부 캘린더 중복 저장 시 예외 발생 - -==== Request - -include::{snippets}/external-calendars/duplicated-save/http-request.adoc[] - -==== Request Headers - -include::{snippets}/external-calendars/duplicated-save/request-headers.adoc[] - -==== Request Body - -include::{snippets}/external-calendars/duplicated-save/request-fields.adoc[] - -==== Response - -include::{snippets}/external-calendars/duplicated-save/http-response.adoc[] +include::{snippets}/externalCalendar/save/failByDuplicate/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/member.adoc b/backend/src/docs/asciidoc/member.adoc index ece68ebb..33be114f 100644 --- a/backend/src/docs/asciidoc/member.adoc +++ b/backend/src/docs/asciidoc/member.adoc @@ -2,52 +2,44 @@ === 내 정보 조회 -==== Request +==== HTTP Request -include::{snippets}/members/me/http-request.adoc[] +include::{snippets}/member/findMe/http-request.adoc[] -==== RequestHeaders +==== HTTP Response -include::{snippets}/members/me/request-headers.adoc[] +include::{snippets}/member/findMe/http-response.adoc[] -==== Response +==== Response Fields -include::{snippets}/members/me/http-response.adoc[] +include::{snippets}/member/findMe/response-fields.adoc[] -=== 삭제된 회원 정보 조회 +=== 내 정보 조회 (존재하지 않는 회원 조회 시) -==== Request +==== HTTP Response -include::{snippets}/members/exception/notfound/http-request.adoc[] - -==== Response - -include::{snippets}/members/exception/notfound/http-response.adoc[] +include::{snippets}/member/findMe/failNoMember/http-response.adoc[] === 내 정보 수정 -==== Request - -include::{snippets}/members/update/http-request.adoc[] - -==== RequestHeaders +==== HTTP Request -include::{snippets}/members/update/request-headers.adoc[] +include::{snippets}/member/update/http-request.adoc[] -==== Request Parameters +==== Request Fields -include::{snippets}/members/update/request-body.adoc[] +include::{snippets}/member/update/request-fields.adoc[] -==== Response +==== HTTP Response -include::{snippets}/members/update/http-response.adoc[] +include::{snippets}/member/update/http-response.adoc[] === 회원 탈퇴 -==== Request +==== HTTP Request -include::{snippets}/members/delete/http-request.adoc[] +include::{snippets}/member/delete/http-request.adoc[] -==== Response +==== HTTP Response -include::{snippets}/members/delete/http-response.adoc[] +include::{snippets}/member/delete/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/schedule.adoc b/backend/src/docs/asciidoc/schedule.adoc index 46ea60b8..7ba1924c 100644 --- a/backend/src/docs/asciidoc/schedule.adoc +++ b/backend/src/docs/asciidoc/schedule.adoc @@ -2,121 +2,107 @@ === 회원 일정 목록 조회 -==== Request - -include::{snippets}/schedules/findAllByMember/http-request.adoc[] - -==== RequestParameters - -include::{snippets}/schedules/findAllByMember/request-parameters.adoc[] - -==== Response - -include::{snippets}/schedules/findAllByMember/http-response.adoc[] +==== HTTP Request -=== 회원 일정 및 외부 일정 목록 조회 +include::{snippets}/schedule/findSchedulesByMemberId/http-request.adoc[] -==== Request - -include::{snippets}/schedules/findAllByMemberWithExternalSchedule/http-request.adoc[] +==== Request Parameters -==== RequestParameters +include::{snippets}/schedule/findSchedulesByMemberId/request-parameters.adoc[] -include::{snippets}/schedules/findAllByMemberWithExternalSchedule/request-parameters.adoc[] - -==== Response +==== HTTP Response -include::{snippets}/schedules/findAllByMemberWithExternalSchedule/http-response.adoc[] +include::{snippets}/schedule/findSchedulesByMemberId/http-response.adoc[] === 일정 등록 -==== Request +==== HTTP Request -include::{snippets}/schedules/save/http-request.adoc[] +include::{snippets}/schedule/save/http-request.adoc[] -==== Response +==== HTTP Response -include::{snippets}/schedules/save/http-response.adoc[] +include::{snippets}/schedule/save/http-response.adoc[] -=== 일정 생성 (카테고리 권한 없음) +=== 일정 등록 (카테고리 권한이 없을 때) -==== Response +==== HTTP Response -include::{snippets}/schedules/save/forbidden/http-response.adoc[] +include::{snippets}/schedule/save/failByNoPermission/http-response.adoc[] === 일정 생성 (카테고리가 존재하지 않음) -==== Response +==== HTTP Response -include::{snippets}/schedules/save/notfound/http-response.adoc[] +include::{snippets}/schedule/save/failByNoCategory/http-response.adoc[] === 일정 단건 조회 -==== Request +==== HTTP Request -include::{snippets}/schedules/findone/http-request.adoc[] +include::{snippets}/schedule/findById/http-request.adoc[] -==== Response +==== HTTP Response -include::{snippets}/schedules/findone/http-response.adoc[] +include::{snippets}/schedule/findById/http-response.adoc[] -=== 일정 단건 조회 (일정이 존재하지 않음) +=== 일정 단건 조회 (일정이 존재하지 않을 때) -==== Response +==== HTTP Response -include::{snippets}/schedules/findone/notfound/http-response.adoc[] +include::{snippets}/schedule/findById/failByNoSchedule/http-response.adoc[] === 일정 수정 -==== Request +==== HTTP Request -include::{snippets}/schedules/update/http-request.adoc[] +include::{snippets}/schedule/update/http-request.adoc[] -==== Path Variable +==== Path Parameters -include::{snippets}/schedules/update/path-parameters.adoc[] +include::{snippets}/schedule/update/path-parameters.adoc[] -==== Response +==== HTTP Response -include::{snippets}/schedules/update/http-response.adoc[] +include::{snippets}/schedule/update/http-response.adoc[] -=== 일정 수정 (카테고리 권한 없음) +=== 일정 수정 (카테고리 권한이 없을 때) -==== Response +==== HTTP Response -include::{snippets}/schedules/update/forbidden/http-response.adoc[] +include::{snippets}/schedule/update/failByNoPermission/http-response.adoc[] -=== 일정 수정 (카테고리가 존재하지 않음) +=== 일정 수정 (카테고리가 존재하지 않을 때) -==== Response +==== HTTP Response -include::{snippets}/schedules/update/notfound/http-response.adoc[] +include::{snippets}/schedule/update/failByNoSchedule/http-response.adoc[] === 일정 제거 -==== Request +==== HTTP Request -include::{snippets}/schedules/delete/http-request.adoc[] +include::{snippets}/schedule/delete/http-request.adoc[] -==== Path Variable +==== Path Parameters -include::{snippets}/schedules/delete/path-parameters.adoc[] +include::{snippets}/schedule/delete/path-parameters.adoc[] -==== Response +==== HTTP Response -include::{snippets}/schedules/delete/http-response.adoc[] +include::{snippets}/schedule/delete/http-response.adoc[] -=== 일정 제거 (카테고리 권한 없음) +=== 일정 제거 (카테고리 권한이 없을 때) -==== Response +==== HTTP Response -include::{snippets}/schedules/delete/forbidden/http-response.adoc[] +include::{snippets}/schedule/delete/failByNoPermission/http-response.adoc[] === 일정 제거 (카테고리가 존재하지 않음) -==== Response +==== HTTP Response -include::{snippets}/schedules/delete/notfound/http-response.adoc[] +include::{snippets}/schedule/delete/failByNoSchedule/http-response.adoc[] === 일정 조율 @@ -124,14 +110,16 @@ include::{snippets}/schedules/delete/notfound/http-response.adoc[] ==== Request -include::{snippets}/scheduler/category/available-periods/http-request.adoc[] +include::{snippets}/scheduler/scheduleByCategory/http-request.adoc[] + +==== Path Parameters -==== Parameters +include::{snippets}/scheduler/scheduleByCategory/path-parameters.adoc[] -include::{snippets}/scheduler/category/available-periods/path-parameters.adoc[] +==== Request Parameters -include::{snippets}/scheduler/category/available-periods/request-parameters.adoc[] +include::{snippets}/scheduler/scheduleByCategory/request-parameters.adoc[] ==== Response -include::{snippets}/scheduler/category/available-periods/http-response.adoc[] +include::{snippets}/scheduler/scheduleByCategory/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/subscription.adoc b/backend/src/docs/asciidoc/subscription.adoc index 93dbcedc..12273c12 100644 --- a/backend/src/docs/asciidoc/subscription.adoc +++ b/backend/src/docs/asciidoc/subscription.adoc @@ -2,7 +2,7 @@ === 구독 등록 -==== Request +==== HTTP Request include::{snippets}/subscription/save/http-request.adoc[] @@ -10,63 +10,35 @@ include::{snippets}/subscription/save/http-request.adoc[] include::{snippets}/subscription/save/path-parameters.adoc[] -==== Request Headers - -include::{snippets}/subscription/save/request-headers.adoc[] - -==== Response +==== HTTP Response include::{snippets}/subscription/save/http-response.adoc[] -=== 자신의 구독 목록 조회 - -==== Request - -include::{snippets}/subscription/findMine/http-request.adoc[] - -==== Response - -include::{snippets}/subscription/findMine/http-response.adoc[] - -=== 중복된 구독 등록 - -==== Request - -include::{snippets}/subscription/exist/http-request.adoc[] - -==== Path Parameters - -include::{snippets}/subscription/exist/path-parameters.adoc[] - -==== Request Headers +=== 구독 등록 (중복된 구독을 등록할 때) -include::{snippets}/subscription/exist/request-headers.adoc[] +==== HTTP Response -==== Response +include::{snippets}/subscription/save/failByAlreadyExisting/http-response.adoc[] -include::{snippets}/subscription/exist/http-response.adoc[] +=== 구독 등록 (3자의 개인 카테고리 구독 요청시) -==== Request +==== HTTP Response -include::{snippets}/subscription/me/http-request.adoc[] +include::{snippets}/subscription/save/failBySubscribingPrivateCategoryOfOther/http-response.adoc[] -==== Request Headers - -include::{snippets}/subscription/me/request-headers.adoc[] - -==== Response +=== 자신의 구독 목록 조회 -include::{snippets}/subscription/me/http-response.adoc[] +==== HTTP Request -=== 3자의 개인 카테고리 구독 요청시 +include::{snippets}/subscription/findMine/http-request.adoc[] -==== Response +==== HTTP Response -include::{snippets}/subscription/private-category/http-response.adoc[] +include::{snippets}/subscription/findMine/http-response.adoc[] === 내 구독 정보 수정 -==== Request +==== HTTP Request include::{snippets}/subscription/update/http-request.adoc[] @@ -82,13 +54,13 @@ include::{snippets}/subscription/update/request-headers.adoc[] include::{snippets}/subscription/update/request-body.adoc[] -==== Response +==== HTTP Response include::{snippets}/subscription/update/http-response.adoc[] -=== 내 구독 정보 삭제 +=== 구독 삭제 -==== Request +==== HTTP Request include::{snippets}/subscription/delete/http-request.adoc[] @@ -96,28 +68,12 @@ include::{snippets}/subscription/delete/http-request.adoc[] include::{snippets}/subscription/delete/path-parameters.adoc[] -==== Request Headers - -include::{snippets}/subscription/delete/request-headers.adoc[] - -==== Response +==== HTTP Response include::{snippets}/subscription/delete/http-response.adoc[] -=== 내 구독 정보가 아닌 구독 삭제 - -==== Request - -include::{snippets}/subscription/permission/http-request.adoc[] - -==== Path Parameters - -include::{snippets}/subscription/permission/path-parameters.adoc[] - -==== Request Headers - -include::{snippets}/subscription/permission/request-headers.adoc[] +=== 구독 삭제 (내 구독이 아닐 때) -==== Response +==== HTTP Response -include::{snippets}/subscription/permission/http-response.adoc[] +include::{snippets}/subscription/delete/failByNoPermission/http-response.adoc[] diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/application/AuthService.java b/backend/src/main/java/com/allog/dallog/domain/auth/application/AuthService.java index fdcb2dd1..dd68ba4d 100644 --- a/backend/src/main/java/com/allog/dallog/domain/auth/application/AuthService.java +++ b/backend/src/main/java/com/allog/dallog/domain/auth/application/AuthService.java @@ -1,14 +1,21 @@ package com.allog.dallog.domain.auth.application; +import static com.allog.dallog.domain.category.domain.CategoryType.PERSONAL; + import com.allog.dallog.domain.auth.domain.OAuthToken; import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; import com.allog.dallog.domain.auth.dto.OAuthMember; import com.allog.dallog.domain.auth.dto.request.TokenRequest; import com.allog.dallog.domain.auth.dto.response.TokenResponse; -import com.allog.dallog.domain.auth.exception.NoSuchOAuthTokenException; -import com.allog.dallog.domain.composition.application.RegisterService; -import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import com.allog.dallog.domain.member.domain.SocialType; +import com.allog.dallog.domain.subscription.application.ColorPicker; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.domain.Subscription; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,22 +23,30 @@ @Service public class AuthService { + private static final String PERSONAL_CATEGORY_NAME = "내 일정"; + + private final MemberRepository memberRepository; + private final CategoryRepository categoryRepository; + private final OAuthTokenRepository oAuthTokenRepository; + private final SubscriptionRepository subscriptionRepository; private final OAuthUri oAuthUri; private final OAuthClient oAuthClient; - private final MemberService memberService; - private final RegisterService registerService; - private final OAuthTokenRepository oAuthTokenRepository; private final TokenProvider tokenProvider; + private final ColorPicker colorPicker; - public AuthService(final OAuthUri oAuthUri, final OAuthClient oAuthClient, final MemberService memberService, - final RegisterService registerService, final OAuthTokenRepository oAuthTokenRepository, - final TokenProvider tokenProvider) { + public AuthService(final MemberRepository memberRepository, final CategoryRepository categoryRepository, + final OAuthTokenRepository oAuthTokenRepository, + final SubscriptionRepository subscriptionRepository, final OAuthUri oAuthUri, + final OAuthClient oAuthClient, final TokenProvider tokenProvider, + final ColorPicker colorPicker) { + this.memberRepository = memberRepository; + this.categoryRepository = categoryRepository; + this.oAuthTokenRepository = oAuthTokenRepository; + this.subscriptionRepository = subscriptionRepository; this.oAuthUri = oAuthUri; this.oAuthClient = oAuthClient; - this.memberService = memberService; - this.registerService = registerService; - this.oAuthTokenRepository = oAuthTokenRepository; this.tokenProvider = tokenProvider; + this.colorPicker = colorPicker; } public String generateGoogleLink(final String redirectUri) { @@ -44,7 +59,7 @@ public TokenResponse generateToken(final TokenRequest tokenRequest) { String redirectUri = tokenRequest.getRedirectUri(); OAuthMember oAuthMember = oAuthClient.getOAuthMember(code, redirectUri); - Member foundMember = getMember(oAuthMember); + Member foundMember = findMember(oAuthMember); OAuthToken oAuthToken = getOAuthToken(oAuthMember, foundMember); oAuthToken.change(oAuthMember.getRefreshToken()); @@ -53,27 +68,48 @@ public TokenResponse generateToken(final TokenRequest tokenRequest) { return new TokenResponse(accessToken); } - private Member getMember(final OAuthMember oAuthMember) { - if (!memberService.existsByEmail(oAuthMember.getEmail())) { - registerService.register(oAuthMember); + private Member findMember(final OAuthMember oAuthMember) { + if (memberRepository.existsByEmail(oAuthMember.getEmail())) { + return memberRepository.getByEmail(oAuthMember.getEmail()); } - return memberService.getByEmail(oAuthMember.getEmail()); + return saveMember(oAuthMember); + } + + private Member saveMember(final OAuthMember oAuthMember) { + Member savedMember = memberRepository.save(toMember(oAuthMember)); + Category savedCategory = saveCategory(savedMember); + saveSubscription(savedMember, savedCategory); + + return savedMember; + } + + private Member toMember(final OAuthMember oAuthMember) { + return new Member(oAuthMember.getEmail(), oAuthMember.getDisplayName(), oAuthMember.getProfileImageUrl(), + SocialType.GOOGLE); + } + + private Category saveCategory(final Member savedMember) { + return categoryRepository.save(new Category(PERSONAL_CATEGORY_NAME, savedMember, PERSONAL)); + } + + private Subscription saveSubscription(final Member savedMember, final Category savedCategory) { + Color randomColor = Color.pick(colorPicker.pickNumber()); + return subscriptionRepository.save(new Subscription(savedMember, savedCategory, randomColor)); } private OAuthToken getOAuthToken(final OAuthMember oAuthMember, final Member foundMember) { - if (!oAuthTokenRepository.existsByMemberId(foundMember.getId())) { - oAuthTokenRepository.save(new OAuthToken(foundMember, oAuthMember.getRefreshToken())); + if (oAuthTokenRepository.existsByMemberId(foundMember.getId())) { + return oAuthTokenRepository.getByMemberId(foundMember.getId()); } - return oAuthTokenRepository.findByMemberId(foundMember.getId()) - .orElseThrow(NoSuchOAuthTokenException::new); + return oAuthTokenRepository.save(new OAuthToken(foundMember, oAuthMember.getRefreshToken())); } public Long extractMemberId(final String accessToken) { tokenProvider.validateToken(accessToken); Long memberId = Long.valueOf(tokenProvider.getPayload(accessToken)); - memberService.validateExistsMember(memberId); + memberRepository.validateExistsById(memberId); return memberId; } } diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepository.java b/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepository.java index c95cf2f8..7d8e3135 100644 --- a/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepository.java +++ b/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepository.java @@ -1,5 +1,6 @@ package com.allog.dallog.domain.auth.domain; +import com.allog.dallog.domain.auth.exception.NoSuchOAuthTokenException; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; @@ -10,4 +11,9 @@ public interface OAuthTokenRepository extends JpaRepository { Optional findByMemberId(final Long memberId); void deleteByMemberId(final Long memberId); + + default OAuthToken getByMemberId(final Long memberId) { + return findByMemberId(memberId) + .orElseThrow(NoSuchOAuthTokenException::new); + } } diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthAccessTokenResponse.java b/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthAccessTokenResponse.java index 9b8539b1..41d3ab64 100644 --- a/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthAccessTokenResponse.java +++ b/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthAccessTokenResponse.java @@ -7,34 +7,15 @@ public class OAuthAccessTokenResponse { private String accessToken; - private String expiresIn; - private String scope; - private String tokenType; private OAuthAccessTokenResponse() { } - public OAuthAccessTokenResponse(final String accessToken, final String expiresIn, final String scope, - final String tokenType) { + public OAuthAccessTokenResponse(final String accessToken) { this.accessToken = accessToken; - this.expiresIn = expiresIn; - this.scope = scope; - this.tokenType = tokenType; } public String getAccessToken() { return accessToken; } - - public String getExpiresIn() { - return expiresIn; - } - - public String getScope() { - return scope; - } - - public String getTokenType() { - return tokenType; - } } diff --git a/backend/src/main/java/com/allog/dallog/domain/category/application/CategoryService.java b/backend/src/main/java/com/allog/dallog/domain/category/application/CategoryService.java index 898ca2aa..61c2f41d 100644 --- a/backend/src/main/java/com/allog/dallog/domain/category/application/CategoryService.java +++ b/backend/src/main/java/com/allog/dallog/domain/category/application/CategoryService.java @@ -2,7 +2,6 @@ import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; -import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.category.domain.Category; import com.allog.dallog.domain.category.domain.CategoryRepository; import com.allog.dallog.domain.category.domain.CategoryType; @@ -13,16 +12,15 @@ import com.allog.dallog.domain.category.dto.request.ExternalCategoryCreateRequest; import com.allog.dallog.domain.category.dto.response.CategoriesResponse; import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.category.exception.DuplicatedExternalCategoryException; import com.allog.dallog.domain.category.exception.InvalidCategoryException; -import com.allog.dallog.domain.category.exception.NoSuchCategoryException; import com.allog.dallog.domain.member.domain.Member; import com.allog.dallog.domain.member.domain.MemberRepository; -import com.allog.dallog.domain.member.exception.NoSuchMemberException; import com.allog.dallog.domain.schedule.domain.ScheduleRepository; +import com.allog.dallog.domain.subscription.application.ColorPicker; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.domain.Subscription; import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; import java.util.List; -import java.util.stream.Collectors; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -36,52 +34,48 @@ public class CategoryService { private final MemberRepository memberRepository; private final SubscriptionRepository subscriptionRepository; private final ScheduleRepository scheduleRepository; + private final ColorPicker colorPicker; public CategoryService(final CategoryRepository categoryRepository, final ExternalCategoryDetailRepository externalCategoryDetailRepository, final MemberRepository memberRepository, final SubscriptionRepository subscriptionRepository, - final ScheduleRepository scheduleRepository) { + final ScheduleRepository scheduleRepository, final ColorPicker colorPicker) { this.categoryRepository = categoryRepository; this.externalCategoryDetailRepository = externalCategoryDetailRepository; this.memberRepository = memberRepository; this.subscriptionRepository = subscriptionRepository; this.scheduleRepository = scheduleRepository; + this.colorPicker = colorPicker; } @Transactional public CategoryResponse save(final Long memberId, final CategoryCreateRequest request) { - Member member = memberRepository.findById(memberId) - .orElseThrow(NoSuchMemberException::new); - Category newCategory = new Category(request.getName(), member, - CategoryType.valueOf(request.getCategoryType().toUpperCase())); - categoryRepository.save(newCategory); - return new CategoryResponse(newCategory); + Member member = memberRepository.getById(memberId); + Category category = request.toEntity(member); + Category savedCategory = categoryRepository.save(category); + subscribeCategory(member, category); + return new CategoryResponse(savedCategory); + } + + private void subscribeCategory(final Member member, final Category category) { + Color color = Color.pick(colorPicker.pickNumber()); + subscriptionRepository.save(new Subscription(member, category, color)); } @Transactional public CategoryResponse save(final Long memberId, final ExternalCategoryCreateRequest request) { - List categories = categoryRepository.findByMemberId(memberId); - validateDuplicateExternalCategory(request.getExternalId(), categories); + List externalCategories = categoryRepository + .findByMemberIdAndCategoryType(memberId, CategoryType.GOOGLE); + externalCategoryDetailRepository + .validateExistByExternalIdAndCategoryIn(request.getExternalId(), externalCategories); CategoryResponse response = save(memberId, new CategoryCreateRequest(request.getName(), CategoryType.GOOGLE)); - Category category = getCategory(response.getId()); - + Category category = categoryRepository.getById(response.getId()); externalCategoryDetailRepository.save(new ExternalCategoryDetail(category, request.getExternalId())); return response; } - private void validateDuplicateExternalCategory(final String externalId, final List categories) { - List externalCategories = categories.stream() - .filter(Category::isExternal) - .collect(Collectors.toList()); - - if (!externalCategories.isEmpty() - && externalCategoryDetailRepository.existsByExternalIdAndCategoryIn(externalId, externalCategories)) { - throw new DuplicatedExternalCategoryException(); - } - } - public CategoriesResponse findNormalByName(final String name, final Pageable pageable) { List categories = categoryRepository.findByNameContainingAndCategoryType(name, NORMAL, pageable).getContent(); @@ -89,76 +83,41 @@ public CategoriesResponse findNormalByName(final String name, final Pageable pag return new CategoriesResponse(pageable.getPageNumber(), categories); } - public CategoriesResponse findMineByName(final Long memberId, final String name, final Pageable pageable) { - List categories = categoryRepository.findByMemberIdLikeCategoryName(memberId, name, pageable) - .getContent(); + public CategoriesResponse findMyCategories(final Long memberId, final String name, final Pageable pageable) { + List categories + = categoryRepository.findByMemberIdAndNameContaining(memberId, name, pageable).getContent(); return new CategoriesResponse(pageable.getPageNumber(), categories); } public CategoryResponse findById(final Long id) { - return new CategoryResponse(getCategory(id)); - } - - public Category getCategory(final Long id) { - return categoryRepository.findById(id) - .orElseThrow(NoSuchCategoryException::new); + Category category = categoryRepository.getById(id); + return new CategoryResponse(category); } @Transactional - public void update(final Long memberId, final Long categoryId, final CategoryUpdateRequest request) { - Category category = getCategory(categoryId); - validatePermission(memberId, categoryId); - + public void update(final Long memberId, final Long id, final CategoryUpdateRequest request) { + categoryRepository.validateExistsByIdAndMemberId(id, memberId); + Category category = categoryRepository.getById(id); category.changeName(request.getName()); } @Transactional - public void deleteById(final Long memberId, final Long categoryId) { - validateCategoryExisting(categoryId); - validatePermission(memberId, categoryId); - validatePersonalCategory(categoryId); - - scheduleRepository.deleteByCategoryIdIn(List.of(categoryId)); - subscriptionRepository.deleteByCategoryIdIn(List.of(categoryId)); - externalCategoryDetailRepository.deleteByCategoryId(categoryId); - categoryRepository.deleteById(categoryId); - } + public void delete(final Long memberId, final Long id) { + categoryRepository.validateExistsByIdAndMemberId(id, memberId); + Category category = categoryRepository.getById(id); - private void validatePersonalCategory(final Long categoryId) { - Category category = getCategory(categoryId); - if (category.isPersonal()) { - throw new InvalidCategoryException("내 일정 카테고리는 삭제할 수 없습니다."); - } - } - - private void validateCategoryExisting(final Long categoryId) { - if (!categoryRepository.existsById(categoryId)) { - throw new NoSuchCategoryException("존재하지 않는 카테고리를 삭제할 수 없습니다."); - } - } + validateNotPersonalCategory(category); - private void validatePermission(final Long memberId, final Long categoryId) { - if (!categoryRepository.existsByIdAndMemberId(categoryId, memberId)) { - throw new NoPermissionException(); - } - } - - @Transactional - public void deleteByMemberId(final Long memberId) { - List categoryIds = categoryRepository.findByMemberId(memberId) - .stream() - .map(Category::getId) - .collect(Collectors.toList()); - - scheduleRepository.deleteByCategoryIdIn(categoryIds); - subscriptionRepository.deleteByCategoryIdIn(categoryIds); - categoryRepository.deleteByMemberId(memberId); + scheduleRepository.deleteByCategoryIdIn(List.of(id)); + subscriptionRepository.deleteByCategoryIdIn(List.of(id)); + externalCategoryDetailRepository.deleteByCategoryId(id); + categoryRepository.deleteById(id); } - public void validateCreatorBy(final Long memberId, final Category category) { - if (!category.isCreator(memberId)) { - throw new NoPermissionException(); + private void validateNotPersonalCategory(final Category category) { + if (category.isPersonal()) { + throw new InvalidCategoryException("내 일정 카테고리는 삭제할 수 없습니다."); } } } diff --git a/backend/src/main/java/com/allog/dallog/domain/category/application/ExternalCategoryDetailService.java b/backend/src/main/java/com/allog/dallog/domain/category/application/ExternalCategoryDetailService.java new file mode 100644 index 00000000..818cceff --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/application/ExternalCategoryDetailService.java @@ -0,0 +1,30 @@ +package com.allog.dallog.domain.category.application; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetail; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetailRepository; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; +import com.allog.dallog.domain.subscription.domain.Subscriptions; +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class ExternalCategoryDetailService { + + private final ExternalCategoryDetailRepository externalCategoryDetailRepository; + private final SubscriptionRepository subscriptionRepository; + + public ExternalCategoryDetailService(final ExternalCategoryDetailRepository externalCategoryDetailRepository, + final SubscriptionRepository subscriptionRepository) { + this.externalCategoryDetailRepository = externalCategoryDetailRepository; + this.subscriptionRepository = subscriptionRepository; + } + + public List findByMemberId(final Long memberId) { + Subscriptions subscriptions = new Subscriptions(subscriptionRepository.findByMemberId(memberId)); + List categories = subscriptions.findExternalCategory(); + return externalCategoryDetailRepository.findByCategoryIn(categories); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/domain/Category.java b/backend/src/main/java/com/allog/dallog/domain/category/domain/Category.java index e4cc2867..800b9c33 100644 --- a/backend/src/main/java/com/allog/dallog/domain/category/domain/Category.java +++ b/backend/src/main/java/com/allog/dallog/domain/category/domain/Category.java @@ -3,10 +3,10 @@ import static com.allog.dallog.domain.category.domain.CategoryType.GOOGLE; import static com.allog.dallog.domain.category.domain.CategoryType.PERSONAL; +import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.category.exception.InvalidCategoryException; import com.allog.dallog.domain.common.BaseEntity; import com.allog.dallog.domain.member.domain.Member; -import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -79,8 +79,23 @@ private void validateNameLength(final String name) { } } - public boolean isCreator(final Long memberId) { - return Objects.equals(member.getId(), memberId); + public void validateSubscriptionPossible(final Member member) { + if (this.categoryType == PERSONAL && !isCreatorId(member.getId())) { + throw new NoPermissionException("구독 권한이 없는 카테고리입니다."); + } + } + + public void validateCanAddSchedule(final Member member) { + if (categoryType == GOOGLE) { + throw new NoPermissionException("외부 연동 카테고리에는 일정을 추가할 수 없습니다."); + } + if (!this.member.equals(member)) { + throw new NoPermissionException(); + } + } + + public boolean isCreatorId(final Long creatorId) { + return member.hasSameId(creatorId); } public boolean isPersonal() { diff --git a/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryRepository.java b/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryRepository.java index 203b2279..fdc6f411 100644 --- a/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryRepository.java +++ b/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryRepository.java @@ -1,5 +1,7 @@ package com.allog.dallog.domain.category.domain; +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; import java.util.List; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -10,17 +12,31 @@ public interface CategoryRepository extends JpaRepository { @Query("SELECT c " + "FROM Category c " - + "WHERE c.name LIKE %:name% AND c.categoryType = :categoryType") - Slice findByNameContainingAndCategoryType(final String name, final CategoryType categoryType, final Pageable pageable); + + "WHERE c.member.id = :memberId AND c.name LIKE %:name%") + Slice findByMemberIdAndNameContaining(final Long memberId, final String name, final Pageable pageable); @Query("SELECT c " + "FROM Category c " - + "WHERE c.member.id = :memberId AND c.name LIKE %:name%") - Slice findByMemberIdLikeCategoryName(final Long memberId, final String name, final Pageable pageable); + + "WHERE c.name LIKE %:name% AND c.categoryType = :categoryType") + Slice findByNameContainingAndCategoryType(final String name, final CategoryType categoryType, + final Pageable pageable); + + List findByMemberIdAndCategoryType(final Long memberId, final CategoryType categoryType); + + List findByMemberId(final Long memberId); + + boolean existsByIdAndMemberId(final Long id, final Long memberId); - List findByMemberId(Long memberId); + void deleteByMemberId(final Long memberId); - boolean existsByIdAndMemberId(Long id, Long memberId); + default Category getById(final Long id) { + return this.findById(id) + .orElseThrow(NoSuchCategoryException::new); + } - void deleteByMemberId(Long memberId); + default void validateExistsByIdAndMemberId(final Long id, final Long memberId) { + if (!existsByIdAndMemberId(id, memberId)) { + throw new NoPermissionException(); + } + } } diff --git a/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepository.java b/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepository.java index 767a66ff..873b47a6 100644 --- a/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepository.java +++ b/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepository.java @@ -1,14 +1,30 @@ package com.allog.dallog.domain.category.domain; +import com.allog.dallog.domain.category.exception.ExistExternalCategoryException; +import com.allog.dallog.domain.category.exception.NoSuchExternalCategoryDetailException; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface ExternalCategoryDetailRepository extends JpaRepository { - Optional findByCategoryId(final Long categoryId); + Optional findByCategory(final Category category); + + List findByCategoryIn(final List categories); boolean existsByExternalIdAndCategoryIn(final String externalId, final List categories); void deleteByCategoryId(final Long categoryId); + + default ExternalCategoryDetail getByCategory(final Category category) { + return this.findByCategory(category) + .orElseThrow(NoSuchExternalCategoryDetailException::new); + } + + default void validateExistByExternalIdAndCategoryIn(final String externalId, + final List externalCategories) { + if (existsByExternalIdAndCategoryIn(externalId, externalCategories)) { + throw new ExistExternalCategoryException(); + } + } } diff --git a/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryCreateRequest.java b/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryCreateRequest.java index d6bd4467..6ce4a11c 100644 --- a/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryCreateRequest.java +++ b/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryCreateRequest.java @@ -1,6 +1,8 @@ package com.allog.dallog.domain.category.dto.request; +import com.allog.dallog.domain.category.domain.Category; import com.allog.dallog.domain.category.domain.CategoryType; +import com.allog.dallog.domain.member.domain.Member; import javax.validation.constraints.NotBlank; public class CategoryCreateRequest { @@ -19,6 +21,10 @@ public CategoryCreateRequest(final String name, final CategoryType categoryType) this.categoryType = categoryType.name(); } + public Category toEntity(final Member member) { + return new Category(name, member, CategoryType.from(categoryType)); + } + public String getName() { return name; } diff --git a/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoriesResponse.java b/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoriesResponse.java index 71e99893..e8162830 100644 --- a/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoriesResponse.java +++ b/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoriesResponse.java @@ -14,10 +14,10 @@ public CategoriesResponse() { public CategoriesResponse(final int page, final List categories) { this.page = page; - this.categories = convertToResponses(categories); + this.categories = toResponses(categories); } - private List convertToResponses(final List categories) { + private List toResponses(final List categories) { return categories.stream() .map(CategoryResponse::new) .collect(Collectors.toList()); diff --git a/backend/src/main/java/com/allog/dallog/domain/category/exception/DuplicatedExternalCategoryException.java b/backend/src/main/java/com/allog/dallog/domain/category/exception/DuplicatedExternalCategoryException.java deleted file mode 100644 index 9bfe6a77..00000000 --- a/backend/src/main/java/com/allog/dallog/domain/category/exception/DuplicatedExternalCategoryException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.allog.dallog.domain.category.exception; - -public class DuplicatedExternalCategoryException extends RuntimeException { - - public DuplicatedExternalCategoryException(final String message) { - super(message); - } - - public DuplicatedExternalCategoryException() { - this("이미 저장된 연동 카테고리입니다."); - } -} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/exception/ExistExternalCategoryException.java b/backend/src/main/java/com/allog/dallog/domain/category/exception/ExistExternalCategoryException.java new file mode 100644 index 00000000..748fbaeb --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/exception/ExistExternalCategoryException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.category.exception; + +public class ExistExternalCategoryException extends RuntimeException { + + public ExistExternalCategoryException(final String message) { + super(message); + } + + public ExistExternalCategoryException() { + this("이미 저장된 연동 카테고리입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/composition/application/CalendarService.java b/backend/src/main/java/com/allog/dallog/domain/composition/application/CalendarService.java deleted file mode 100644 index 7da8dc06..00000000 --- a/backend/src/main/java/com/allog/dallog/domain/composition/application/CalendarService.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.allog.dallog.domain.composition.application; - -import com.allog.dallog.domain.auth.application.OAuthClient; -import com.allog.dallog.domain.auth.domain.OAuthToken; -import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; -import com.allog.dallog.domain.auth.exception.NoSuchOAuthTokenException; -import com.allog.dallog.domain.category.domain.Category; -import com.allog.dallog.domain.category.domain.ExternalCategoryDetail; -import com.allog.dallog.domain.category.domain.ExternalCategoryDetailRepository; -import com.allog.dallog.domain.category.exception.NoSuchExternalCategoryDetailException; -import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; -import com.allog.dallog.domain.integrationschedule.dao.IntegrationScheduleDao; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; -import com.allog.dallog.domain.integrationschedule.domain.TypedSchedules; -import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; -import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponses; -import com.allog.dallog.domain.subscription.application.SubscriptionService; -import com.allog.dallog.domain.subscription.domain.Subscription; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -// TODO: 전체 리팩토링 -@Transactional(readOnly = true) -@Service -public class CalendarService { - - private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; - - private final SubscriptionService subscriptionService; - private final IntegrationScheduleDao integrationScheduleDao; - private final ExternalCategoryDetailRepository externalCategoryDetailRepository; - private final OAuthTokenRepository oAuthTokenRepository; - private final OAuthClient oAuthClient; - private final ExternalCalendarClient externalCalendarClient; - - public CalendarService(final SubscriptionService subscriptionService, - final IntegrationScheduleDao integrationScheduleDao, - final ExternalCategoryDetailRepository externalCategoryDetailRepository, - final OAuthTokenRepository oAuthTokenRepository, final OAuthClient oAuthClient, - final ExternalCalendarClient externalCalendarClient) { - this.subscriptionService = subscriptionService; - this.integrationScheduleDao = integrationScheduleDao; - this.externalCategoryDetailRepository = externalCategoryDetailRepository; - this.oAuthTokenRepository = oAuthTokenRepository; - this.oAuthClient = oAuthClient; - this.externalCalendarClient = externalCalendarClient; - } - - public List getSchedulesOfSubscriberIds(final List subscriberIds, - final DateRangeRequest dateRange) { - return subscriberIds.stream() - .flatMap(subscriberId -> getIntegrationSchedulesByMemberIdAndSubscriptions(subscriberId, - dateRange).stream()) - .distinct() - .collect(Collectors.toList()); - } - - public MemberScheduleResponses findSchedulesByMemberId(final Long memberId, - final DateRangeRequest dateRange) { - List subscriptions = subscriptionService.getAllByMemberId(memberId); - List integrationSchedules = getIntegrationSchedulesByMemberIdAndSubscriptions(memberId, - dateRange); - return new MemberScheduleResponses(subscriptions, new TypedSchedules(integrationSchedules)); - } - - private List getIntegrationSchedulesByMemberIdAndSubscriptions(final Long memberId, - final DateRangeRequest dateRange) { - List subscriptions = subscriptionService.getAllByMemberId(memberId); - List internalCategoryIds = findCheckedInternalCategories(subscriptions); - - List integrationSchedules = new ArrayList<>( - integrationScheduleDao.findByCategoryIdInAndBetween( - internalCategoryIds, dateRange.getStartDateTime(), dateRange.getEndDateTime())); - - List externalCategoryDetails = findCheckedExternalCategoryDetails(subscriptions); - if (!externalCategoryDetails.isEmpty()) { - integrationSchedules.addAll(getExternalSchedules(memberId, dateRange, externalCategoryDetails)); - } - - return integrationSchedules; - } - - private List findCheckedInternalCategories(final List subscriptions) { - return findCheckedSubscriptions(subscriptions) - .stream() - .map(Subscription::getCategory) - .filter(Category::isInternal) - .map(Category::getId) - .collect(Collectors.toList()); - } - - private List findCheckedExternalCategoryDetails(final List subscriptions) { - return findCheckedSubscriptions(subscriptions) - .stream() - .map(Subscription::getCategory) - .filter(Category::isExternal) - .map(this::getExternalCategoryDetail) - .collect(Collectors.toList()); - } - - private List findCheckedSubscriptions(final List subscriptions) { - return subscriptions.stream() - .filter(Subscription::isChecked) - .collect(Collectors.toList()); - } - - private ExternalCategoryDetail getExternalCategoryDetail(final Category category) { - return externalCategoryDetailRepository.findByCategoryId(category.getId()) - .orElseThrow(NoSuchExternalCategoryDetailException::new); - } - - private List getExternalSchedules(final Long memberId, final DateRangeRequest request, - final List externalCategories) { - String refreshToken = getOAuthToken(memberId).getRefreshToken(); - String accessToken = oAuthClient.getAccessToken(refreshToken).getAccessToken(); - - return externalCategories.stream() - .flatMap(externalCategory -> flatIntegrationSchedules(request, accessToken, externalCategory)) - .collect(Collectors.toList()); - } - - private OAuthToken getOAuthToken(final Long memberId) { - return oAuthTokenRepository.findByMemberId(memberId).orElseThrow(NoSuchOAuthTokenException::new); - } - - private Stream flatIntegrationSchedules(final DateRangeRequest dateRangeRequest, - final String accessToken, - final ExternalCategoryDetail externalCategory) { - String startDateTime = dateRangeRequest.getStartDateTime().format(DateTimeFormatter.ofPattern(DATE_FORMAT)); - String endDateTime = dateRangeRequest.getEndDateTime().format(DateTimeFormatter.ofPattern(DATE_FORMAT)); - - return externalCalendarClient.getExternalCalendarSchedules(accessToken, externalCategory.getCategory().getId(), - externalCategory.getExternalId(), startDateTime, endDateTime).stream(); - } -} diff --git a/backend/src/main/java/com/allog/dallog/domain/composition/application/CategorySubscriptionService.java b/backend/src/main/java/com/allog/dallog/domain/composition/application/CategorySubscriptionService.java deleted file mode 100644 index 62199bdd..00000000 --- a/backend/src/main/java/com/allog/dallog/domain/composition/application/CategorySubscriptionService.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.allog.dallog.domain.composition.application; - -import com.allog.dallog.domain.category.application.CategoryService; -import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; -import com.allog.dallog.domain.category.dto.request.ExternalCategoryCreateRequest; -import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.subscription.application.SubscriptionService; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Transactional(readOnly = true) -@Service -public class CategorySubscriptionService { - - private final CategoryService categoryService; - private final SubscriptionService subscriptionService; - - public CategorySubscriptionService(final CategoryService categoryService, - final SubscriptionService subscriptionService) { - this.categoryService = categoryService; - this.subscriptionService = subscriptionService; - } - - @Transactional - public CategoryResponse save(final Long memberId, final CategoryCreateRequest request) { - CategoryResponse response = categoryService.save(memberId, request); - subscriptionService.save(memberId, response.getId()); - return response; - } - - @Transactional - public CategoryResponse save(final Long memberId, final ExternalCategoryCreateRequest request) { - CategoryResponse response = categoryService.save(memberId, request); - subscriptionService.save(memberId, response.getId()); - return response; - } -} diff --git a/backend/src/main/java/com/allog/dallog/domain/composition/application/RegisterService.java b/backend/src/main/java/com/allog/dallog/domain/composition/application/RegisterService.java deleted file mode 100644 index 74b83626..00000000 --- a/backend/src/main/java/com/allog/dallog/domain/composition/application/RegisterService.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.allog.dallog.domain.composition.application; - -import static com.allog.dallog.domain.category.domain.CategoryType.PERSONAL; - -import com.allog.dallog.domain.auth.dto.OAuthMember; -import com.allog.dallog.domain.category.application.CategoryService; -import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; -import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.member.application.MemberService; -import com.allog.dallog.domain.member.domain.Member; -import com.allog.dallog.domain.member.domain.SocialType; -import com.allog.dallog.domain.member.dto.MemberResponse; -import com.allog.dallog.domain.subscription.application.SubscriptionService; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Transactional(readOnly = true) -@Service -public class RegisterService { - - private static final String PERSONAL_CATEGORY_NAME = "내 일정"; - - private final MemberService memberService; - private final CategoryService categoryService; - private final SubscriptionService subscriptionService; - - public RegisterService(final MemberService memberService, final CategoryService categoryService, - final SubscriptionService subscriptionService) { - this.memberService = memberService; - this.categoryService = categoryService; - this.subscriptionService = subscriptionService; - } - - @Transactional - public MemberResponse register(final OAuthMember oAuthMember) { - MemberResponse memberResponse = createMember(oAuthMember); - Long categoryId = createPersonalCategory(memberResponse.getId()).getId(); - subscriptionService.save(memberResponse.getId(), categoryId); - return memberResponse; - } - - private MemberResponse createMember(final OAuthMember oAuthMember) { - Member member = new Member(oAuthMember.getEmail(), oAuthMember.getDisplayName(), - oAuthMember.getProfileImageUrl(), SocialType.GOOGLE); - return memberService.save(member); - } - - private CategoryResponse createPersonalCategory(final Long memberId) { - CategoryCreateRequest categoryCreateRequest = new CategoryCreateRequest(PERSONAL_CATEGORY_NAME, PERSONAL); - return categoryService.save(memberId, categoryCreateRequest); - } - - @Transactional - public void deleteByMemberId(final Long memberId) { - categoryService.deleteByMemberId(memberId); - memberService.deleteById(memberId); - } -} diff --git a/backend/src/main/java/com/allog/dallog/domain/composition/application/SchedulerService.java b/backend/src/main/java/com/allog/dallog/domain/composition/application/SchedulerService.java deleted file mode 100644 index 7352f517..00000000 --- a/backend/src/main/java/com/allog/dallog/domain/composition/application/SchedulerService.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.allog.dallog.domain.composition.application; - -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; -import com.allog.dallog.domain.integrationschedule.domain.Period; -import com.allog.dallog.domain.member.application.MemberService; -import com.allog.dallog.domain.member.dto.MemberResponse; -import com.allog.dallog.domain.schedule.domain.scheduler.Scheduler; -import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; -import com.allog.dallog.domain.schedule.dto.response.PeriodResponse; -import com.allog.dallog.domain.subscription.application.SubscriptionService; -import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Transactional(readOnly = true) -@Service -public class SchedulerService { - - private final CalendarService calendarService; - private final SubscriptionService subscriptionService; - private final MemberService memberService; - - public SchedulerService(final CalendarService calendarService, final SubscriptionService subscriptionService, - final MemberService memberService) { - this.calendarService = calendarService; - this.subscriptionService = subscriptionService; - this.memberService = memberService; - } - - public List getAvailablePeriods(final Long categoryId, final DateRangeRequest dateRange) { - List subscriberIds = getSubscriberIds(categoryId); - List schedules = calendarService.getSchedulesOfSubscriberIds(subscriberIds, dateRange); - - Scheduler scheduler = new Scheduler(schedules, dateRange.getStartDateTime(), dateRange.getEndDateTime()); - - return convertPeriodsToPeriodResponses(scheduler.getPeriods()); - } - - private List getSubscriberIds(final Long categoryId) { - List subscriptions = subscriptionService.findByCategoryId(categoryId); - return subscriptions.stream() - .map(subscriptionResponse -> memberService.findBySubscriptionId(subscriptionResponse.getId())) - .map(MemberResponse::getId) - .collect(Collectors.toList()); - } - - private List convertPeriodsToPeriodResponses(final List periods) { - return periods.stream() - .map(PeriodResponse::new) - .collect(Collectors.toList()); - } -} diff --git a/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarClient.java b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarClient.java index d550a16e..92fdb991 100644 --- a/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarClient.java +++ b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarClient.java @@ -1,7 +1,7 @@ package com.allog.dallog.domain.externalcalendar.application; import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; import java.util.List; public interface ExternalCalendarClient { diff --git a/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarService.java b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarService.java index 1dfca38f..267b826f 100644 --- a/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarService.java +++ b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarService.java @@ -3,7 +3,6 @@ import com.allog.dallog.domain.auth.application.OAuthClient; import com.allog.dallog.domain.auth.domain.OAuthToken; import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; -import com.allog.dallog.domain.auth.dto.response.OAuthAccessTokenResponse; import com.allog.dallog.domain.auth.exception.NoSuchOAuthTokenException; import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendarsResponse; @@ -30,9 +29,9 @@ public ExternalCalendarsResponse findByMemberId(final Long memberId) { OAuthToken oAuthToken = oAuthTokenRepository.findByMemberId(memberId) .orElseThrow(NoSuchOAuthTokenException::new); - OAuthAccessTokenResponse oAuthAccessTokenResponse = oAuthClient.getAccessToken(oAuthToken.getRefreshToken()); - String oAuthAccessToken = oAuthAccessTokenResponse.getAccessToken(); - + String oAuthAccessToken = oAuthClient.getAccessToken(oAuthToken.getRefreshToken()) + .getAccessToken(); + List externalCalendars = externalCalendarClient.getExternalCalendars(oAuthAccessToken); return new ExternalCalendarsResponse(externalCalendars); diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDao.java b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDao.java deleted file mode 100644 index 38805d4a..00000000 --- a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDao.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.allog.dallog.domain.integrationschedule.dao; - -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; -import com.allog.dallog.domain.integrationschedule.domain.Period; -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.stereotype.Repository; - -@Repository -public class IntegrationScheduleDao { - - private final NamedParameterJdbcTemplate jdbcTemplate; - - public IntegrationScheduleDao(final NamedParameterJdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findByCategoryIdInAndBetween(final List categoryIds, - final LocalDateTime startDateTime, - final LocalDateTime endDateTime) { - if (categoryIds.isEmpty()) { - return Collections.emptyList(); - } - - String sql = "SELECT s.id as id, c.id as categoryId, s.title as title, " - + "s.start_date_time as startDateTime, s.end_date_time as endDateTime, " - + "s.memo as memo, c.category_type as categoryType " - + "FROM schedules s " - + "JOIN categories c ON s.categories_id = c.id " - + "WHERE c.id IN (:categoryIds) " - + "AND s.start_date_time <= :endDateTime " - + "AND s.end_date_time >= :startDateTime "; - - MapSqlParameterSource parameterSource = new MapSqlParameterSource() - .addValue("categoryIds", categoryIds) - .addValue("startDateTime", startDateTime.toString()) - .addValue("endDateTime", endDateTime.toString()); - - return jdbcTemplate.query(sql, parameterSource, generateIntegrationSchedule()); - } - - private RowMapper generateIntegrationSchedule() { - return (resultSet, rowNum) -> { - Long id = resultSet.getLong("id"); - Long categoryId = resultSet.getLong("categoryId"); - String title = resultSet.getString("title"); - LocalDateTime startDateTime = resultSet.getTimestamp("startDateTime").toLocalDateTime(); - LocalDateTime endDateTime = resultSet.getTimestamp("endDateTime").toLocalDateTime(); - String memo = resultSet.getString("memo"); - String categoryType = resultSet.getString("categoryType"); - - Period period = new Period(startDateTime, endDateTime); - - return new IntegrationSchedule(String.valueOf(id), categoryId, title, period, memo, categoryType); - }; - } -} diff --git a/backend/src/main/java/com/allog/dallog/domain/member/application/MemberService.java b/backend/src/main/java/com/allog/dallog/domain/member/application/MemberService.java index 53cd1307..d4e5b3ea 100644 --- a/backend/src/main/java/com/allog/dallog/domain/member/application/MemberService.java +++ b/backend/src/main/java/com/allog/dallog/domain/member/application/MemberService.java @@ -1,14 +1,17 @@ package com.allog.dallog.domain.member.application; import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; import com.allog.dallog.domain.member.domain.Member; import com.allog.dallog.domain.member.domain.MemberRepository; import com.allog.dallog.domain.member.dto.MemberResponse; import com.allog.dallog.domain.member.dto.MemberUpdateRequest; -import com.allog.dallog.domain.member.exception.NoSuchMemberException; +import com.allog.dallog.domain.schedule.domain.ScheduleRepository; import com.allog.dallog.domain.subscription.domain.Subscription; import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; -import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; +import java.util.List; +import java.util.stream.Collectors; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -17,29 +20,28 @@ public class MemberService { private final MemberRepository memberRepository; + private final CategoryRepository categoryRepository; + private final ScheduleRepository scheduleRepository; private final SubscriptionRepository subscriptionRepository; private final OAuthTokenRepository oAuthTokenRepository; - public MemberService(final MemberRepository memberRepository, final SubscriptionRepository subscriptionRepository, + public MemberService(final MemberRepository memberRepository, final CategoryRepository categoryRepository, + final ScheduleRepository scheduleRepository, + final SubscriptionRepository subscriptionRepository, final OAuthTokenRepository oAuthTokenRepository) { this.memberRepository = memberRepository; + this.categoryRepository = categoryRepository; + this.scheduleRepository = scheduleRepository; this.subscriptionRepository = subscriptionRepository; this.oAuthTokenRepository = oAuthTokenRepository; } - @Transactional - public MemberResponse save(final Member member) { - Member newMember = memberRepository.save(member); - return new MemberResponse(newMember); - } - public MemberResponse findById(final Long id) { - return new MemberResponse(getMember(id)); + return new MemberResponse(memberRepository.getById(id)); } public MemberResponse findBySubscriptionId(final Long subscriptionId) { - Subscription subscription = subscriptionRepository.findById(subscriptionId) - .orElseThrow(NoSuchSubscriptionException::new); + Subscription subscription = subscriptionRepository.getById(subscriptionId); Member member = subscription.getMember(); return new MemberResponse(member); @@ -47,33 +49,22 @@ public MemberResponse findBySubscriptionId(final Long subscriptionId) { @Transactional public void update(final Long id, final MemberUpdateRequest request) { - Member member = getMember(id); + Member member = memberRepository.getById(id); member.change(request.getDisplayName()); } @Transactional public void deleteById(final Long id) { - oAuthTokenRepository.deleteByMemberId(id); - memberRepository.deleteById(id); - } + List categoryIds = categoryRepository.findByMemberId(id) + .stream() + .map(Category::getId) + .collect(Collectors.toList()); - public Member getMember(final Long id) { - return memberRepository.findById(id) - .orElseThrow(NoSuchMemberException::new); - } + scheduleRepository.deleteByCategoryIdIn(categoryIds); + subscriptionRepository.deleteByCategoryIdIn(categoryIds); + categoryRepository.deleteByMemberId(id); - public Member getByEmail(final String email) { - return memberRepository.findByEmail(email) - .orElseThrow(NoSuchMemberException::new); - } - - public boolean existsByEmail(final String email) { - return memberRepository.existsByEmail(email); - } - - public void validateExistsMember(final Long id) { - if (!memberRepository.existsById(id)) { - throw new NoSuchMemberException("존재하지 않는 회원입니다."); - } + oAuthTokenRepository.deleteByMemberId(id); + memberRepository.deleteById(id); } } diff --git a/backend/src/main/java/com/allog/dallog/domain/member/domain/Member.java b/backend/src/main/java/com/allog/dallog/domain/member/domain/Member.java index 17bf3518..050f19f7 100644 --- a/backend/src/main/java/com/allog/dallog/domain/member/domain/Member.java +++ b/backend/src/main/java/com/allog/dallog/domain/member/domain/Member.java @@ -2,6 +2,7 @@ import com.allog.dallog.domain.common.BaseEntity; import com.allog.dallog.domain.member.exception.InvalidMemberException; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.Column; @@ -70,6 +71,10 @@ public void change(final String displayName) { this.displayName = displayName; } + public boolean hasSameId(final Long memberId) { + return Objects.equals(this.id, memberId); + } + public Long getId() { return id; } diff --git a/backend/src/main/java/com/allog/dallog/domain/member/domain/MemberRepository.java b/backend/src/main/java/com/allog/dallog/domain/member/domain/MemberRepository.java index 602d671b..4bc8dfd2 100644 --- a/backend/src/main/java/com/allog/dallog/domain/member/domain/MemberRepository.java +++ b/backend/src/main/java/com/allog/dallog/domain/member/domain/MemberRepository.java @@ -1,5 +1,6 @@ package com.allog.dallog.domain.member.domain; +import com.allog.dallog.domain.member.exception.NoSuchMemberException; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; @@ -8,4 +9,20 @@ public interface MemberRepository extends JpaRepository { Optional findByEmail(final String email); boolean existsByEmail(final String email); + + default Member getById(final Long id) { + return findById(id) + .orElseThrow(NoSuchMemberException::new); + } + + default Member getByEmail(final String email) { + return findByEmail(email) + .orElseThrow(NoSuchMemberException::new); + } + + default void validateExistsById(final Long id) { + if (!existsById(id)) { + throw new NoSuchMemberException(); + } + } } diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/application/ScheduleService.java b/backend/src/main/java/com/allog/dallog/domain/schedule/application/ScheduleService.java index e4771bc5..011cc586 100644 --- a/backend/src/main/java/com/allog/dallog/domain/schedule/application/ScheduleService.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/application/ScheduleService.java @@ -1,14 +1,19 @@ package com.allog.dallog.domain.schedule.application; -import com.allog.dallog.domain.auth.exception.NoPermissionException; -import com.allog.dallog.domain.category.application.CategoryService; import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; import com.allog.dallog.domain.schedule.domain.Schedule; import com.allog.dallog.domain.schedule.domain.ScheduleRepository; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; -import com.allog.dallog.domain.schedule.exception.NoSuchScheduleException; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; +import com.allog.dallog.domain.subscription.domain.Subscriptions; +import java.util.List; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -17,52 +22,62 @@ public class ScheduleService { private final ScheduleRepository scheduleRepository; - private final CategoryService categoryService; + private final CategoryRepository categoryRepository; + private final MemberRepository memberRepository; + private final SubscriptionRepository subscriptionRepository; - public ScheduleService(final ScheduleRepository scheduleRepository, final CategoryService categoryService) { + public ScheduleService(final ScheduleRepository scheduleRepository, final CategoryRepository categoryRepository, + final MemberRepository memberRepository, + final SubscriptionRepository subscriptionRepository) { this.scheduleRepository = scheduleRepository; - this.categoryService = categoryService; + this.categoryRepository = categoryRepository; + this.memberRepository = memberRepository; + this.subscriptionRepository = subscriptionRepository; } @Transactional public ScheduleResponse save(final Long memberId, final Long categoryId, final ScheduleCreateRequest request) { - Category category = categoryService.getCategory(categoryId); - categoryService.validateCreatorBy(memberId, category); - validateCategoryType(category); - + Category category = categoryRepository.getById(categoryId); + Member member = memberRepository.getById(memberId); + category.validateCanAddSchedule(member); Schedule schedule = scheduleRepository.save(request.toEntity(category)); return new ScheduleResponse(schedule); } - private static void validateCategoryType(final Category category) { - if (category.isExternal()) { - throw new NoPermissionException("외부 연동 카테고리에는 일정을 추가할 수 없습니다."); - } - } - public ScheduleResponse findById(final Long id) { - Schedule schedule = getSchedule(id); - + Schedule schedule = scheduleRepository.getById(id); return new ScheduleResponse(schedule); } - private Schedule getSchedule(Long id) { - return scheduleRepository.findById(id) - .orElseThrow(NoSuchScheduleException::new); + public List findInternalByMemberIdAndDateRange(final Long memberId, + final DateRangeRequest dateRange) { + Subscriptions subscriptions = new Subscriptions(subscriptionRepository.findByMemberId(memberId)); + return toIntegrationSchedules(dateRange, subscriptions); + } + + private List toIntegrationSchedules(final DateRangeRequest dateRange, + final Subscriptions subscriptions) { + List categories = subscriptions.findInternalCategory(); + return scheduleRepository.getByCategoriesAndBetween(categories, dateRange.getStartDateTime(), + dateRange.getEndDateTime()); } @Transactional public void update(final Long id, final Long memberId, final ScheduleUpdateRequest request) { - Schedule schedule = getSchedule(id); - categoryService.validateCreatorBy(memberId, schedule.getCategory()); - schedule.change(request.getTitle(), request.getStartDateTime(), request.getEndDateTime(), request.getMemo()); + Long categoryId = request.getCategoryId(); + categoryRepository.validateExistsByIdAndMemberId(categoryId, memberId); + + Category categoryForUpdate = categoryRepository.getById(categoryId); + Schedule schedule = scheduleRepository.getById(id); + schedule.validateEditPossible(memberId); + schedule.change(categoryForUpdate, request.getTitle(), request.getStartDateTime(), request.getEndDateTime(), + request.getMemo()); } @Transactional - public void deleteById(final Long id, final Long memberId) { - Schedule schedule = getSchedule(id); - - categoryService.validateCreatorBy(memberId, schedule.getCategory()); + public void delete(final Long id, final Long memberId) { + Schedule schedule = scheduleRepository.getById(id); + schedule.validateEditPossible(memberId); scheduleRepository.deleteById(id); } } diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/application/SubscribingSchedulesFinder.java b/backend/src/main/java/com/allog/dallog/domain/schedule/application/SubscribingSchedulesFinder.java new file mode 100644 index 00000000..9b5ec284 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/application/SubscribingSchedulesFinder.java @@ -0,0 +1,96 @@ +package com.allog.dallog.domain.schedule.application; + +import com.allog.dallog.domain.auth.application.OAuthClient; +import com.allog.dallog.domain.auth.domain.OAuthToken; +import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; +import com.allog.dallog.domain.auth.dto.response.OAuthAccessTokenResponse; +import com.allog.dallog.domain.category.application.ExternalCategoryDetailService; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetail; +import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.schedule.domain.TypedSchedules; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; +import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponses; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; +import com.allog.dallog.domain.subscription.domain.Subscriptions; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import org.springframework.stereotype.Service; + +@Service +public class SubscribingSchedulesFinder { + + private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; + + private final ScheduleService scheduleService; + private final SubscriptionRepository subscriptionRepository; + private final ExternalCategoryDetailService externalCategoryDetailService; + private final ExternalCalendarClient externalCalendarClient; + private final OAuthTokenRepository oAuthTokenRepository; + private final OAuthClient oAuthClient; + + public SubscribingSchedulesFinder(final ScheduleService scheduleService, + final SubscriptionRepository subscriptionRepository, + final ExternalCategoryDetailService externalCategoryDetailService, + final ExternalCalendarClient externalCalendarClient, + final OAuthTokenRepository oAuthTokenRepository, final OAuthClient oAuthClient) { + this.scheduleService = scheduleService; + this.subscriptionRepository = subscriptionRepository; + this.externalCategoryDetailService = externalCategoryDetailService; + this.externalCalendarClient = externalCalendarClient; + this.oAuthTokenRepository = oAuthTokenRepository; + this.oAuthClient = oAuthClient; + } + + public MemberScheduleResponses findMySubscribingSchedules(final Long memberId, final DateRangeRequest dateRange) { + List schedules = new ArrayList<>(); + + List externalSchedules = findExternalSchedules(memberId, dateRange); + List internalSchedules = scheduleService.findInternalByMemberIdAndDateRange(memberId, + dateRange); + + schedules.addAll(externalSchedules); + schedules.addAll(internalSchedules); + + Subscriptions subscriptions = new Subscriptions(subscriptionRepository.findByMemberId(memberId)); + return new MemberScheduleResponses(subscriptions, new TypedSchedules(schedules)); + } + + private List findExternalSchedules(final Long memberId, final DateRangeRequest dateRange) { + List schedules = new ArrayList<>(); + + List details = externalCategoryDetailService.findByMemberId(memberId); + if (details.isEmpty()) { + return schedules; + } + + String accessToken = getExternalAccessToken(memberId); + for (ExternalCategoryDetail detail : details) { + List externalSchedules = findExternalSchedules(dateRange, accessToken, detail); + schedules.addAll(externalSchedules); + } + return schedules; + } + + private String getExternalAccessToken(final Long memberId) { + OAuthToken oAuthToken = oAuthTokenRepository.getByMemberId(memberId); + String refreshToken = oAuthToken.getRefreshToken(); + + OAuthAccessTokenResponse accessTokenResponse = oAuthClient.getAccessToken(refreshToken); + return accessTokenResponse.getAccessToken(); + } + + private List findExternalSchedules(final DateRangeRequest dateRange, final String accessToken, + final ExternalCategoryDetail detail) { + LocalDateTime startDateTime = dateRange.getStartDateTime(); + LocalDateTime endDateTime = dateRange.getEndDateTime(); + + String startTime = startDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + String endTime = endDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + + return externalCalendarClient.getExternalCalendarSchedules( + accessToken, detail.getCategory().getId(), detail.getExternalId(), startTime, endTime); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedule.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/IntegrationSchedule.java similarity index 65% rename from backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedule.java rename to backend/src/main/java/com/allog/dallog/domain/schedule/domain/IntegrationSchedule.java index 190c84b8..f5e1b073 100644 --- a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedule.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/IntegrationSchedule.java @@ -1,34 +1,40 @@ -package com.allog.dallog.domain.integrationschedule.domain; +package com.allog.dallog.domain.schedule.domain; import com.allog.dallog.domain.category.domain.Category; -import com.allog.dallog.domain.category.exception.NoSuchCategoryException; -import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.category.domain.CategoryType; import com.allog.dallog.domain.subscription.domain.Subscription; import java.time.LocalDateTime; -import java.util.List; import java.util.Objects; public class IntegrationSchedule { private static final int ONE_DAY = 1; - private static final int MIDNIGHT_HOUR = 23; - private static final int MIDNIGHT_MINUTE = 59; private final String id; private final Long categoryId; private final String title; private final Period period; private final String memo; - private final String categoryType; + private final CategoryType categoryType; + + public IntegrationSchedule(final Schedule schedule) { + this.id = String.valueOf(schedule.getId()); + Category category = schedule.getCategory(); + this.categoryId = category.getId(); + this.title = schedule.getTitle(); + this.period = new Period(schedule.getStartDateTime(), schedule.getEndDateTime()); + this.memo = schedule.getMemo(); + this.categoryType = category.getCategoryType(); + } public IntegrationSchedule(final String id, final Long categoryId, final String title, final LocalDateTime startDateTime, final LocalDateTime endDateTime, final String memo, - final String categoryType) { + final CategoryType categoryType) { this(id, categoryId, title, new Period(startDateTime, endDateTime), memo, categoryType); } public IntegrationSchedule(final String id, final Long categoryId, final String title, final Period period, - final String memo, final String categoryType) { + final String memo, final CategoryType categoryType) { this.id = id; this.categoryId = categoryId; this.title = title; @@ -38,29 +44,20 @@ public IntegrationSchedule(final String id, final Long categoryId, final String } public boolean isLongTerms() { - return period.calculateDayDifference() >= ONE_DAY; + return !isAllDays() + && period.calculateDayDifference() >= ONE_DAY; } public boolean isAllDays() { - return period.calculateDayDifference() < ONE_DAY - && period.calculateHourDifference() == MIDNIGHT_HOUR - && period.calculateMinuteDifference() == MIDNIGHT_MINUTE; + return period.calculateDayDifference() == ONE_DAY + && period.isMidnightToMidnight(); } public boolean isFewHours() { - return !isAllDays() - && period.calculateDayDifference() < ONE_DAY; + return period.calculateDayDifference() < ONE_DAY; } - public Color findSubscriptionColor(final List subscriptions) { - return subscriptions.stream() - .filter(this::isSameCategory) - .findAny() - .orElseThrow(() -> new NoSuchCategoryException("구독하지 않은 카테고리 입니다.")) - .getColor(); - } - - private boolean isSameCategory(final Subscription subscription) { + public boolean isSameCategory(final Subscription subscription) { Category category = subscription.getCategory(); return category.getId().equals(categoryId); } @@ -93,12 +90,10 @@ public String getMemo() { return memo; } - public String getCategoryType() { + public CategoryType getCategoryType() { return categoryType; } - // 중복 일정을 판별하기 위한 equals & hashCode - @Override public boolean equals(final Object o) { if (this == o) { diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleComparator.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/IntegrationScheduleComparator.java similarity index 56% rename from backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleComparator.java rename to backend/src/main/java/com/allog/dallog/domain/schedule/domain/IntegrationScheduleComparator.java index 70d33096..e0731edc 100644 --- a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleComparator.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/IntegrationScheduleComparator.java @@ -1,42 +1,33 @@ -package com.allog.dallog.domain.integrationschedule.domain; +package com.allog.dallog.domain.schedule.domain; import java.time.LocalDateTime; import java.util.Comparator; public class IntegrationScheduleComparator implements Comparator { - private static final int BEFORE = -1; - private static final int AFTER = 1; - private static final int SAME = 0; + private static final int SAME_CONDITION = 0; @Override public int compare(final IntegrationSchedule firstSchedule, final IntegrationSchedule secondSchedule) { LocalDateTime firstScheduleStartDateTime = firstSchedule.getStartDateTime(); LocalDateTime secondScheduleStartDateTime = secondSchedule.getStartDateTime(); - if (firstScheduleStartDateTime.isBefore(secondScheduleStartDateTime)) { - return BEFORE; - } - if (firstScheduleStartDateTime.isAfter(secondScheduleStartDateTime)) { - return AFTER; - } - if (firstScheduleStartDateTime.isEqual(secondScheduleStartDateTime)) { + int cmp = firstScheduleStartDateTime.compareTo(secondScheduleStartDateTime); + if (cmp == SAME_CONDITION) { return compareEndDateTime(firstSchedule, secondSchedule); } - return SAME; + return cmp; } private int compareEndDateTime(IntegrationSchedule firstSchedule, IntegrationSchedule secondSchedule) { LocalDateTime firstScheduleEndDateTime = firstSchedule.getEndDateTime(); LocalDateTime secondScheduleEndDateTime = secondSchedule.getEndDateTime(); - if (firstScheduleEndDateTime.isBefore(secondScheduleEndDateTime)) { - return AFTER; - } - if (firstScheduleEndDateTime.isAfter(secondScheduleEndDateTime)) { - return BEFORE; + int cmp = secondScheduleEndDateTime.compareTo(firstScheduleEndDateTime); + if (cmp == SAME_CONDITION) { + return compareByTitle(firstSchedule, secondSchedule); } - return compareByTitle(firstSchedule, secondSchedule); + return cmp; } private int compareByTitle(IntegrationSchedule firstSchedule, IntegrationSchedule secondSchedule) { diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedules.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/IntegrationSchedules.java similarity index 90% rename from backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedules.java rename to backend/src/main/java/com/allog/dallog/domain/schedule/domain/IntegrationSchedules.java index b6d42044..257bbd3d 100644 --- a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedules.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/IntegrationSchedules.java @@ -1,4 +1,4 @@ -package com.allog.dallog.domain.integrationschedule.domain; +package com.allog.dallog.domain.schedule.domain; import java.util.ArrayList; import java.util.List; diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/Period.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/Period.java similarity index 81% rename from backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/Period.java rename to backend/src/main/java/com/allog/dallog/domain/schedule/domain/Period.java index 769ae36c..b44a969d 100644 --- a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/Period.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/Period.java @@ -1,4 +1,6 @@ -package com.allog.dallog.domain.integrationschedule.domain; +package com.allog.dallog.domain.schedule.domain; + +import static java.time.LocalTime.MIDNIGHT; import java.time.LocalDate; import java.time.LocalDateTime; @@ -10,8 +12,6 @@ public class Period { - private static final int ONE_HOUR = 60; - private final LocalDateTime startDateTime; private final LocalDateTime endDateTime; @@ -26,26 +26,31 @@ public long calculateDayDifference() { return ChronoUnit.DAYS.between(startDate, endDate); } - public long calculateHourDifference() { - LocalTime startTime = LocalTime.from(startDateTime); - LocalTime endTime = LocalTime.from(endDateTime); - return ChronoUnit.HOURS.between(startTime, endTime); - } - - public long calculateMinuteDifference() { + public boolean isMidnightToMidnight() { LocalTime startTime = LocalTime.from(startDateTime); LocalTime endTime = LocalTime.from(endDateTime); - return ChronoUnit.MINUTES.between(startTime, endTime) % ONE_HOUR; + return startTime.equals(MIDNIGHT) && endTime.equals(MIDNIGHT); } public List slice(final Period otherPeriod) { if (isNotOverlapped(otherPeriod)) { return List.of(this); } - return sliceByOtherPeriod(otherPeriod); } + private boolean isNotOverlapped(final Period otherPeriod) { + // other가 좌측 방향으로 멀리 떨어져 겹치지 않을때 + boolean farFromLeftSideOfBase = otherPeriod.endDateTime + .isBefore(startDateTime); + + // other가 우측 방향으로 멀리 떨어져 겹치지 않을때 + boolean farFromRightSideOfBase = otherPeriod.startDateTime + .isAfter(endDateTime); + + return farFromLeftSideOfBase || farFromRightSideOfBase; + } + private List sliceByOtherPeriod(final Period otherPeriod) { List periods = new ArrayList<>(); if (startDateTime.isBefore(otherPeriod.startDateTime)) { @@ -59,16 +64,6 @@ private List sliceByOtherPeriod(final Period otherPeriod) { return periods; } - private boolean isNotOverlapped(final Period otherPeriod) { - boolean farFromLeftSideOfBase = otherPeriod.endDateTime.isBefore(startDateTime); - // other가 좌측 방향으로 멀리 떨어져 겹치지 않을때 - - boolean farFromRightSideOfBase = otherPeriod.startDateTime.isAfter(endDateTime); - // other가 우측 방향으로 멀리 떨어져 겹치지 않을때 - - return farFromLeftSideOfBase || farFromRightSideOfBase; - } - public LocalDateTime getStartDateTime() { return startDateTime; } diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/domain/Schedule.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/Schedule.java index 1f8da026..4ed39f83 100644 --- a/backend/src/main/java/com/allog/dallog/domain/schedule/domain/Schedule.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/Schedule.java @@ -1,5 +1,6 @@ package com.allog.dallog.domain.schedule.domain; +import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.category.domain.Category; import com.allog.dallog.domain.common.BaseEntity; import com.allog.dallog.domain.schedule.exception.InvalidScheduleException; @@ -57,11 +58,12 @@ public Schedule(final Category category, final String title, final LocalDateTime this.memo = memo; } - public void change(final String title, final LocalDateTime startDateTime, final LocalDateTime endDateTime, - final String memo) { + public void change(final Category category, final String title, final LocalDateTime startDateTime, + final LocalDateTime endDateTime, final String memo) { validateTitleLength(title); validatePeriod(startDateTime, endDateTime); validateMemoLength(memo); + this.category = category; this.title = title; this.startDateTime = startDateTime; this.endDateTime = endDateTime; @@ -86,6 +88,12 @@ private void validateMemoLength(final String memo) { } } + public void validateEditPossible(final Long memberId) { + if (!category.isCreatorId(memberId)) { + throw new NoPermissionException(); + } + } + public Long getId() { return id; } diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleRepository.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleRepository.java index a45e93bf..6ba03d94 100644 --- a/backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleRepository.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleRepository.java @@ -1,9 +1,42 @@ package com.allog.dallog.domain.schedule.domain; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.schedule.exception.NoSuchScheduleException; +import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; public interface ScheduleRepository extends JpaRepository { void deleteByCategoryIdIn(final List categoryIds); + + @Query("SELECT s " + + "FROM Schedule s " + + "JOIN s.category c " + + "WHERE c IN :categories " + + "AND s.startDateTime <= :endDate " + + "AND s.endDateTime >= :startDate") + List findByCategoriesAndBetween(final List categories, final LocalDateTime startDate, + final LocalDateTime endDate); + + default Schedule getById(final Long id) { + return this.findById(id) + .orElseThrow(NoSuchScheduleException::new); + } + + default List getByCategoriesAndBetween(final List categories, + final LocalDateTime startDateTime, + final LocalDateTime endDateTime) { + if (categories.isEmpty()) { + return new ArrayList<>(); + } + + List schedules = findByCategoriesAndBetween(categories, startDateTime, endDateTime); + return schedules.stream() + .map(IntegrationSchedule::new) + .collect(Collectors.toList()); + } } diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/ScheduleType.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleType.java similarity index 93% rename from backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/ScheduleType.java rename to backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleType.java index 1605103c..eedebc3c 100644 --- a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/ScheduleType.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleType.java @@ -1,4 +1,4 @@ -package com.allog.dallog.domain.integrationschedule.domain; +package com.allog.dallog.domain.schedule.domain; import java.util.Arrays; import java.util.function.Predicate; diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/TypedSchedules.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/TypedSchedules.java similarity index 80% rename from backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/TypedSchedules.java rename to backend/src/main/java/com/allog/dallog/domain/schedule/domain/TypedSchedules.java index de5ad6c4..4199f832 100644 --- a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/TypedSchedules.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/TypedSchedules.java @@ -1,4 +1,4 @@ -package com.allog.dallog.domain.integrationschedule.domain; +package com.allog.dallog.domain.schedule.domain; import java.util.HashMap; import java.util.List; @@ -12,8 +12,8 @@ public TypedSchedules(final List integrationSchedules) { initializeValues(); for (IntegrationSchedule integrationSchedule : integrationSchedules) { ScheduleType scheduleType = ScheduleType.from(integrationSchedule); - IntegrationSchedules sortedSchedules = values.get(scheduleType); - sortedSchedules.add(integrationSchedule); + IntegrationSchedules schedules = values.get(scheduleType); + schedules.add(integrationSchedule); } } diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/domain/scheduler/Scheduler.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/scheduler/Scheduler.java index 4b39a6e8..c644c0cb 100644 --- a/backend/src/main/java/com/allog/dallog/domain/schedule/domain/scheduler/Scheduler.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/scheduler/Scheduler.java @@ -1,7 +1,7 @@ package com.allog.dallog.domain.schedule.domain.scheduler; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; -import com.allog.dallog.domain.integrationschedule.domain.Period; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.schedule.domain.Period; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleUpdateRequest.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleUpdateRequest.java index 3744671b..05d1aee4 100644 --- a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleUpdateRequest.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleUpdateRequest.java @@ -1,12 +1,14 @@ package com.allog.dallog.domain.schedule.dto.request; import java.time.LocalDateTime; -import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import org.springframework.format.annotation.DateTimeFormat; public class ScheduleUpdateRequest { + @NotNull(message = "Null일 수 없습니다.") + private long categoryId; + @NotNull(message = "Null일 수 없습니다.") private String title; @@ -22,8 +24,9 @@ public class ScheduleUpdateRequest { private ScheduleUpdateRequest() { } - public ScheduleUpdateRequest(final String title, final LocalDateTime startDateTime, final LocalDateTime endDateTime, - final String memo) { + public ScheduleUpdateRequest(final Long categoryId, final String title, final LocalDateTime startDateTime, + final LocalDateTime endDateTime, final String memo) { + this.categoryId = categoryId; this.title = title; this.startDateTime = startDateTime; this.endDateTime = endDateTime; @@ -45,4 +48,8 @@ public LocalDateTime getEndDateTime() { public String getMemo() { return memo; } + + public Long getCategoryId() { + return categoryId; + } } diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponse.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponse.java index 82299165..3d1925a2 100644 --- a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponse.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponse.java @@ -1,6 +1,6 @@ package com.allog.dallog.domain.schedule.dto.response; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; import com.allog.dallog.domain.subscription.domain.Color; import java.time.LocalDateTime; @@ -18,7 +18,8 @@ public class MemberScheduleResponse { public MemberScheduleResponse(final IntegrationSchedule integrationSchedule, final Color color) { this(integrationSchedule.getId(), integrationSchedule.getTitle(), integrationSchedule.getStartDateTime(), integrationSchedule.getEndDateTime(), integrationSchedule.getMemo(), - integrationSchedule.getCategoryId(), color.getColorCode(), integrationSchedule.getCategoryType()); + integrationSchedule.getCategoryId(), color.getColorCode(), + integrationSchedule.getCategoryType().name()); } public MemberScheduleResponse(final String id, final String title, final LocalDateTime startDateTime, diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponses.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponses.java index 0f63ec4c..579b4319 100644 --- a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponses.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponses.java @@ -1,14 +1,13 @@ package com.allog.dallog.domain.schedule.dto.response; -import com.allog.dallog.domain.integrationschedule.domain.ScheduleType; -import com.allog.dallog.domain.integrationschedule.domain.TypedSchedules; -import com.allog.dallog.domain.subscription.domain.Subscription; +import com.allog.dallog.domain.schedule.domain.ScheduleType; +import com.allog.dallog.domain.schedule.domain.TypedSchedules; +import com.allog.dallog.domain.subscription.domain.Subscriptions; import java.util.List; import java.util.stream.Collectors; public class MemberScheduleResponses { - // TODO: 리팩토링 private final List longTerms; private final List allDays; private final List fewHours; @@ -21,19 +20,19 @@ public MemberScheduleResponses(final List longTerms, this.fewHours = fewHours; } - public MemberScheduleResponses(final List subscriptions, final TypedSchedules typedSchedules) { + public MemberScheduleResponses(final Subscriptions subscriptions, final TypedSchedules typedSchedules) { this.longTerms = getColoredScheduleResponses(ScheduleType.LONG_TERMS, subscriptions, typedSchedules); this.allDays = getColoredScheduleResponses(ScheduleType.ALL_DAYS, subscriptions, typedSchedules); this.fewHours = getColoredScheduleResponses(ScheduleType.FEW_HOURS, subscriptions, typedSchedules); } private List getColoredScheduleResponses(final ScheduleType scheduleType, - final List subscriptions, + final Subscriptions subscriptions, final TypedSchedules typedSchedules) { return typedSchedules.getSortedSchedules(scheduleType) .getSortedValues() .stream() - .map(schedule -> new MemberScheduleResponse(schedule, schedule.findSubscriptionColor(subscriptions))) + .map(schedule -> new MemberScheduleResponse(schedule, subscriptions.findColor(schedule))) .collect(Collectors.toList()); } diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/PeriodResponse.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/PeriodResponse.java index 3d79d5e0..7c13cf1c 100644 --- a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/PeriodResponse.java +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/PeriodResponse.java @@ -1,6 +1,6 @@ package com.allog.dallog.domain.schedule.dto.response; -import com.allog.dallog.domain.integrationschedule.domain.Period; +import com.allog.dallog.domain.schedule.domain.Period; import java.time.LocalDateTime; public class PeriodResponse { diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/application/ColorPicker.java b/backend/src/main/java/com/allog/dallog/domain/subscription/application/ColorPicker.java new file mode 100644 index 00000000..1726d608 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/application/ColorPicker.java @@ -0,0 +1,7 @@ +package com.allog.dallog.domain.subscription.application; + +@FunctionalInterface +public interface ColorPicker { + + int pickNumber(); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/application/RandomColorPicker.java b/backend/src/main/java/com/allog/dallog/domain/subscription/application/RandomColorPicker.java new file mode 100644 index 00000000..4874072f --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/application/RandomColorPicker.java @@ -0,0 +1,16 @@ +package com.allog.dallog.domain.subscription.application; + +import com.allog.dallog.domain.subscription.domain.Color; +import java.util.concurrent.ThreadLocalRandom; +import org.springframework.stereotype.Component; + +@Component +public class RandomColorPicker implements ColorPicker { + + @Override + public int pickNumber() { + ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current(); + Color[] colors = Color.values(); + return threadLocalRandom.nextInt(colors.length); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/application/SubscriptionService.java b/backend/src/main/java/com/allog/dallog/domain/subscription/application/SubscriptionService.java index 85b3f064..17806a8a 100644 --- a/backend/src/main/java/com/allog/dallog/domain/subscription/application/SubscriptionService.java +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/application/SubscriptionService.java @@ -1,23 +1,16 @@ package com.allog.dallog.domain.subscription.application; -import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.category.domain.Category; import com.allog.dallog.domain.category.domain.CategoryRepository; -import com.allog.dallog.domain.category.exception.NoSuchCategoryException; import com.allog.dallog.domain.member.domain.Member; import com.allog.dallog.domain.member.domain.MemberRepository; -import com.allog.dallog.domain.member.exception.NoSuchMemberException; import com.allog.dallog.domain.subscription.domain.Color; -import com.allog.dallog.domain.subscription.domain.ColorPickerStrategy; import com.allog.dallog.domain.subscription.domain.Subscription; import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; import com.allog.dallog.domain.subscription.dto.request.SubscriptionUpdateRequest; import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; import com.allog.dallog.domain.subscription.dto.response.SubscriptionsResponse; -import com.allog.dallog.domain.subscription.exception.ExistSubscriptionException; -import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; import java.util.List; -import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,46 +19,36 @@ @Service public class SubscriptionService { - private static final ColorPickerStrategy PICK_RANDOM_STRATEGY - = () -> ThreadLocalRandom.current().nextInt(Color.values().length); - private final SubscriptionRepository subscriptionRepository; private final MemberRepository memberRepository; private final CategoryRepository categoryRepository; + private final ColorPicker colorPicker; public SubscriptionService(final SubscriptionRepository subscriptionRepository, - final MemberRepository memberRepository, - final CategoryRepository categoryRepository) { + final MemberRepository memberRepository, final CategoryRepository categoryRepository, + final ColorPicker colorPicker) { this.subscriptionRepository = subscriptionRepository; this.memberRepository = memberRepository; this.categoryRepository = categoryRepository; + this.colorPicker = colorPicker; } @Transactional public SubscriptionResponse save(final Long memberId, final Long categoryId) { - validateAlreadyExists(memberId, categoryId); - - Member member = memberRepository.findById(memberId) - .orElseThrow(NoSuchMemberException::new); - Category category = categoryRepository.findById(categoryId) - .orElseThrow(NoSuchCategoryException::new); - validatePermission(memberId, category); + subscriptionRepository.validateNotExistsByMemberIdAndCategoryId(memberId, categoryId); - Color color = Color.pickAny(PICK_RANDOM_STRATEGY); - Subscription subscription = subscriptionRepository.save(new Subscription(member, category, color)); - return new SubscriptionResponse(subscription); - } + Member member = memberRepository.getById(memberId); + Category category = categoryRepository.getById(categoryId); + category.validateSubscriptionPossible(member); - private void validateAlreadyExists(final Long memberId, final Long categoryId) { - if (subscriptionRepository.existsByMemberIdAndCategoryId(memberId, categoryId)) { - throw new ExistSubscriptionException(); - } + Color color = Color.pick(colorPicker.pickNumber()); + Subscription savedSubscription = subscriptionRepository.save(new Subscription(member, category, color)); + return new SubscriptionResponse(savedSubscription); } - private void validatePermission(final Long memberId, final Category category) { - if (category.isPersonal() && !category.isCreator(memberId)) { - throw new NoPermissionException("구독 권한이 없는 카테고리입니다."); - } + public SubscriptionResponse findById(final Long id) { + Subscription subscription = subscriptionRepository.getById(id); + return new SubscriptionResponse(subscription); } public SubscriptionsResponse findByMemberId(final Long memberId) { @@ -78,12 +61,6 @@ public SubscriptionsResponse findByMemberId(final Long memberId) { return new SubscriptionsResponse(subscriptionResponses); } - public SubscriptionResponse findById(final Long id) { - Subscription subscription = getSubscription(id); - - return new SubscriptionResponse(subscription); - } - public List findByCategoryId(final Long categoryId) { return subscriptionRepository.findByCategoryId(categoryId) .stream() @@ -91,42 +68,17 @@ public List findByCategoryId(final Long categoryId) { .collect(Collectors.toList()); } - public List getAllByMemberId(final Long memberId) { - return subscriptionRepository.findByMemberId(memberId); - } - @Transactional public void update(final Long id, final Long memberId, final SubscriptionUpdateRequest request) { - validateSubscriptionPermission(id, memberId); - - Subscription subscription = getSubscription(id); + subscriptionRepository.validateExistsByIdAndMemberId(id, memberId); + Subscription subscription = subscriptionRepository.getById(id); subscription.change(request.getColor(), request.isChecked()); } - private Subscription getSubscription(final Long id) { - return subscriptionRepository.findById(id) - .orElseThrow(NoSuchSubscriptionException::new); - } - @Transactional - public void deleteById(final Long id, final Long memberId) { - Subscription subscription = getSubscription(id); - - validateSubscriptionPermission(id, memberId); - validateCategoryCreator(subscription.getCategory(), memberId); - + public void delete(final Long id, final Long memberId) { + Subscription subscription = subscriptionRepository.getById(id); + subscription.validateDeletePossible(memberId); subscriptionRepository.deleteById(id); } - - private void validateSubscriptionPermission(final Long id, final Long memberId) { - if (!subscriptionRepository.existsByIdAndMemberId(id, memberId)) { - throw new NoPermissionException(); - } - } - - private void validateCategoryCreator(final Category category, final Long memberId) { - if (category.isCreator(memberId)) { - throw new NoPermissionException("내가 만든 카테고리는 구독 취소 할 수 없습니다."); - } - } } diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Color.java b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Color.java index 7dabec95..6a6f9f70 100644 --- a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Color.java +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Color.java @@ -36,8 +36,8 @@ public enum Color { this.colorCode = colorCode; } - public static Color pickAny(ColorPickerStrategy strategy) { - return Color.values()[strategy.pickNumber()]; + public static Color pick(int index) { + return Color.values()[index]; } public static Color from(final String colorCode) { diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/ColorPickerStrategy.java b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/ColorPickerStrategy.java deleted file mode 100644 index 3365655f..00000000 --- a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/ColorPickerStrategy.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.allog.dallog.domain.subscription.domain; - -@FunctionalInterface -public interface ColorPickerStrategy { - - int pickNumber(); -} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscription.java b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscription.java index 6379880d..a324ca8d 100644 --- a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscription.java +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscription.java @@ -1,5 +1,6 @@ package com.allog.dallog.domain.subscription.domain; +import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.category.domain.Category; import com.allog.dallog.domain.common.BaseEntity; import com.allog.dallog.domain.member.domain.Member; @@ -18,7 +19,7 @@ @Table(name = "subscriptions") @Entity public class Subscription extends BaseEntity { - + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @@ -54,6 +55,20 @@ public void change(final Color color, final boolean checked) { this.checked = checked; } + public void validateDeletePossible(final Long memberId) { + if (category.isCreatorId(memberId)) { + throw new NoPermissionException("내가 만든 카테고리는 구독 취소 할 수 없습니다."); + } + } + + public boolean hasInternalCategory() { + return category.isInternal(); + } + + public boolean hasExternalCategory() { + return category.isExternal(); + } + public Long getId() { return id; } diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepository.java b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepository.java index f6217765..f17a9687 100644 --- a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepository.java +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepository.java @@ -1,5 +1,8 @@ package com.allog.dallog.domain.subscription.domain; +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.subscription.exception.ExistSubscriptionException; +import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; @@ -7,11 +10,32 @@ public interface SubscriptionRepository extends JpaRepository findByMemberId(final Long memberId); List findByCategoryId(final Long categoryId); - boolean existsByIdAndMemberId(final Long id, final Long memberId); + List findByMemberIdIn(final List memberIds); void deleteByCategoryIdIn(final List categoryIds); + + default Subscription getById(final Long id) { + return findById(id) + .orElseThrow(NoSuchSubscriptionException::new); + } + + default void validateNotExistsByMemberIdAndCategoryId(final Long memberId, final Long categoryId) { + if (existsByMemberIdAndCategoryId(memberId, categoryId)) { + throw new ExistSubscriptionException(); + } + } + + default void validateExistsByIdAndMemberId(final Long id, final Long memberId) { + if (!existsByIdAndMemberId(id, memberId)) { + throw new NoPermissionException(); + } + } } diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscriptions.java b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscriptions.java new file mode 100644 index 00000000..3b85cea2 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscriptions.java @@ -0,0 +1,40 @@ +package com.allog.dallog.domain.subscription.domain; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; +import java.util.List; +import java.util.stream.Collectors; + +public class Subscriptions { + + private final List subscriptions; + + public Subscriptions(final List subscriptions) { + this.subscriptions = subscriptions; + } + + public List findInternalCategory() { + return subscriptions.stream() + .filter(Subscription::isChecked) + .filter(Subscription::hasInternalCategory) + .map(Subscription::getCategory) + .collect(Collectors.toList()); + } + + public List findExternalCategory() { + return subscriptions.stream() + .filter(Subscription::isChecked) + .filter(Subscription::hasExternalCategory) + .map(Subscription::getCategory) + .collect(Collectors.toList()); + } + + public Color findColor(final IntegrationSchedule schedule) { + return subscriptions.stream() + .filter(schedule::isSameCategory) + .findAny() + .orElseThrow(() -> new NoSuchCategoryException("구독하지 않은 카테고리 입니다.")) + .getColor(); + } +} diff --git a/backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java b/backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java index d023ac4e..fc557f2d 100644 --- a/backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java +++ b/backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java @@ -4,7 +4,7 @@ import com.allog.dallog.domain.auth.exception.InvalidTokenException; import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.auth.exception.NoSuchOAuthTokenException; -import com.allog.dallog.domain.category.exception.DuplicatedExternalCategoryException; +import com.allog.dallog.domain.category.exception.ExistExternalCategoryException; import com.allog.dallog.domain.category.exception.InvalidCategoryException; import com.allog.dallog.domain.category.exception.NoSuchCategoryException; import com.allog.dallog.domain.member.exception.InvalidMemberException; @@ -41,7 +41,7 @@ public class ControllerAdvice { InvalidScheduleException.class, InvalidSubscriptionException.class, ExistSubscriptionException.class, - DuplicatedExternalCategoryException.class + ExistExternalCategoryException.class }) public ResponseEntity handleInvalidData(final RuntimeException e) { ErrorResponse errorResponse = new ErrorResponse(e.getMessage()); diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java index 2fefc06e..500173ed 100644 --- a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java @@ -1,14 +1,11 @@ package com.allog.dallog.infrastructure.oauth.client; -import com.allog.dallog.domain.category.domain.CategoryType; import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; -import com.allog.dallog.infrastructure.oauth.dto.GoogleCalendarEventResponse; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; import com.allog.dallog.infrastructure.oauth.dto.GoogleCalendarEventsResponse; import com.allog.dallog.infrastructure.oauth.dto.GoogleCalendarListResponse; import com.allog.dallog.infrastructure.oauth.exception.OAuthException; -import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -58,17 +55,17 @@ private ResponseEntity fetchGoogleCalendarList(final @Override public List getExternalCalendarSchedules(final String accessToken, final Long internalCategoryId, - final String calendarId, + final String externalCalendarId, final String startDateTime, final String endDateTime) { HttpEntity request = new HttpEntity<>(generateCalendarRequestHeaders(accessToken)); + Map uriVariables = generateEventsVariables(externalCalendarId, startDateTime, endDateTime); - Map uriVariables = generateEventsVariables(calendarId, startDateTime, endDateTime); - GoogleCalendarEventsResponse response = fetchGoogleCalendarEvents(uriVariables, request).getBody(); + GoogleCalendarEventsResponse response = fetchGoogleCalendarEvents(request, uriVariables).getBody(); return response.getItems() .stream() - .map(event -> parseIntegrationSchedule(internalCategoryId, event)) + .map(event -> event.toIntegrationSchedule(internalCategoryId)) .collect(Collectors.toList()); } @@ -89,7 +86,7 @@ private Map generateEventsVariables(final String externalCalenda } private ResponseEntity fetchGoogleCalendarEvents( - final Map uriVariables, final HttpEntity request) { + final HttpEntity request, final Map uriVariables) { try { return restTemplate.exchange(CALENDAR_EVENTS_REQUEST_URI, HttpMethod.GET, request, GoogleCalendarEventsResponse.class, uriVariables); @@ -97,21 +94,4 @@ private ResponseEntity fetchGoogleCalendarEvents( throw new OAuthException(e); } } - - private IntegrationSchedule parseIntegrationSchedule(final Long internalCategoryId, - final GoogleCalendarEventResponse event) { - LocalDateTime startDateTime = event.getStartDateTime(); - LocalDateTime endDateTime = event.getEndDateTime(); - if (isAllDay(startDateTime, endDateTime)) { - endDateTime = endDateTime.minusMinutes(1); - } - - return new IntegrationSchedule(event.getId(), internalCategoryId, event.getSummary(), startDateTime, - endDateTime, event.getDescription(), CategoryType.GOOGLE.name()); - } - - private boolean isAllDay(final LocalDateTime startDateTime, final LocalDateTime endDateTime) { - return startDateTime.getHour() == 0 && startDateTime.getMinute() == 0 - && endDateTime.getHour() == 0 && endDateTime.getMinute() == 0; - } } diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java index cecbdbb0..29134127 100644 --- a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java @@ -1,5 +1,7 @@ package com.allog.dallog.infrastructure.oauth.dto; +import com.allog.dallog.domain.category.domain.CategoryType; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -7,39 +9,31 @@ public class GoogleCalendarEventResponse { - private String kind; - private String etag; private String id; - private String status; - private String htmlLink; private String summary = ""; private String description = ""; - private String location; private GoogleDateFormat start; private GoogleDateFormat end; - private String recurringEventId; private GoogleCalendarEventResponse() { } - public GoogleCalendarEventResponse(final String kind, final String etag, final String id, final String status, - final String htmlLink, final String summary, final String description, - final String location, final GoogleDateFormat start, final GoogleDateFormat end, - final String recurringEventId) { - this.kind = kind; - this.etag = etag; + public GoogleCalendarEventResponse(final String id, final String summary, final String description, + final GoogleDateFormat start, + final GoogleDateFormat end) { this.id = id; - this.status = status; - this.htmlLink = htmlLink; this.summary = summary; this.description = description; - this.location = location; this.start = start; this.end = end; - this.recurringEventId = recurringEventId; } - public LocalDateTime getStartDateTime() { + public IntegrationSchedule toIntegrationSchedule(final Long internalCategoryId) { + return new IntegrationSchedule(id, internalCategoryId, summary, getStartDateTime(), getEndDateTime(), + description, CategoryType.GOOGLE); + } + + private LocalDateTime getStartDateTime() { if (Objects.isNull(start.getDate())) { return LocalDateTime.parse(start.getDateTime().substring(0, 19)); } @@ -47,7 +41,7 @@ public LocalDateTime getStartDateTime() { return LocalDateTime.of(LocalDate.parse(start.getDate()), LocalTime.MIN); } - public LocalDateTime getEndDateTime() { + private LocalDateTime getEndDateTime() { if (Objects.isNull(end.getDate())) { return LocalDateTime.parse(end.getDateTime().substring(0, 19)); } @@ -55,26 +49,10 @@ public LocalDateTime getEndDateTime() { return LocalDateTime.of(LocalDate.parse(end.getDate()), LocalTime.MIN); } - public String getKind() { - return kind; - } - - public String getEtag() { - return etag; - } - public String getId() { return id; } - public String getStatus() { - return status; - } - - public String getHtmlLink() { - return htmlLink; - } - public String getSummary() { return summary; } @@ -83,10 +61,6 @@ public String getDescription() { return description; } - public String getLocation() { - return location; - } - public GoogleDateFormat getStart() { return start; } @@ -94,8 +68,4 @@ public GoogleDateFormat getStart() { public GoogleDateFormat getEnd() { return end; } - - public String getRecurringEventId() { - return recurringEventId; - } } diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java index ffb3fcda..5f72f9b4 100644 --- a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java @@ -4,66 +4,15 @@ public class GoogleCalendarEventsResponse { - private String kind; - private String etag; - private String summary; - private String description; - private String timeZone; - private String accessRole; - private String nextPageToken; - private String nextSyncToken; private List items; private GoogleCalendarEventsResponse() { } - public GoogleCalendarEventsResponse(final String kind, final String etag, final String summary, - final String description, final String timeZone, final String accessRole, - final String nextPageToken, final String nextSyncToken, - final List items) { - this.kind = kind; - this.etag = etag; - this.summary = summary; - this.description = description; - this.timeZone = timeZone; - this.accessRole = accessRole; - this.nextPageToken = nextPageToken; - this.nextSyncToken = nextSyncToken; + public GoogleCalendarEventsResponse(final List items) { this.items = items; } - public String getKind() { - return kind; - } - - public String getEtag() { - return etag; - } - - public String getSummary() { - return summary; - } - - public String getDescription() { - return description; - } - - public String getTimeZone() { - return timeZone; - } - - public String getAccessRole() { - return accessRole; - } - - public String getNextPageToken() { - return nextPageToken; - } - - public String getNextSyncToken() { - return nextSyncToken; - } - public List getItems() { return items; } diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java index 61d92a56..ee89e787 100644 --- a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java @@ -4,40 +4,15 @@ public class GoogleCalendarListResponse { - private String kind; - private String etag; - private String nextPageToken; - private String nextSyncToken; private List items; private GoogleCalendarListResponse() { } - public GoogleCalendarListResponse(final String kind, final String etag, final String nextPageToken, - final String nextSyncToken, final List items) { - this.kind = kind; - this.etag = etag; - this.nextPageToken = nextPageToken; - this.nextSyncToken = nextSyncToken; + public GoogleCalendarListResponse(final List items) { this.items = items; } - public String getKind() { - return kind; - } - - public String getEtag() { - return etag; - } - - public String getNextPageToken() { - return nextPageToken; - } - - public String getNextSyncToken() { - return nextSyncToken; - } - public List getItems() { return items; } diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java index d43cf782..17dd6305 100644 --- a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java @@ -2,55 +2,17 @@ public class GoogleCalendarResponse { - private String kind; - private String etag; private String id; private String summary; private String description; - private String location; - private String timeZone; - private String summaryOverride; - private String colorId; - private String backgroundColor; - private String foregroundColor; - private boolean hidden; - private boolean selected; - private String accessRole; - private boolean primary; - private boolean deleted; private GoogleCalendarResponse() { } - public GoogleCalendarResponse(final String kind, final String etag, final String id, final String summary, - final String description, final String location, final String timeZone, - final String summaryOverride, final String colorId, final String backgroundColor, - final String foregroundColor, final boolean hidden, final boolean selected, - final String accessRole, final boolean primary, final boolean deleted) { - this.kind = kind; - this.etag = etag; + public GoogleCalendarResponse(final String id, final String summary, final String description) { this.id = id; this.summary = summary; this.description = description; - this.location = location; - this.timeZone = timeZone; - this.summaryOverride = summaryOverride; - this.colorId = colorId; - this.backgroundColor = backgroundColor; - this.foregroundColor = foregroundColor; - this.hidden = hidden; - this.selected = selected; - this.accessRole = accessRole; - this.primary = primary; - this.deleted = deleted; - } - - public String getKind() { - return kind; - } - - public String getEtag() { - return etag; } public String getId() { @@ -64,48 +26,4 @@ public String getSummary() { public String getDescription() { return description; } - - public String getLocation() { - return location; - } - - public String getTimeZone() { - return timeZone; - } - - public String getSummaryOverride() { - return summaryOverride; - } - - public String getColorId() { - return colorId; - } - - public String getBackgroundColor() { - return backgroundColor; - } - - public String getForegroundColor() { - return foregroundColor; - } - - public boolean isHidden() { - return hidden; - } - - public boolean isSelected() { - return selected; - } - - public String getAccessRole() { - return accessRole; - } - - public boolean isPrimary() { - return primary; - } - - public boolean isDeleted() { - return deleted; - } } diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java index cd30178d..6683f3b6 100644 --- a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java @@ -4,15 +4,13 @@ public class GoogleDateFormat { private String date; private String dateTime; - private String timeZone; private GoogleDateFormat() { } - public GoogleDateFormat(final String date, final String dateTime, final String timeZone) { + public GoogleDateFormat(final String date, final String dateTime) { this.date = date; this.dateTime = dateTime; - this.timeZone = timeZone; } public String getDate() { @@ -22,8 +20,4 @@ public String getDate() { public String getDateTime() { return dateTime; } - - public String getTimeZone() { - return timeZone; - } } diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java index ec979965..cbae35f1 100644 --- a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java @@ -6,28 +6,15 @@ @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class GoogleTokenResponse { - private String accessToken; private String refreshToken; private String idToken; - private String expiresIn; - private String tokenType; - private String scope; private GoogleTokenResponse() { } - public GoogleTokenResponse(final String accessToken, final String refreshToken, final String idToken, - final String expiresIn, final String scope, final String tokenType) { - this.accessToken = accessToken; + public GoogleTokenResponse(final String refreshToken, final String idToken) { this.refreshToken = refreshToken; this.idToken = idToken; - this.expiresIn = expiresIn; - this.scope = scope; - this.tokenType = tokenType; - } - - public String getAccessToken() { - return accessToken; } public String getRefreshToken() { @@ -37,16 +24,4 @@ public String getRefreshToken() { public String getIdToken() { return idToken; } - - public String getExpiresIn() { - return expiresIn; - } - - public String getScope() { - return scope; - } - - public String getTokenType() { - return tokenType; - } } diff --git a/backend/src/main/java/com/allog/dallog/presentation/CategoryController.java b/backend/src/main/java/com/allog/dallog/presentation/CategoryController.java index 7d18729f..192685b1 100644 --- a/backend/src/main/java/com/allog/dallog/presentation/CategoryController.java +++ b/backend/src/main/java/com/allog/dallog/presentation/CategoryController.java @@ -6,7 +6,6 @@ import com.allog.dallog.domain.category.dto.request.CategoryUpdateRequest; import com.allog.dallog.domain.category.dto.response.CategoriesResponse; import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.composition.application.CategorySubscriptionService; import com.allog.dallog.presentation.auth.AuthenticationPrincipal; import java.net.URI; import javax.validation.Valid; @@ -27,39 +26,36 @@ public class CategoryController { private final CategoryService categoryService; - private final CategorySubscriptionService categorySubscriptionService; - public CategoryController(final CategoryService categoryService, - final CategorySubscriptionService categorySubscriptionService) { + public CategoryController(final CategoryService categoryService) { this.categoryService = categoryService; - this.categorySubscriptionService = categorySubscriptionService; } @PostMapping public ResponseEntity save(@AuthenticationPrincipal final LoginMember loginMember, @Valid @RequestBody final CategoryCreateRequest request) { - CategoryResponse categoryResponse = categorySubscriptionService.save(loginMember.getId(), request); + CategoryResponse categoryResponse = categoryService.save(loginMember.getId(), request); return ResponseEntity.created(URI.create("/api/categories/" + categoryResponse.getId())).body(categoryResponse); } @GetMapping - public ResponseEntity findAllByName(@RequestParam(defaultValue = "") final String name, - final Pageable pageable) { + public ResponseEntity findNormalByName(@RequestParam(defaultValue = "") final String name, + final Pageable pageable) { return ResponseEntity.ok(categoryService.findNormalByName(name, pageable)); } - @GetMapping("/me") - public ResponseEntity findMineByName(@AuthenticationPrincipal final LoginMember loginMember, - @RequestParam(defaultValue = "") final String name, - final Pageable pageable) { - return ResponseEntity.ok(categoryService.findMineByName(loginMember.getId(), name, pageable)); - } - @GetMapping("/{categoryId}") public ResponseEntity findById(@PathVariable final Long categoryId) { return ResponseEntity.ok().body(categoryService.findById(categoryId)); } + @GetMapping("/me") + public ResponseEntity findMyCategories(@AuthenticationPrincipal final LoginMember loginMember, + @RequestParam(defaultValue = "") final String name, + final Pageable pageable) { + return ResponseEntity.ok(categoryService.findMyCategories(loginMember.getId(), name, pageable)); + } + @PatchMapping("/{categoryId}") public ResponseEntity update(@AuthenticationPrincipal final LoginMember loginMember, @PathVariable final Long categoryId, @@ -71,7 +67,7 @@ public ResponseEntity update(@AuthenticationPrincipal final LoginMember lo @DeleteMapping("/{categoryId}") public ResponseEntity delete(@AuthenticationPrincipal final LoginMember loginMember, @PathVariable final Long categoryId) { - categoryService.deleteById(loginMember.getId(), categoryId); + categoryService.delete(loginMember.getId(), categoryId); return ResponseEntity.noContent().build(); } } diff --git a/backend/src/main/java/com/allog/dallog/presentation/ExternalCalendarController.java b/backend/src/main/java/com/allog/dallog/presentation/ExternalCalendarController.java index 239b0d94..f43c8188 100644 --- a/backend/src/main/java/com/allog/dallog/presentation/ExternalCalendarController.java +++ b/backend/src/main/java/com/allog/dallog/presentation/ExternalCalendarController.java @@ -1,9 +1,9 @@ package com.allog.dallog.presentation; import com.allog.dallog.domain.auth.dto.LoginMember; +import com.allog.dallog.domain.category.application.CategoryService; import com.allog.dallog.domain.category.dto.request.ExternalCategoryCreateRequest; import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.composition.application.CategorySubscriptionService; import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarService; import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendarsResponse; import com.allog.dallog.presentation.auth.AuthenticationPrincipal; @@ -20,25 +20,24 @@ public class ExternalCalendarController { private final ExternalCalendarService externalCalendarService; - private final CategorySubscriptionService categorySubscriptionService; + private final CategoryService categoryService; public ExternalCalendarController(final ExternalCalendarService externalCalendarService, - final CategorySubscriptionService categorySubscriptionService) { + final CategoryService categoryService) { this.externalCalendarService = externalCalendarService; - this.categorySubscriptionService = categorySubscriptionService; + this.categoryService = categoryService; } @GetMapping public ResponseEntity getExternalCalendar( @AuthenticationPrincipal final LoginMember loginMember) { - return ResponseEntity.ok(externalCalendarService.findByMemberId(loginMember.getId())); } @PostMapping public ResponseEntity save(@AuthenticationPrincipal final LoginMember loginMember, @RequestBody final ExternalCategoryCreateRequest request) { - CategoryResponse response = categorySubscriptionService.save(loginMember.getId(), request); + CategoryResponse response = categoryService.save(loginMember.getId(), request); return ResponseEntity.created(URI.create("/api/categories/" + response.getId())).body(response); } } diff --git a/backend/src/main/java/com/allog/dallog/presentation/MemberController.java b/backend/src/main/java/com/allog/dallog/presentation/MemberController.java index d07b7ba9..009d3597 100644 --- a/backend/src/main/java/com/allog/dallog/presentation/MemberController.java +++ b/backend/src/main/java/com/allog/dallog/presentation/MemberController.java @@ -1,7 +1,6 @@ package com.allog.dallog.presentation; import com.allog.dallog.domain.auth.dto.LoginMember; -import com.allog.dallog.domain.composition.application.RegisterService; import com.allog.dallog.domain.member.application.MemberService; import com.allog.dallog.domain.member.dto.MemberResponse; import com.allog.dallog.domain.member.dto.MemberUpdateRequest; @@ -19,11 +18,9 @@ public class MemberController { private final MemberService memberService; - private final RegisterService registerService; - public MemberController(final MemberService memberService, final RegisterService registerService) { + public MemberController(final MemberService memberService) { this.memberService = memberService; - this.registerService = registerService; } @GetMapping("/me") @@ -41,7 +38,7 @@ public ResponseEntity update(@AuthenticationPrincipal LoginMember loginMem @DeleteMapping("/me") public ResponseEntity delete(@AuthenticationPrincipal final LoginMember loginMember) { - registerService.deleteByMemberId(loginMember.getId()); + memberService.deleteById(loginMember.getId()); return ResponseEntity.noContent().build(); } } diff --git a/backend/src/main/java/com/allog/dallog/presentation/ScheduleController.java b/backend/src/main/java/com/allog/dallog/presentation/ScheduleController.java index 4d95e609..db8cf495 100644 --- a/backend/src/main/java/com/allog/dallog/presentation/ScheduleController.java +++ b/backend/src/main/java/com/allog/dallog/presentation/ScheduleController.java @@ -1,8 +1,8 @@ package com.allog.dallog.presentation; import com.allog.dallog.domain.auth.dto.LoginMember; -import com.allog.dallog.domain.composition.application.CalendarService; import com.allog.dallog.domain.schedule.application.ScheduleService; +import com.allog.dallog.domain.schedule.application.SubscribingSchedulesFinder; import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; @@ -27,11 +27,12 @@ public class ScheduleController { private final ScheduleService scheduleService; - private final CalendarService calendarService; + private final SubscribingSchedulesFinder subscribingSchedulesFinder; - public ScheduleController(final ScheduleService scheduleService, final CalendarService calendarService) { + public ScheduleController(final ScheduleService scheduleService, + final SubscribingSchedulesFinder subscribingSchedulesFinder) { this.scheduleService = scheduleService; - this.calendarService = calendarService; + this.subscribingSchedulesFinder = subscribingSchedulesFinder; } @PostMapping("/categories/{categoryId}/schedules") @@ -43,9 +44,10 @@ public ResponseEntity save(@AuthenticationPrincipal final Logi } @GetMapping("/members/me/schedules") - public ResponseEntity findSchedulesByMemberId( + public ResponseEntity findMySubscribingSchedules( @AuthenticationPrincipal final LoginMember loginMember, @ModelAttribute DateRangeRequest request) { - MemberScheduleResponses response = calendarService.findSchedulesByMemberId(loginMember.getId(), request); + MemberScheduleResponses response = subscribingSchedulesFinder + .findMySubscribingSchedules(loginMember.getId(), request); return ResponseEntity.ok(response); } @@ -66,7 +68,7 @@ public ResponseEntity update(@AuthenticationPrincipal final LoginMember lo @DeleteMapping("/schedules/{scheduleId}") public ResponseEntity delete(@AuthenticationPrincipal final LoginMember loginMember, @PathVariable final Long scheduleId) { - scheduleService.deleteById(scheduleId, loginMember.getId()); + scheduleService.delete(scheduleId, loginMember.getId()); return ResponseEntity.noContent().build(); } } diff --git a/backend/src/main/java/com/allog/dallog/presentation/SchedulerController.java b/backend/src/main/java/com/allog/dallog/presentation/SchedulerController.java deleted file mode 100644 index f82c4f73..00000000 --- a/backend/src/main/java/com/allog/dallog/presentation/SchedulerController.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.allog.dallog.presentation; - -import com.allog.dallog.domain.composition.application.SchedulerService; -import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; -import com.allog.dallog.domain.schedule.dto.response.PeriodResponse; -import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RequestMapping("/api/scheduler") -@RestController -public class SchedulerController { - - private final SchedulerService schedulerService; - - public SchedulerController(final SchedulerService schedulerService) { - this.schedulerService = schedulerService; - } - - @GetMapping("/categories/{categoryId}/available-periods") - public ResponseEntity> scheduleByCategory(@PathVariable final Long categoryId, - @ModelAttribute DateRangeRequest dateRange) { - List periods = schedulerService.getAvailablePeriods(categoryId, dateRange); - return ResponseEntity.ok(periods); - } -} diff --git a/backend/src/main/java/com/allog/dallog/presentation/SubscriptionController.java b/backend/src/main/java/com/allog/dallog/presentation/SubscriptionController.java index 2cdc5cc2..dd0e9506 100644 --- a/backend/src/main/java/com/allog/dallog/presentation/SubscriptionController.java +++ b/backend/src/main/java/com/allog/dallog/presentation/SubscriptionController.java @@ -37,7 +37,7 @@ public ResponseEntity save(@AuthenticationPrincipal final } @GetMapping("/subscriptions") - public ResponseEntity findMine(@AuthenticationPrincipal final LoginMember loginMember) { + public ResponseEntity findByMemberId(@AuthenticationPrincipal final LoginMember loginMember) { SubscriptionsResponse response = subscriptionService.findByMemberId(loginMember.getId()); return ResponseEntity.ok(response); } @@ -51,9 +51,9 @@ public ResponseEntity update(@AuthenticationPrincipal final LoginMember lo } @DeleteMapping("/subscriptions/{subscriptionId}") - public ResponseEntity deleteById(@AuthenticationPrincipal final LoginMember loginMember, - @PathVariable final Long subscriptionId) { - subscriptionService.deleteById(subscriptionId, loginMember.getId()); + public ResponseEntity delete(@AuthenticationPrincipal final LoginMember loginMember, + @PathVariable final Long subscriptionId) { + subscriptionService.delete(subscriptionId, loginMember.getId()); return ResponseEntity.noContent().build(); } } diff --git a/backend/src/main/resources/application-test.yml b/backend/src/main/resources/application-test.yml new file mode 100644 index 00000000..cadd0137 --- /dev/null +++ b/backend/src/main/resources/application-test.yml @@ -0,0 +1,43 @@ +spring: + data: + web: + pageable: + max-page-size: 100 + + main: + allow-bean-definition-overriding: true + + datasource: + url: jdbc:h2:~/dallog;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + username: sa + + jpa: + properties: + hibernate: + format_sql: true + show-sql: true + + hibernate: + ddl-auto: create + +cors: + allow-origin: + urls: http://localhost:3000 + +security: + jwt: + token: + secret-key: fsmjgbdafmjgbasmfgadbsgmadfhgbfamjghbvmssdgsdfgdf + expire-length: 3600000 + +oauth: + google: + client-id: hyeonic + client-secret: 123 + oauth-end-point: https://accounts.google.com/o/oauth2/v2/auth + response-type: code + scopes: + - https://www.googleapis.com/auth/userinfo.profile + - https://www.googleapis.com/auth/userinfo.email + token-uri: https://oauth2.googleapis.com/token + access-type: offline diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config index 553c1765..cf508249 160000 --- a/backend/src/main/resources/config +++ b/backend/src/main/resources/config @@ -1 +1 @@ -Subproject commit 553c17653bab46e2ebd0ff2afd45e05518ba337d +Subproject commit cf5082492d2b7850ed1d9da54d6933dd96f29806 diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index cfcc2772..07c7a542 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -36,6 +36,18 @@ + + + + + + + + + + + + @@ -43,18 +55,26 @@ - + + - + + + + + - + - + + + + diff --git a/backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java index 77baa342..e37c3138 100644 --- a/backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java +++ b/backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java @@ -7,8 +7,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.ActiveProfiles; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = ExternalApiConfig.class) +@ActiveProfiles("test") abstract class AcceptanceTest { @LocalServerPort diff --git a/backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java index 3cefdf3c..4c9aa314 100644 --- a/backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java +++ b/backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java @@ -65,7 +65,8 @@ class ScheduleAcceptanceTest extends AcceptanceTest { CategoryResponse 공통_일정_응답 = 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청).as(CategoryResponse.class); ScheduleResponse 알록달록_회의 = 새로운_일정을_등록한다(accessToken, 공통_일정_응답.getId()).as(ScheduleResponse.class); - ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(공통_일정_응답.getId(), 레벨_인터뷰_제목, 레벨_인터뷰_시작일시, + 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); // when ExtractableResponse response = 일정을_수정한다(accessToken, 알록달록_회의.getId(), 일정_수정_요청); diff --git a/backend/src/test/java/com/allog/dallog/acceptance/SchedulerAcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/SchedulerAcceptanceTest.java deleted file mode 100644 index d64e17f7..00000000 --- a/backend/src/test/java/com/allog/dallog/acceptance/SchedulerAcceptanceTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.allog.dallog.acceptance; - -import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생성하고_토큰을_반환한다; -import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.새로운_카테고리를_등록한다; -import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_200이_반환된다; -import static com.allog.dallog.acceptance.fixtures.ScheduleAcceptanceFixtures.새로운_일정을_등록한다; -import static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER; -import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; -import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; - -import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; -import io.restassured.RestAssured; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.http.MediaType; - -@DisplayName("일정 관련 기능") -public class SchedulerAcceptanceTest extends AcceptanceTest { - - @DisplayName("일정 조율을 받아오면 상태코드 200을 반환한다.") - @Test - void 일정_조율을_받아오면_상태코드_200을_반환한다() { - // given - String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); - - CategoryResponse 공통_일정_응답 = 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청).as(CategoryResponse.class); - - 새로운_일정을_등록한다(accessToken, 공통_일정_응답.getId()).as(ScheduleResponse.class); - - // when - ExtractableResponse response = RestAssured.given().log().all() - .contentType(MediaType.APPLICATION_JSON_VALUE) - .queryParam("startDateTime", "2022-07-01T00:00") - .queryParam("endDateTime", "2022-07-31T00:00") - .auth().oauth2(accessToken) - .when().get("/api/scheduler/categories/{categoryId}/available-periods", 공통_일정_응답.getId()) - .then().log().all() - .extract(); - - // then - 상태코드_200이_반환된다(response); - } -} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java index c2b49763..4990b587 100644 --- a/backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java +++ b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java @@ -15,7 +15,7 @@ public class ScheduleAcceptanceFixtures { public static ExtractableResponse 새로운_일정을_등록한다(final String accessToken, final Long categoryId) { - Map params = new HashMap(); + Map params = new HashMap<>(); params.put("title", 알록달록_회의_제목); params.put("startDateTime", "2022-07-04T13:00"); params.put("endDateTime", "2022-07-05T16:00"); diff --git a/backend/src/test/java/com/allog/dallog/common/annotation/RepositoryTest.java b/backend/src/test/java/com/allog/dallog/common/annotation/RepositoryTest.java index d2370dcd..0ba9fef3 100644 --- a/backend/src/test/java/com/allog/dallog/common/annotation/RepositoryTest.java +++ b/backend/src/test/java/com/allog/dallog/common/annotation/RepositoryTest.java @@ -3,8 +3,10 @@ import com.allog.dallog.global.config.JpaConfig; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; @DataJpaTest @Import(JpaConfig.class) +@ActiveProfiles("test") public class RepositoryTest { } diff --git a/backend/src/test/java/com/allog/dallog/common/annotation/ServiceTest.java b/backend/src/test/java/com/allog/dallog/common/annotation/ServiceTest.java index 5d24814d..53be6d1f 100644 --- a/backend/src/test/java/com/allog/dallog/common/annotation/ServiceTest.java +++ b/backend/src/test/java/com/allog/dallog/common/annotation/ServiceTest.java @@ -2,13 +2,21 @@ import com.allog.dallog.common.DatabaseCleaner; import com.allog.dallog.common.config.ExternalApiConfig; +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.auth.dto.request.TokenRequest; +import com.allog.dallog.domain.auth.dto.response.TokenResponse; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; @SpringBootTest(classes = ExternalApiConfig.class) +@ActiveProfiles("test") public class ServiceTest { + @Autowired + private AuthService authService; + @Autowired private DatabaseCleaner databaseCleaner; @@ -16,4 +24,9 @@ public class ServiceTest { void setUp() { databaseCleaner.execute(); } + + protected Long parseMemberId(final TokenRequest tokenRequest) { + TokenResponse tokenResponse = authService.generateToken(tokenRequest); + return authService.extractMemberId(tokenResponse.getAccessToken()); + } } diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/AuthFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/AuthFixtures.java index 3b166c3f..0f96cc85 100644 --- a/backend/src/test/java/com/allog/dallog/common/fixtures/AuthFixtures.java +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/AuthFixtures.java @@ -1,6 +1,11 @@ package com.allog.dallog.common.fixtures; -import com.allog.dallog.domain.auth.dto.OAuthMember; +import static com.allog.dallog.common.fixtures.OAuthFixtures.관리자; +import static com.allog.dallog.common.fixtures.OAuthFixtures.리버; +import static com.allog.dallog.common.fixtures.OAuthFixtures.매트; +import static com.allog.dallog.common.fixtures.OAuthFixtures.파랑; +import static com.allog.dallog.common.fixtures.OAuthFixtures.후디; + import com.allog.dallog.domain.auth.dto.request.TokenRequest; import com.allog.dallog.domain.auth.dto.response.TokenResponse; @@ -29,9 +34,26 @@ public class AuthFixtures { public static final String 더미_시크릿_키 = "asdfasarspofjkosdfasdjkflikasndflkasndsdfjkadsnfkjasdn"; public static final String STUB_OAUTH_ACCESS_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.cccccccccc"; - public static final String STUB_OAUTH_EXPIRES_IN = "3599"; - public static final String STUB_OAUTH_SCOPE = "openid"; - public static final String STUB_OAUTH_TOKEN_TYPE = "Bearer"; + + public static TokenRequest 관리자_인증_코드_토큰_요청() { + return new TokenRequest(관리자.getCode(), "https://dallog.me/oauth"); + } + + public static TokenRequest 파랑_인증_코드_토큰_요청() { + return new TokenRequest(파랑.getCode(), "https://dallog.me/oauth"); + } + + public static TokenRequest 리버_인증_코드_토큰_요청() { + return new TokenRequest(리버.getCode(), "https://dallog.me/oauth"); + } + + public static TokenRequest 후디_인증_코드_토큰_요청() { + return new TokenRequest(후디.getCode(), "https://dallog.me/oauth"); + } + + public static TokenRequest 매트_인증_코드_토큰_요청() { + return new TokenRequest(매트.getCode(), "https://dallog.me/oauth"); + } public static TokenRequest MEMBER_인증_코드_토큰_요청() { return new TokenRequest(STUB_MEMBER_인증_코드, "https://dallog.me/oauth"); @@ -41,11 +63,5 @@ public class AuthFixtures { return new TokenResponse(STUB_MEMBER_인증_코드); } - public static OAuthMember STUB_OAUTH_MEMBER() { - return new OAuthMember(MEMBER_이메일, MEMBER_이름, MEMBER_프로필, MEMBER_REFRESH_TOKEN); - } - public static OAuthMember STUB_OAUTH_CREATOR() { - return new OAuthMember(CREATOR_이메일, CREATOR_이름, CREATOR_프로필, CREATOR_REFRESH_TOKEN); - } } diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/CategoryFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/CategoryFixtures.java index 24e71dc3..3e2b8496 100644 --- a/backend/src/test/java/com/allog/dallog/common/fixtures/CategoryFixtures.java +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/CategoryFixtures.java @@ -9,6 +9,7 @@ import com.allog.dallog.domain.category.dto.response.CategoryResponse; import com.allog.dallog.domain.member.domain.Member; import com.allog.dallog.domain.member.dto.MemberResponse; +import java.lang.reflect.Field; import java.time.LocalDateTime; public class CategoryFixtures { @@ -20,10 +21,12 @@ public class CategoryFixtures { /* BE 일정 카테고리 */ public static final String BE_일정_이름 = "BE 일정"; public static final CategoryCreateRequest BE_일정_생성_요청 = new CategoryCreateRequest(BE_일정_이름, NORMAL); + public static final CategoryCreateRequest 외부_BE_일정_생성_요청 = new CategoryCreateRequest(BE_일정_이름, GOOGLE); /* FE 일정 카테고리 */ public static final String FE_일정_이름 = "FE 일정"; public static final CategoryCreateRequest FE_일정_생성_요청 = new CategoryCreateRequest(FE_일정_이름, NORMAL); + public static final CategoryCreateRequest 외부_FE_일정_생성_요청 = new CategoryCreateRequest(FE_일정_이름, GOOGLE); /* 매트 아고라 카테고리 */ public static final String 매트_아고라_이름 = "매트 아고라"; @@ -88,4 +91,15 @@ public class CategoryFixtures { public static CategoryResponse 후디_JPA_스터디_응답(final MemberResponse creatorResponse) { return new CategoryResponse(5L, 후디_JPA_스터디_이름, NORMAL.name(), creatorResponse, LocalDateTime.now()); } + + public static Category setId(final Category category, final Long id) { + try { + Field idField = Category.class.getDeclaredField("id"); + idField.setAccessible(true); + idField.set(category, id); + return category; + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new IllegalArgumentException(e.getMessage()); + } + } } diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/IntegrationScheduleFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/IntegrationScheduleFixtures.java index c6d7005f..28704445 100644 --- a/backend/src/test/java/com/allog/dallog/common/fixtures/IntegrationScheduleFixtures.java +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/IntegrationScheduleFixtures.java @@ -1,25 +1,25 @@ package com.allog.dallog.common.fixtures; -import com.allog.dallog.domain.category.domain.CategoryType; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; -import com.allog.dallog.domain.integrationschedule.domain.Period; +import static com.allog.dallog.domain.category.domain.CategoryType.GOOGLE; +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; + +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.schedule.domain.Period; import java.time.LocalDateTime; public class IntegrationScheduleFixtures { public static final IntegrationSchedule 점심_식사 = new IntegrationSchedule("1", 2L, "점심 식사", - new Period(LocalDateTime.of(2022, 8, 16, 11, 00), LocalDateTime.of(2022, 8, 16, 13, 00)), "", - CategoryType.NORMAL.name()); + new Period(LocalDateTime.of(2022, 8, 16, 11, 00), LocalDateTime.of(2022, 8, 16, 13, 00)), "", NORMAL); public static final IntegrationSchedule 달록_여행 = new IntegrationSchedule("2", 2L, "달록 여행", - new Period(LocalDateTime.of(2022, 8, 24, 00, 00), LocalDateTime.of(2022, 8, 25, 23, 59)), "", - CategoryType.NORMAL.name()); + new Period(LocalDateTime.of(2022, 8, 24, 00, 00), LocalDateTime.of(2022, 8, 25, 23, 59)), "", NORMAL); public static final IntegrationSchedule 레벨3_방학 = new IntegrationSchedule("gsgadfgqwrtqwerfgasdasdasd", 1L, "레벨3 방학", new Period(LocalDateTime.of(2022, 8, 20, 00, 00), LocalDateTime.of(2022, 8, 20, 00, 00)), "", - CategoryType.GOOGLE.name()); + GOOGLE); public static final IntegrationSchedule 포수타 = new IntegrationSchedule("asgasgasfgadfgdf", 1L, "포수타", new Period(LocalDateTime.of(2022, 8, 12, 14, 00), LocalDateTime.of(2022, 8, 12, 14, 30)), "", - CategoryType.GOOGLE.name()); + GOOGLE); } diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/OAuthFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/OAuthFixtures.java new file mode 100644 index 00000000..ea0907fa --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/OAuthFixtures.java @@ -0,0 +1,96 @@ +package com.allog.dallog.common.fixtures; + +import com.allog.dallog.domain.auth.dto.OAuthMember; +import java.util.Arrays; +import java.util.NoSuchElementException; + +public enum OAuthFixtures { + + 관리자("관리자", 관리자()), + 파랑("파랑", 파랑()), + 리버("리버", 리버()), + 후디("후디", 후디()), + 매트("매트", 매트()), + MEMBER("member authorization code", MEMBER()), + CREATOR("creator authorization code", CREATOR()); + + private String code; + private OAuthMember oAuthMember; + + OAuthFixtures(final String code, final OAuthMember oAuthMember) { + this.code = code; + this.oAuthMember = oAuthMember; + } + + public static OAuthMember parseOAuthMember(final String code) { + OAuthFixtures oAuthFixtures = Arrays.stream(values()) + .filter(value -> value.code.equals(code)) + .findFirst() + .orElseThrow(NoSuchElementException::new); + return oAuthFixtures.oAuthMember; + } + + private static OAuthMember 관리자() { + String 관리자_이메일 = "dallog.admin@gmail.com"; + String 관리자_이름 = "관리자"; + String 관리자_프로필 = "/admin.png"; + String 관리자_REFRESH_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.cccccccccc"; + return new OAuthMember(관리자_이메일, 관리자_이름, 관리자_프로필, 관리자_REFRESH_TOKEN); + } + + private static OAuthMember 파랑() { + String 파랑_이메일 = "parang@email.com"; + String 파랑_이름 = "파랑"; + String 파랑_프로필 = "/parang.png"; + String 파랑_REFRESH_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.cccccccccc"; + return new OAuthMember(파랑_이메일, 파랑_이름, 파랑_프로필, 파랑_REFRESH_TOKEN); + } + + private static OAuthMember 리버() { + String 리버_이메일 = "leaver@email.com"; + String 리버_이름 = "리버"; + String 리버_프로필 = "/leaver.png"; + String 리버_REFRESH_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.cccccccccc"; + return new OAuthMember(리버_이메일, 리버_이름, 리버_프로필, 리버_REFRESH_TOKEN); + } + + private static OAuthMember 후디() { + String 후디_이메일 = "devhudi@gmail.com"; + String 후디_이름 = "후디"; + String 후디_프로필 = "/hudi.png"; + String 후디_REFRESH_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.cccccccccc"; + return new OAuthMember(후디_이메일, 후디_이름, 후디_프로필, 후디_REFRESH_TOKEN); + } + + private static OAuthMember 매트() { + String 매트_이메일 = "dev.hyeonic@gmail.com"; + String 매트_이름 = "매트"; + String 매트_프로필 = "/mat.png"; + String 매트_REFRESH_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.cccccccccc"; + return new OAuthMember(매트_이메일, 매트_이름, 매트_프로필, 매트_REFRESH_TOKEN); + } + + private static OAuthMember MEMBER() { + String MEMBER_이메일 = "member@email.com"; + String MEMBER_이름 = "member"; + String MEMBER_프로필 = "/member.png"; + String MEMBER_REFRESH_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.ccccccccc"; + return new OAuthMember(MEMBER_이메일, MEMBER_이름, MEMBER_프로필, MEMBER_REFRESH_TOKEN); + } + + private static OAuthMember CREATOR() { + String CREATOR_이메일 = "creator@email.com"; + String CREATOR_이름 = "creator"; + String CREATOR_프로필 = "/creator.png"; + String CREATOR_REFRESH_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.ccccccccc"; + return new OAuthMember(CREATOR_이메일, CREATOR_이름, CREATOR_프로필, CREATOR_REFRESH_TOKEN); + } + + public String getCode() { + return code; + } + + public OAuthMember getOAuthMember() { + return oAuthMember; + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/ScheduleFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/ScheduleFixtures.java index 4f478972..5a83bf63 100644 --- a/backend/src/test/java/com/allog/dallog/common/fixtures/ScheduleFixtures.java +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/ScheduleFixtures.java @@ -13,6 +13,7 @@ public class ScheduleFixtures { public static final LocalDateTime 날짜_2022년_7월_7일_16시_0분 = LocalDateTime.of(2022, 7, 7, 16, 0); public static final LocalDateTime 날짜_2022년_7월_10일_0시_0분 = LocalDateTime.of(2022, 7, 10, 0, 0); public static final LocalDateTime 날짜_2022년_7월_10일_11시_59분 = LocalDateTime.of(2022, 7, 10, 23, 59); + public static final LocalDateTime 날짜_2022년_7월_11일_0시_0분 = LocalDateTime.of(2022, 7, 11, 0, 0); public static final LocalDateTime 날짜_2022년_7월_15일_16시_0분 = LocalDateTime.of(2022, 7, 15, 16, 0); public static final LocalDateTime 날짜_2022년_7월_16일_16시_0분 = LocalDateTime.of(2022, 7, 16, 16, 0); public static final LocalDateTime 날짜_2022년_7월_16일_16시_1분 = LocalDateTime.of(2022, 7, 16, 16, 1); @@ -21,8 +22,10 @@ public class ScheduleFixtures { public static final LocalDateTime 날짜_2022년_7월_17일_23시_59분 = LocalDateTime.of(2022, 7, 17, 23, 59); public static final LocalDateTime 날짜_2022년_7월_20일_0시_0분 = LocalDateTime.of(2022, 7, 20, 0, 0); public static final LocalDateTime 날짜_2022년_7월_20일_11시_59분 = LocalDateTime.of(2022, 7, 20, 23, 59); + public static final LocalDateTime 날짜_2022년_7월_21일_0시_0분 = LocalDateTime.of(2022, 7, 21, 0, 0); public static final LocalDateTime 날짜_2022년_7월_27일_0시_0분 = LocalDateTime.of(2022, 7, 27, 0, 0); public static final LocalDateTime 날짜_2022년_7월_27일_11시_59분 = LocalDateTime.of(2022, 7, 27, 23, 59); + public static final LocalDateTime 날짜_2022년_7월_28일_0시_0분 = LocalDateTime.of(2022, 7, 28, 0, 0); public static final LocalDateTime 날짜_2022년_7월_31일_0시_0분 = LocalDateTime.of(2022, 7, 31, 0, 0); public static final LocalDateTime 날짜_2022년_8월_15일_14시_0분 = LocalDateTime.of(2022, 8, 15, 14, 0); public static final LocalDateTime 날짜_2022년_8월_15일_17시_0분 = LocalDateTime.of(2022, 8, 15, 17, 0); diff --git a/backend/src/test/java/com/allog/dallog/domain/category/application/CategoryServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/category/application/CategoryServiceTest.java index 36e4380a..98996842 100644 --- a/backend/src/test/java/com/allog/dallog/domain/category/application/CategoryServiceTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/category/application/CategoryServiceTest.java @@ -1,5 +1,7 @@ package com.allog.dallog.domain.category.application; +import static com.allog.dallog.common.fixtures.AuthFixtures.리버_인증_코드_토큰_요청; +import static com.allog.dallog.common.fixtures.AuthFixtures.후디_인증_코드_토큰_요청; import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_이름; import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; @@ -14,7 +16,6 @@ import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.대한민국_공휴일_생성_요청; import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.대한민국_공휴일_이름; import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; -import static com.allog.dallog.common.fixtures.MemberFixtures.리버; import static com.allog.dallog.common.fixtures.MemberFixtures.매트; import static com.allog.dallog.common.fixtures.MemberFixtures.후디; import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_생성_요청; @@ -26,7 +27,9 @@ import static org.junit.jupiter.api.Assertions.assertAll; import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.common.fixtures.AuthFixtures; import com.allog.dallog.common.fixtures.CategoryFixtures; +import com.allog.dallog.domain.auth.application.AuthService; import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.category.domain.Category; import com.allog.dallog.domain.category.domain.CategoryRepository; @@ -34,19 +37,21 @@ import com.allog.dallog.domain.category.dto.request.CategoryUpdateRequest; import com.allog.dallog.domain.category.dto.response.CategoriesResponse; import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.category.exception.DuplicatedExternalCategoryException; +import com.allog.dallog.domain.category.exception.ExistExternalCategoryException; import com.allog.dallog.domain.category.exception.InvalidCategoryException; import com.allog.dallog.domain.category.exception.NoSuchCategoryException; import com.allog.dallog.domain.member.application.MemberService; import com.allog.dallog.domain.member.domain.Member; import com.allog.dallog.domain.member.domain.MemberRepository; -import com.allog.dallog.domain.member.dto.MemberResponse; import com.allog.dallog.domain.schedule.application.ScheduleService; import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; import com.allog.dallog.domain.schedule.exception.NoSuchScheduleException; import com.allog.dallog.domain.subscription.application.SubscriptionService; +import com.allog.dallog.domain.subscription.domain.Subscription; import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionsResponse; import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -59,9 +64,6 @@ class CategoryServiceTest extends ServiceTest { @Autowired private CategoryService categoryService; - @Autowired - private CategoryRepository categoryRepository; - @Autowired private SubscriptionService subscriptionService; @@ -71,6 +73,12 @@ class CategoryServiceTest extends ServiceTest { @Autowired private MemberService memberService; + @Autowired + private AuthService authService; + + @Autowired + private CategoryRepository categoryRepository; + @Autowired private MemberRepository memberRepository; @@ -104,6 +112,21 @@ class CategoryServiceTest extends ServiceTest { }); } + @DisplayName("카테고리 생성 시 자동으로 구독한다.") + @Test + void 카테고리_생성_시_자동으로_구독한다() { + // given + Long 파랑_id = parseMemberId(AuthFixtures.파랑_인증_코드_토큰_요청()); + + // when + categoryService.save(파랑_id, 공통_일정_생성_요청); + + SubscriptionsResponse subscriptions = subscriptionService.findByMemberId(파랑_id); + List actual = subscriptions.getSubscriptions(); + // then + assertThat(actual).hasSize(2); + } + @DisplayName("새로운 카테고리를 생성 할 떄 이름이 공백이거나 길이가 20을 초과하는 경우 예외를 던진다.") @ParameterizedTest @ValueSource(strings = {"", "일이삼사오육칠팔구십일이삼사오육칠팔구십일", "알록달록 알록달록 알록달록 알록달록 알록달록 알록달록 카테고리"}) @@ -145,7 +168,24 @@ class CategoryServiceTest extends ServiceTest { // then assertThatThrownBy(() -> categoryService.save(후디.getId(), 대한민국_공휴일_생성_요청)) - .isInstanceOf(DuplicatedExternalCategoryException.class); + .isInstanceOf(ExistExternalCategoryException.class); + } + + + @DisplayName("외부 카테고리 생성 시 자동으로 구독한다.") + @Test + void 외부_카테고리_생성_시_자동으로_구독한다() { + // given + Long 파랑_id = parseMemberId(AuthFixtures.파랑_인증_코드_토큰_요청()); + + // when + categoryService.save(파랑_id, 대한민국_공휴일_생성_요청); + + SubscriptionsResponse subscriptions = subscriptionService.findByMemberId(파랑_id); + List actual = subscriptions.getSubscriptions(); + + // then + assertThat(actual).hasSize(2); } @DisplayName("페이지와 제목을 받아 해당하는 구간의 카테고리를 가져온다.") @@ -176,9 +216,8 @@ class CategoryServiceTest extends ServiceTest { @Test void 개인_카테고리는_전체_조회_대상에서_제외된다() { // given - MemberResponse 후디 = memberService.save(후디()); // 후디의 개인 카테고리가 생성된다 - MemberResponse 리버 = memberService.save(리버()); // 리버의 개인 카테고리가 생성된다 - categoryService.save(후디.getId(), 내_일정_생성_요청); + authService.generateToken(후디_인증_코드_토큰_요청()); + authService.generateToken(리버_인증_코드_토큰_요청()); // when CategoriesResponse response = categoryService.findNormalByName("", PageRequest.of(0, 10)); @@ -202,7 +241,7 @@ class CategoryServiceTest extends ServiceTest { PageRequest request = PageRequest.of(1, 2); // when - CategoriesResponse response = categoryService.findMineByName(관리자_ID, "", request); + CategoriesResponse response = categoryService.findMyCategories(관리자_ID, "", request); // then assertThat(response.getCategories()) @@ -226,7 +265,7 @@ class CategoryServiceTest extends ServiceTest { PageRequest request = PageRequest.of(0, 3); // when - CategoriesResponse response = categoryService.findMineByName(관리자_ID, "일", request); + CategoriesResponse response = categoryService.findMyCategories(관리자_ID, "일", request); // then assertThat(response.getCategories()) @@ -275,7 +314,7 @@ class CategoryServiceTest extends ServiceTest { // when categoryService.update(관리자.getId(), 공통_일정.getId(), categoryUpdateRequest); - Category category = categoryService.getCategory(공통_일정.getId()); + Category category = categoryRepository.getById(공통_일정.getId()); //then assertThat(category.getName()).isEqualTo(우테코_공통_일정_이름); @@ -291,7 +330,7 @@ class CategoryServiceTest extends ServiceTest { // when & then assertThatThrownBy(() -> categoryService.update(관리자.getId(), 공통_일정.getId() + 1, categoryUpdateRequest)) - .isInstanceOf(NoSuchCategoryException.class); + .isInstanceOf(NoPermissionException.class); } @DisplayName("자신이 만들지 않은 카테고리를 수정할 경우 예외를 던진다.") @@ -317,10 +356,10 @@ class CategoryServiceTest extends ServiceTest { CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); // when - categoryService.deleteById(관리자.getId(), 공통_일정.getId()); + categoryService.delete(관리자.getId(), 공통_일정.getId()); //then - assertThatThrownBy(() -> categoryService.getCategory(공통_일정.getId())) + assertThatThrownBy(() -> categoryRepository.getById(공통_일정.getId())) .isInstanceOf(NoSuchCategoryException.class); } @@ -332,8 +371,8 @@ class CategoryServiceTest extends ServiceTest { CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); // when & then - assertThatThrownBy(() -> categoryService.deleteById(관리자.getId(), 공통_일정.getId() + 1)) - .isInstanceOf(NoSuchCategoryException.class); + assertThatThrownBy(() -> categoryService.delete(관리자.getId(), 공통_일정.getId() + 1)) + .isInstanceOf(NoPermissionException.class); } @DisplayName("자신이 만들지 않은 카테고리를 삭제할 경우 예외를 던진다.") @@ -346,7 +385,7 @@ class CategoryServiceTest extends ServiceTest { // when & then assertThatThrownBy( - () -> categoryService.deleteById(매트.getId(), 공통_일정.getId())) + () -> categoryService.delete(매트.getId(), 공통_일정.getId())) .isInstanceOf(NoPermissionException.class); } @@ -360,7 +399,7 @@ class CategoryServiceTest extends ServiceTest { ScheduleResponse 레벨_인터뷰 = scheduleService.save(관리자.getId(), 공통_일정.getId(), 레벨_인터뷰_생성_요청); // when - categoryService.deleteById(관리자.getId(), 공통_일정.getId()); + categoryService.delete(관리자.getId(), 공통_일정.getId()); // then assertAll(() -> { @@ -382,7 +421,7 @@ class CategoryServiceTest extends ServiceTest { SubscriptionResponse 구독 = subscriptionService.save(후디.getId(), 공통_일정.getId()); // when - categoryService.deleteById(관리자.getId(), 공통_일정.getId()); + categoryService.delete(관리자.getId(), 공통_일정.getId()); // then assertThatThrownBy(() -> subscriptionService.findById(구독.getId())) @@ -395,49 +434,12 @@ class CategoryServiceTest extends ServiceTest { // given Member 관리자 = memberRepository.save(관리자()); CategoryResponse 내_일정 = categoryService.save(관리자.getId(), 내_일정_생성_요청); - subscriptionService.save(관리자.getId(), 내_일정.getId()); // when & then - assertThatThrownBy(() -> categoryService.deleteById(관리자.getId(), 내_일정.getId())) + assertThatThrownBy(() -> categoryService.delete(관리자.getId(), 내_일정.getId())) .isInstanceOf(InvalidCategoryException.class); } - @DisplayName("특정 회원의 카테고리를 전부 삭제한다.") - @Test - void 특정_회원의_카테고리를_전부_삭제한다() { - // given - Member 관리자 = memberRepository.save(관리자()); - CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); - - // when - categoryService.deleteByMemberId(관리자.getId()); - - //then - assertThatThrownBy(() -> categoryService.getCategory(공통_일정.getId())) - .isInstanceOf(NoSuchCategoryException.class); - } - - @DisplayName("특정 회원의 카테고리를 삭제할 때 연관된 일정도 삭제한다.") - @Test - void 특정_회원의_카테고리를_삭제할_때_연관된_일정도_삭제한다() { - // given - Member 관리자 = memberRepository.save(관리자()); - CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); - ScheduleResponse 알록달록_회식 = scheduleService.save(관리자.getId(), 공통_일정.getId(), 알록달록_회식_생성_요청); - ScheduleResponse 레벨_인터뷰 = scheduleService.save(관리자.getId(), 공통_일정.getId(), 레벨_인터뷰_생성_요청); - - // when - categoryService.deleteByMemberId(관리자.getId()); - - // then - assertAll(() -> { - assertThatThrownBy(() -> scheduleService.findById(알록달록_회식.getId())) - .isInstanceOf(NoSuchScheduleException.class); - assertThatThrownBy(() -> scheduleService.findById(레벨_인터뷰.getId())) - .isInstanceOf(NoSuchScheduleException.class); - }); - } - @DisplayName("외부 캘린더의 카테고리를 삭제한다.") @Test void 외부_캘린더의_카테고리를_삭제한다() { @@ -446,7 +448,7 @@ class CategoryServiceTest extends ServiceTest { CategoryResponse 우아한테크코스_외부_일정 = categoryService.save(관리자.getId(), 우아한테크코스_외부_일정_생성_요청); // when - categoryService.deleteById(관리자.getId(), 우아한테크코스_외부_일정.getId()); + categoryService.delete(관리자.getId(), 우아한테크코스_외부_일정.getId()); // then assertThatThrownBy(() -> categoryService.findById(우아한테크코스_외부_일정.getId())) diff --git a/backend/src/test/java/com/allog/dallog/domain/category/application/ExternalCategoryDetailServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/category/application/ExternalCategoryDetailServiceTest.java new file mode 100644 index 00000000..07c58429 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/category/application/ExternalCategoryDetailServiceTest.java @@ -0,0 +1,56 @@ +package com.allog.dallog.domain.category.application; + +import static com.allog.dallog.common.fixtures.AuthFixtures.리버_인증_코드_토큰_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.외부_BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.외부_FE_일정_생성_요청; +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetail; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetailRepository; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class ExternalCategoryDetailServiceTest extends ServiceTest { + + @Autowired + private ExternalCategoryDetailService externalCategoryDetailService; + + @Autowired + private CategoryService categoryService; + + @Autowired + private ExternalCategoryDetailRepository externalCategoryDetailRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @DisplayName("월별 일정 조회 시, 유저 ID로 해당하는 외부 연동 카테고리 전체를 조회한다.") + @Test + void 월별_일정_조회_시_유저_ID로_해당하는_외부_연동_카테고리의_전체를_조회한다() { + // given + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + + CategoryResponse 외부_BE_일정_응답 = categoryService.save(리버_id, 외부_BE_일정_생성_요청); + Category 외부_BE_일정 = categoryRepository.getById(외부_BE_일정_응답.getId()); + + CategoryResponse 외부_FE_일정_응답 = categoryService.save(리버_id, 외부_FE_일정_생성_요청); + Category 외부_FE_일정 = categoryRepository.getById(외부_FE_일정_응답.getId()); + + externalCategoryDetailRepository.save(new ExternalCategoryDetail(외부_BE_일정, "1111111")); + externalCategoryDetailRepository.save(new ExternalCategoryDetail(외부_FE_일정, "2222222")); + + // when + List details = externalCategoryDetailService.findByMemberId(리버_id); + + // then + assertThat(details).hasSize(2); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryRepositoryTest.java index c2be8d78..f9574883 100644 --- a/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryRepositoryTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryRepositoryTest.java @@ -6,9 +6,9 @@ import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_이름; import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_이름; -import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_일정; import static com.allog.dallog.common.fixtures.CategoryFixtures.내_일정; import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라; +import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_일정; import static com.allog.dallog.common.fixtures.CategoryFixtures.후디_JPA_스터디; import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; import static com.allog.dallog.common.fixtures.MemberFixtures.후디; @@ -96,7 +96,7 @@ class CategoryRepositoryTest extends RepositoryTest { PageRequest pageRequest = PageRequest.of(0, 8); // when - Slice categories = categoryRepository.findByMemberIdLikeCategoryName(관리자.getId(), "일", pageRequest); + Slice categories = categoryRepository.findByMemberIdAndNameContaining(관리자.getId(), "일", pageRequest); // then assertAll(() -> { @@ -182,7 +182,7 @@ class CategoryRepositoryTest extends RepositoryTest { categoryRepository.deleteByMemberId(관리자.getId()); // then - assertThat(categoryRepository.findByMemberIdLikeCategoryName(관리자.getId(), "", pageRequest)) + assertThat(categoryRepository.findByMemberIdAndNameContaining(관리자.getId(), "", pageRequest)) .hasSize(0); } } diff --git a/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTest.java b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTest.java index 815935db..72520ee2 100644 --- a/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTest.java @@ -4,12 +4,14 @@ import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_일정; import static com.allog.dallog.common.fixtures.CategoryFixtures.내_일정; import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.리버; import static com.allog.dallog.common.fixtures.MemberFixtures.후디; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import com.allog.dallog.domain.category.exception.InvalidCategoryException; +import com.allog.dallog.domain.member.domain.Member; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -66,7 +68,7 @@ class CategoryTest { Category BE_일정 = BE_일정(관리자()); // when - boolean actual = BE_일정.isCreator(999L); + boolean actual = BE_일정.isCreatorId(999L); // then assertThat(actual).isFalse(); diff --git a/backend/src/test/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepositoryTest.java new file mode 100644 index 00000000..c002fd43 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepositoryTest.java @@ -0,0 +1,82 @@ +package com.allog.dallog.domain.category.domain; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_일정; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import com.allog.dallog.common.annotation.RepositoryTest; +import com.allog.dallog.domain.category.exception.ExistExternalCategoryException; +import com.allog.dallog.domain.category.exception.NoSuchExternalCategoryDetailException; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class ExternalCategoryDetailRepositoryTest extends RepositoryTest { + + @Autowired + private ExternalCategoryDetailRepository externalCategoryDetailRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @DisplayName("존재하지 않는 외부 카테고리 세부정보를 가져오는 경우 예외를 던진다.") + @Test + void 존재하지_않는_외부_카테고리_세부정보를_가져오는_경우_예외를_던진다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category 우아한테크코스_일정 = 우아한테크코스_일정(관리자); + categoryRepository.save(우아한테크코스_일정); + + externalCategoryDetailRepository.save(new ExternalCategoryDetail(우아한테크코스_일정, "externalId")); + + Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + + // when & then + assertThatThrownBy(() -> externalCategoryDetailRepository.getByCategory(공통_일정)) + .isInstanceOf(NoSuchExternalCategoryDetailException.class); + } + + @DisplayName("새로운 외부 카테고리 세부정보인 경우 예외를 던지지 않는다.") + @Test + void 새로운_외부_카테고리_세부정보인_경우_예외를_던지지_않는다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category 우아한테크코스_일정 = 우아한테크코스_일정(관리자); + categoryRepository.save(우아한테크코스_일정); + + String externalId = "externalId"; + externalCategoryDetailRepository.save(new ExternalCategoryDetail(우아한테크코스_일정, externalId)); + + // when & then + assertDoesNotThrow(() -> externalCategoryDetailRepository + .validateExistByExternalIdAndCategoryIn(externalId, List.of())); + } + + @DisplayName("이미 존재하는 외부 카테고리 세부정보인 경우 예외를 던진다.") + @Test + void 이미_존재하는_외부_카테고리_세부정보인_경우_예외를_던진다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category 우아한테크코스_일정 = 우아한테크코스_일정(관리자); + categoryRepository.save(우아한테크코스_일정); + + String externalId = "externalId"; + externalCategoryDetailRepository.save(new ExternalCategoryDetail(우아한테크코스_일정, externalId)); + + // when & then + assertThatThrownBy(() -> externalCategoryDetailRepository + .validateExistByExternalIdAndCategoryIn(externalId, List.of(우아한테크코스_일정))) + .isInstanceOf(ExistExternalCategoryException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/composition/application/CalendarServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/composition/application/CalendarServiceTest.java deleted file mode 100644 index 52762016..00000000 --- a/backend/src/test/java/com/allog/dallog/domain/composition/application/CalendarServiceTest.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.allog.dallog.domain.composition.application; - -import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; -import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; -import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; -import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_외부_일정_생성_요청; -import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; -import static com.allog.dallog.common.fixtures.MemberFixtures.리버; -import static com.allog.dallog.common.fixtures.MemberFixtures.매트; -import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; -import static com.allog.dallog.common.fixtures.MemberFixtures.후디; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_1분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_17시_0분; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -import com.allog.dallog.common.annotation.ServiceTest; -import com.allog.dallog.domain.auth.domain.OAuthToken; -import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; -import com.allog.dallog.domain.category.application.CategoryService; -import com.allog.dallog.domain.category.domain.Category; -import com.allog.dallog.domain.category.domain.ExternalCategoryDetail; -import com.allog.dallog.domain.category.domain.ExternalCategoryDetailRepository; -import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; -import com.allog.dallog.domain.member.application.MemberService; -import com.allog.dallog.domain.member.dto.MemberResponse; -import com.allog.dallog.domain.schedule.application.ScheduleService; -import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; -import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; -import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponse; -import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponses; -import com.allog.dallog.domain.subscription.application.SubscriptionService; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -class CalendarServiceTest extends ServiceTest { - - @Autowired - private CalendarService calendarService; - - @Autowired - private MemberService memberService; - - @Autowired - private CategoryService categoryService; - - @Autowired - private OAuthTokenRepository oAuthTokenRepository; - - @Autowired - private ExternalCategoryDetailRepository externalCategoryDetailRepository; - - @Autowired - private SubscriptionService subscriptionService; - - @Autowired - private ScheduleService scheduleService; - - @DisplayName("시작일시와 종료일시로 유저의 캘린더를 일정 유형에 따라 분류하고 정렬하여 반환한다.") - @Test - void 시작일시와_종료일시로_유저의_캘린더를_일정_유형에_따라_분류하고_정렬하여_반환한다() { - // given - MemberResponse 후디 = memberService.save(후디()); - oAuthTokenRepository.save(new OAuthToken(memberService.getMember(후디.getId()), "wegwefaasdasdasda")); - - CategoryResponse BE_일정_응답 = categoryService.save(후디.getId(), BE_일정_생성_요청); - Category BE_일정 = categoryService.getCategory(BE_일정_응답.getId()); - - subscriptionService.save(후디.getId(), BE_일정.getId()); - - /* 장기간 일정 */ - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("장기간 첫번째", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("장기간 두번째", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분, "")); - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("장기간 세번째", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_16일_16시_1분, "")); - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("장기간 네번째", 날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_15일_16시_0분, "")); - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("장기간 다섯번째", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_17시_0분, "")); - - /* 종일 일정 */ - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("종일 첫번째", 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_10일_11시_59분, "")); - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("종일 두번째", 날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_20일_11시_59분, "")); - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("종일 세번째", 날짜_2022년_7월_27일_0시_0분, 날짜_2022년_7월_27일_11시_59분, "")); - - /* 몇시간 일정 */ - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("몇시간 첫번째", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_20시_0분, "")); - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("몇시간 두번째", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, "")); - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("몇시간 세번째", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); - scheduleService.save(후디.getId(), BE_일정.getId(), - new ScheduleCreateRequest("몇시간 네번째", 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_18시_0분, "")); - - CategoryResponse 우아한테크코스_외부_일정_응답 = categoryService.save(후디.getId(), 우아한테크코스_외부_일정_생성_요청); - Category 우아한테크코스 = categoryService.getCategory(우아한테크코스_외부_일정_응답.getId()); - externalCategoryDetailRepository.save(new ExternalCategoryDetail(우아한테크코스, "dfggsdfasdasadsgs")); - - subscriptionService.save(후디.getId(), 우아한테크코스.getId()); - - // when - MemberScheduleResponses memberScheduleResponses = calendarService.findSchedulesByMemberId(후디.getId(), - new DateRangeRequest("2022-07-01T00:00", "2022-08-15T23:59")); - - // then - assertAll(() -> { - assertThat(memberScheduleResponses.getLongTerms()).extracting(MemberScheduleResponse::getTitle) - .contains("장기간 첫번째", "장기간 두번째", "장기간 세번째", "장기간 네번째", "장기간 다섯번째"); - assertThat(memberScheduleResponses.getAllDays()).extracting(MemberScheduleResponse::getTitle) - .contains("종일 첫번째", "종일 두번째", "종일 세번째"); - assertThat(memberScheduleResponses.getFewHours()).extracting(MemberScheduleResponse::getTitle) - .contains("몇시간 첫번째", "몇시간 두번째", "몇시간 세번째", "몇시간 네번째"); - }); - } - - // TODO: 외부 일정을 잘 가져오는지를 테스트 해야함 - @DisplayName("카테고리를 구독하는 유저들의 모든 내부 일정을 가져온다.") - @Test - void 카테고리를_구독하는_유저들의_모든_내부_일정을_가져온다() { - // given - MemberResponse 관리자 = memberService.save(관리자()); - CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); - CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); - CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); - - /* 카테고리에 일정 추가 */ - scheduleService.save(관리자.getId(), 공통_일정.getId(), - new ScheduleCreateRequest("공통 일정 1", 날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_10일_0시_0분, "")); - scheduleService.save(관리자.getId(), 공통_일정.getId(), - new ScheduleCreateRequest("공통 일정 2", 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_15일_16시_0분, "")); - scheduleService.save(관리자.getId(), 공통_일정.getId(), - new ScheduleCreateRequest("공통 일정 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); - scheduleService.save(관리자.getId(), BE_일정.getId(), - new ScheduleCreateRequest("백엔드 일정 1", 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_20시_0분, "")); - scheduleService.save(관리자.getId(), BE_일정.getId(), - new ScheduleCreateRequest("백엔드 일정 2", 날짜_2022년_7월_16일_20시_0분, 날짜_2022년_7월_20일_0시_0분, "")); - scheduleService.save(관리자.getId(), BE_일정.getId(), - new ScheduleCreateRequest("백엔드 일정 3", 날짜_2022년_7월_20일_11시_59분, 날짜_2022년_7월_27일_0시_0분, "")); - scheduleService.save(관리자.getId(), FE_일정.getId(), - new ScheduleCreateRequest("프론트엔드 일정 1", 날짜_2022년_7월_27일_11시_59분, 날짜_2022년_7월_31일_0시_0분, "")); - scheduleService.save(관리자.getId(), FE_일정.getId(), - new ScheduleCreateRequest("프론트엔드 일정 2", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); - - /* 카테고리 구독 */ - MemberResponse 후디 = memberService.save(후디()); - MemberResponse 파랑 = memberService.save(파랑()); - MemberResponse 매트 = memberService.save(매트()); - MemberResponse 리버 = memberService.save(리버()); - - subscriptionService.save(후디.getId(), 공통_일정.getId()); - subscriptionService.save(파랑.getId(), 공통_일정.getId()); - subscriptionService.save(매트.getId(), 공통_일정.getId()); - subscriptionService.save(리버.getId(), 공통_일정.getId()); - - subscriptionService.save(매트.getId(), BE_일정.getId()); - subscriptionService.save(매트.getId(), FE_일정.getId()); - subscriptionService.save(리버.getId(), FE_일정.getId()); - - // when - List actual = calendarService.getSchedulesOfSubscriberIds( - List.of(후디.getId(), 파랑.getId(), 매트.getId(), 리버.getId()), - new DateRangeRequest("2022-07-07T16:00", "2022-08-15T14:00")); - - // then - assertThat(actual.stream().map(IntegrationSchedule::getTitle)) - .containsExactly("공통 일정 1", "공통 일정 2", "공통 일정 3", "백엔드 일정 1", "백엔드 일정 2", "백엔드 일정 3", "프론트엔드 일정 1", - "프론트엔드 일정 2"); - } -} diff --git a/backend/src/test/java/com/allog/dallog/domain/composition/application/CategorySubscriptionServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/composition/application/CategorySubscriptionServiceTest.java deleted file mode 100644 index 975e3fee..00000000 --- a/backend/src/test/java/com/allog/dallog/domain/composition/application/CategorySubscriptionServiceTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.allog.dallog.domain.composition.application; - -import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; -import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.대한민국_공휴일_생성_요청; -import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; -import static org.assertj.core.api.Assertions.assertThat; - -import com.allog.dallog.common.annotation.ServiceTest; -import com.allog.dallog.domain.member.application.MemberService; -import com.allog.dallog.domain.member.dto.MemberResponse; -import com.allog.dallog.domain.subscription.application.SubscriptionService; -import com.allog.dallog.domain.subscription.domain.Subscription; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -class CategorySubscriptionServiceTest extends ServiceTest { - - @Autowired - private CategorySubscriptionService categorySubscriptionService; - - @Autowired - private MemberService memberService; - - @Autowired - private SubscriptionService subscriptionService; - - @DisplayName("카테고리 생성 시 자동으로 구독한다.") - @Test - void 카테고리_생성_시_자동으로_구독한다() { - // given - MemberResponse 파랑 = memberService.save(파랑()); - - // when - categorySubscriptionService.save(파랑.getId(), 공통_일정_생성_요청); - - List subscriptions = subscriptionService.getAllByMemberId(파랑.getId()); - - // then - assertThat(subscriptions).hasSize(1); - } - - @DisplayName("외부 카테고리 생성 시 자동으로 구독한다.") - @Test - void 외부_카테고리_생성_시_자동으로_구독한다() { - // given - MemberResponse 파랑 = memberService.save(파랑()); - - // when - categorySubscriptionService.save(파랑.getId(), 대한민국_공휴일_생성_요청); - - List subscriptions = subscriptionService.getAllByMemberId(파랑.getId()); - - // then - assertThat(subscriptions).hasSize(1); - } -} diff --git a/backend/src/test/java/com/allog/dallog/domain/composition/application/RegisterServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/composition/application/RegisterServiceTest.java deleted file mode 100644 index 90200807..00000000 --- a/backend/src/test/java/com/allog/dallog/domain/composition/application/RegisterServiceTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.allog.dallog.domain.composition.application; - -import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_MEMBER; -import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import com.allog.dallog.common.annotation.ServiceTest; -import com.allog.dallog.domain.auth.dto.OAuthMember; -import com.allog.dallog.domain.category.application.CategoryService; -import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.category.exception.NoSuchCategoryException; -import com.allog.dallog.domain.member.application.MemberService; -import com.allog.dallog.domain.member.dto.MemberResponse; -import com.allog.dallog.domain.member.exception.NoSuchMemberException; -import com.allog.dallog.domain.subscription.application.SubscriptionService; -import com.allog.dallog.domain.subscription.domain.Subscription; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -class RegisterServiceTest extends ServiceTest { - - @Autowired - private RegisterService registerService; - - @Autowired - private MemberService memberService; - - @Autowired - private CategoryService categoryService; - - @Autowired - private SubscriptionService subscriptionService; - - @DisplayName("유저 생성 시 개인 카테고리를 자동으로 생성하고 구독한다.") - @Test - void 유저_생성_시_개인_카테고리를_자동으로_생성하고_구독한다() { - // given & when - OAuthMember member = STUB_OAUTH_MEMBER(); - MemberResponse memberResponse = registerService.register(member); - - List subscriptions = subscriptionService.getAllByMemberId(memberResponse.getId()); - - // then - assertAll(() -> { - assertThat(memberResponse.getEmail()).isEqualTo(member.getEmail()); - assertThat(subscriptions).hasSize(1); - }); - } - - @DisplayName("유저 삭제 시 연관된 구독과 카테고리를 순차적으로 삭제한다.") - @Test - void 유저_삭제_시_연관된_구독과_카테고리를_순차적으로_삭제한다() { - // given - OAuthMember member = STUB_OAUTH_MEMBER(); - MemberResponse memberResponse = registerService.register(member); - Long memberId = memberResponse.getId(); - - CategoryResponse categoryResponse = categoryService.save(memberId, 공통_일정_생성_요청); - subscriptionService.save(memberId, categoryResponse.getId()); - - // when - registerService.deleteByMemberId(memberId); - - // then - assertAll(() -> { - assertThatThrownBy(() -> memberService.getMember(memberId)) - .isInstanceOf(NoSuchMemberException.class); - assertThatThrownBy(() -> categoryService.getCategory(memberId)) - .isInstanceOf(NoSuchCategoryException.class); - assertThat(subscriptionService.getAllByMemberId(memberId)).hasSize(0); - }); - } -} diff --git a/backend/src/test/java/com/allog/dallog/domain/composition/application/SchedulerServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/composition/application/SchedulerServiceTest.java deleted file mode 100644 index f8294715..00000000 --- a/backend/src/test/java/com/allog/dallog/domain/composition/application/SchedulerServiceTest.java +++ /dev/null @@ -1,186 +0,0 @@ -package com.allog.dallog.domain.composition.application; - -import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; -import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; -import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; -import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; -import static com.allog.dallog.common.fixtures.MemberFixtures.리버; -import static com.allog.dallog.common.fixtures.MemberFixtures.매트; -import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; -import static com.allog.dallog.common.fixtures.MemberFixtures.후디; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_1분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -import com.allog.dallog.common.annotation.ServiceTest; -import com.allog.dallog.domain.category.application.CategoryService; -import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.member.application.MemberService; -import com.allog.dallog.domain.member.dto.MemberResponse; -import com.allog.dallog.domain.schedule.application.ScheduleService; -import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; -import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; -import com.allog.dallog.domain.schedule.dto.response.PeriodResponse; -import com.allog.dallog.domain.subscription.application.SubscriptionService; -import com.allog.dallog.domain.subscription.domain.Color; -import com.allog.dallog.domain.subscription.dto.request.SubscriptionUpdateRequest; -import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; -import java.time.LocalDateTime; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -class SchedulerServiceTest extends ServiceTest { - - @Autowired - private SchedulerService schedulerService; - - @Autowired - private ScheduleService scheduleService; - - @Autowired - private CategoryService categoryService; - - @Autowired - private SubscriptionService subscriptionService; - - @Autowired - private MemberService memberService; - - @DisplayName("비어있는 기간 목록을 반환한다.") - @Test - void 비어있는_기간_목록을_반환한다() { - // given - /* 관리자 및 카테고리 생성 */ - MemberResponse 관리자 = memberService.save(관리자()); - CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); - CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); - CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); - - /* 카테고리에 일정 추가 */ - scheduleService.save(관리자.getId(), 공통_일정.getId(), - new ScheduleCreateRequest("공통 일정 1", 날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_10일_0시_0분, "")); - scheduleService.save(관리자.getId(), 공통_일정.getId(), - new ScheduleCreateRequest("공통 일정 2", 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_15일_16시_0분, "")); - scheduleService.save(관리자.getId(), 공통_일정.getId(), - new ScheduleCreateRequest("공통 일정 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); - scheduleService.save(관리자.getId(), BE_일정.getId(), - new ScheduleCreateRequest("백엔드 일정 1", 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_20시_0분, "")); - scheduleService.save(관리자.getId(), BE_일정.getId(), - new ScheduleCreateRequest("백엔드 일정 2", 날짜_2022년_7월_16일_20시_0분, 날짜_2022년_7월_20일_0시_0분, "")); - scheduleService.save(관리자.getId(), BE_일정.getId(), - new ScheduleCreateRequest("백엔드 일정 3", 날짜_2022년_7월_20일_11시_59분, 날짜_2022년_7월_27일_0시_0분, "")); - scheduleService.save(관리자.getId(), FE_일정.getId(), - new ScheduleCreateRequest("프론트엔드 일정 1", 날짜_2022년_7월_27일_11시_59분, 날짜_2022년_7월_31일_0시_0분, "")); - scheduleService.save(관리자.getId(), FE_일정.getId(), - new ScheduleCreateRequest("프론트엔드 일정 2", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); - - /* 카테고리 구독 */ - MemberResponse 후디 = memberService.save(후디()); - MemberResponse 파랑 = memberService.save(파랑()); - MemberResponse 매트 = memberService.save(매트()); - MemberResponse 리버 = memberService.save(리버()); - - subscriptionService.save(후디.getId(), 공통_일정.getId()); - subscriptionService.save(파랑.getId(), 공통_일정.getId()); - subscriptionService.save(매트.getId(), 공통_일정.getId()); - subscriptionService.save(리버.getId(), 공통_일정.getId()); - - subscriptionService.save(매트.getId(), BE_일정.getId()); - subscriptionService.save(매트.getId(), FE_일정.getId()); - subscriptionService.save(리버.getId(), FE_일정.getId()); - - // when - List actual = schedulerService.getAvailablePeriods(공통_일정.getId(), - new DateRangeRequest("2022-07-01T00:00", "2022-08-31T00:00")); - - // then - assertAll(() -> { - assertThat(actual).hasSize(7); - assertThat(actual.stream().map(PeriodResponse::getStartDateTime)).containsExactly(날짜_2022년_7월_1일_0시_0분, - 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_1분, 날짜_2022년_7월_20일_0시_0분, - 날짜_2022년_7월_27일_0시_0분, 날짜_2022년_8월_15일_14시_0분); - assertThat(actual.stream().map(PeriodResponse::getEndDateTime)).containsExactly(날짜_2022년_7월_7일_16시_0분, - 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_20일_11시_59분, - 날짜_2022년_7월_27일_11시_59분, LocalDateTime.of(2022, 8, 31, 0, 0)); - }); - } - - @DisplayName("체크하지 않은 구독은 일정 산정 대상에서 제외된다.") - @Test - void 체크하지_않은_구독은_일정_산정_대상에서_제외된다() { - // given - /* 관리자 및 카테고리 생성 */ - MemberResponse 관리자 = memberService.save(관리자()); - CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); - CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); - CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); - - /* 카테고리에 일정 추가 */ - scheduleService.save(관리자.getId(), 공통_일정.getId(), - new ScheduleCreateRequest("공통 일정 1", 날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_10일_0시_0분, "")); - scheduleService.save(관리자.getId(), 공통_일정.getId(), - new ScheduleCreateRequest("공통 일정 2", 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_15일_16시_0분, "")); - scheduleService.save(관리자.getId(), 공통_일정.getId(), - new ScheduleCreateRequest("공통 일정 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); - scheduleService.save(관리자.getId(), BE_일정.getId(), - new ScheduleCreateRequest("백엔드 일정 1", 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_20시_0분, "")); - scheduleService.save(관리자.getId(), BE_일정.getId(), - new ScheduleCreateRequest("백엔드 일정 2", 날짜_2022년_7월_16일_20시_0분, 날짜_2022년_7월_20일_0시_0분, "")); - scheduleService.save(관리자.getId(), BE_일정.getId(), - new ScheduleCreateRequest("백엔드 일정 3", 날짜_2022년_7월_20일_11시_59분, 날짜_2022년_7월_27일_0시_0분, "")); - scheduleService.save(관리자.getId(), FE_일정.getId(), - new ScheduleCreateRequest("프론트엔드 일정 1", 날짜_2022년_7월_27일_11시_59분, 날짜_2022년_7월_31일_0시_0분, "")); - scheduleService.save(관리자.getId(), FE_일정.getId(), - new ScheduleCreateRequest("프론트엔드 일정 2", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); - - /* 카테고리 구독 */ - MemberResponse 후디 = memberService.save(후디()); - MemberResponse 파랑 = memberService.save(파랑()); - MemberResponse 매트 = memberService.save(매트()); - MemberResponse 리버 = memberService.save(리버()); - - subscriptionService.save(후디.getId(), 공통_일정.getId()); - subscriptionService.save(파랑.getId(), 공통_일정.getId()); - subscriptionService.save(매트.getId(), 공통_일정.getId()); - subscriptionService.save(리버.getId(), 공통_일정.getId()); - subscriptionService.save(매트.getId(), BE_일정.getId()); - - SubscriptionResponse 매트_FE_일정_구독 = subscriptionService.save(매트.getId(), FE_일정.getId()); - SubscriptionResponse 리버_FE_일정_구독 = subscriptionService.save(리버.getId(), FE_일정.getId()); - subscriptionService.update(매트_FE_일정_구독.getId(), 매트.getId(), - new SubscriptionUpdateRequest(Color.COLOR_1, false)); - subscriptionService.update(리버_FE_일정_구독.getId(), 리버.getId(), - new SubscriptionUpdateRequest(Color.COLOR_1, false)); - - // when - List actual = schedulerService.getAvailablePeriods(공통_일정.getId(), - new DateRangeRequest("2022-07-01T00:00", "2022-08-31T00:00")); - - // then - assertAll(() -> { - assertThat(actual).hasSize(6); - assertThat(actual.stream().map(PeriodResponse::getStartDateTime)).containsExactly(날짜_2022년_7월_1일_0시_0분, - 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_1분, 날짜_2022년_7월_20일_0시_0분, - 날짜_2022년_7월_27일_0시_0분); - assertThat(actual.stream().map(PeriodResponse::getEndDateTime)).containsExactly(날짜_2022년_7월_7일_16시_0분, - 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_20일_11시_59분, - LocalDateTime.of(2022, 8, 31, 0, 0)); - }); - } -} diff --git a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDaoTest.java b/backend/src/test/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDaoTest.java deleted file mode 100644 index 56ccf579..00000000 --- a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDaoTest.java +++ /dev/null @@ -1,361 +0,0 @@ -package com.allog.dallog.domain.integrationschedule.dao; - -import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; -import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정; -import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; -import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라; -import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; -import static com.allog.dallog.common.fixtures.MemberFixtures.후디; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_1분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_17일_23시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_17시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_23시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_메모; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_제목; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.매고라_메모; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.매고라_제목; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_메모; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_제목; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; -import static org.assertj.core.api.Assertions.assertThat; - -import com.allog.dallog.common.config.ExternalApiConfig; -import com.allog.dallog.domain.category.domain.Category; -import com.allog.dallog.domain.category.domain.CategoryRepository; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; -import com.allog.dallog.domain.member.domain.Member; -import com.allog.dallog.domain.member.domain.MemberRepository; -import com.allog.dallog.domain.schedule.domain.Schedule; -import com.allog.dallog.domain.schedule.domain.ScheduleRepository; -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; - -@SpringBootTest -@Import(ExternalApiConfig.class) -class IntegrationScheduleDaoTest { - - @Autowired - private MemberRepository memberRepository; - - @Autowired - private ScheduleRepository scheduleRepository; - - @Autowired - private CategoryRepository categoryRepository; - - @Autowired - private IntegrationScheduleDao integrationScheduleDao; - - @DisplayName("카테코리와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.") - @Test - void 카테고리와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다() { - // given - Member 관리자 = memberRepository.save(관리자()); - - Category BE_일정 = categoryRepository.save(BE_일정(관리자)); - - Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); - Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); - - scheduleRepository.save(알록달록_회의); - scheduleRepository.save(알록달록_회식); - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(List.of(BE_일정.getId()), - 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분); - - // then - assertThat(actual).hasSize(1); - } - - @DisplayName("조회하기 위한 category id의 크기가 0인 경우 빈 리스트를 반환한다.") - @Test - void 조회하기_위한_category_id의_크기가_0인_경우_빈_리스트를_반환한다() { - // given - Member 관리자 = memberRepository.save(관리자()); - - Category BE_일정 = categoryRepository.save(BE_일정(관리자)); - - Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); - Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); - - scheduleRepository.save(알록달록_회의); - scheduleRepository.save(알록달록_회식); - - List categoryIds = Collections.emptyList(); - LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; - LocalDateTime endDateTime = 날짜_2022년_7월_31일_0시_0분; - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, - startDateTime, endDateTime); - - // then - assertThat(actual).hasSize(0); - } - - @DisplayName("카테고리가 여러 개 일 때, 카테고리와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.") - @Test - void 카테고리가_여러_개_일_때_카테고리와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다() { - // given - Member 관리자 = memberRepository.save(관리자()); - - Category BE_일정 = categoryRepository.save(BE_일정(관리자)); - Category FE_일정 = categoryRepository.save(FE_일정(관리자)); - Category 매트_아고라 = categoryRepository.save(매트_아고라(관리자)); - - Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); - Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); - - Schedule 레벨_인터뷰 = new Schedule(FE_일정, 레벨_인터뷰_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 레벨_인터뷰_메모); - - Schedule 매고라 = new Schedule(매트_아고라, 매고라_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 매고라_메모); - - scheduleRepository.save(알록달록_회의); - scheduleRepository.save(알록달록_회식); - scheduleRepository.save(레벨_인터뷰); - scheduleRepository.save(매고라); - - List categoryIds = List.of(BE_일정.getId()); - LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; - LocalDateTime endDateTime = 날짜_2022년_7월_31일_0시_0분; - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, - startDateTime, endDateTime); - - // then - assertThat(actual).hasSize(1); - } - - @DisplayName("카테고리가 여러 개 일 때, 카테고리 id 리스트와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.") - @Test - void 카테고리가_여러_개_일_때_카테고리_id_리스트와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다() { - // given - Member 관리자 = memberRepository.save(관리자()); - - Category BE_일정 = categoryRepository.save(BE_일정(관리자)); - Category FE_일정 = categoryRepository.save(FE_일정(관리자)); - Category 매트_아고라 = categoryRepository.save(매트_아고라(관리자)); - - Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); - Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); - - Schedule 레벨_인터뷰 = new Schedule(FE_일정, 레벨_인터뷰_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 레벨_인터뷰_메모); - - Schedule 매고라 = new Schedule(매트_아고라, 매고라_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 매고라_메모); - - scheduleRepository.save(알록달록_회의); - scheduleRepository.save(알록달록_회식); - scheduleRepository.save(레벨_인터뷰); - scheduleRepository.save(매고라); - - List categoryIds = List.of(BE_일정.getId(), FE_일정.getId(), 매트_아고라.getId()); - LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; - LocalDateTime endDateTime = 날짜_2022년_7월_31일_0시_0분; - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, - startDateTime, endDateTime); - - // then - assertThat(actual).hasSize(3); - } - - @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 시작날짜가 종료일시와 같으면 조회한다.") - @Test - void 시작일시와_종료일시를_전달할_때_일정의_시작일시와_같으면_조회된다() { - // given - Member 관리자 = memberRepository.save(관리자()); - - Category BE_일정 = categoryRepository.save(BE_일정(관리자)); - - Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); - Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); - - scheduleRepository.save(알록달록_회의); - scheduleRepository.save(알록달록_회식); - - List categoryIds = List.of(BE_일정.getId()); - LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; - LocalDateTime endDateTime = 날짜_2022년_7월_15일_16시_0분; - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, - startDateTime, endDateTime); - - // then - assertThat(actual).hasSize(1); - } - - @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 시작날짜가 종료일시 이후이면 조회되지 않는다.") - @Test - void 카테고리와_시작일시_종료일시를_전달할_때_일정의_시작날짜가_종료일시_이후이면_조회되지_않는다() { - // given - Member 관리자 = memberRepository.save(관리자()); - - Category BE_일정 = categoryRepository.save(BE_일정(관리자)); - - Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); - Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); - - scheduleRepository.save(알록달록_회의); - scheduleRepository.save(알록달록_회식); - - List categoryIds = List.of(BE_일정.getId()); - LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; - LocalDateTime endDateTime = 날짜_2022년_7월_7일_16시_0분; - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, - startDateTime, endDateTime); - - // then - assertThat(actual).hasSize(0); - } - - @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 종료날짜가 시작일시와 같으면 조회된다.") - @Test - void 카테고리와_시작일시와_종료일시를_전달할_때_일정의_종료날짜가_시작일시와_같으면_조회된다() { - // given - Member 관리자 = memberRepository.save(관리자()); - - Category BE_일정 = categoryRepository.save(BE_일정(관리자)); - - Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); - Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); - - scheduleRepository.save(알록달록_회의); - scheduleRepository.save(알록달록_회식); - - List categoryIds = List.of(BE_일정.getId()); - LocalDateTime startDateTime = 날짜_2022년_7월_16일_16시_0분; - LocalDateTime endDateTime = 날짜_2022년_7월_31일_0시_0분; - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, - startDateTime, endDateTime); - - // then - assertThat(actual).hasSize(1); - } - - @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 종료날짜가 시작일시 이전이면 조회되지 않는다.") - @Test - void 카테고리와_시작일시와_종료일시를_전달할_때_일정의_종료날짜가_시작일시_이전이면_조회되지_않는다() { - // given - Member 관리자 = memberRepository.save(관리자()); - - Category BE_일정 = categoryRepository.save(BE_일정(관리자)); - - Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); - Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); - - scheduleRepository.save(알록달록_회의); - scheduleRepository.save(알록달록_회식); - - List categoryIds = List.of(BE_일정.getId()); - LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; - LocalDateTime endDateTime = 날짜_2022년_7월_7일_16시_0분; - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, - startDateTime, endDateTime); - - // then - assertThat(actual).hasSize(0); - } - - @DisplayName("시작일시와 종료일시로 특정 카테고리의 일정을 조회한다.") - @Test - void 시작일시와_종료일시로_특정_카테고리의_일정을_조회한다() { - // given - Member 후디 = memberRepository.save(후디()); - - Category BE_일정 = categoryRepository.save(BE_일정(후디)); - Category FE_일정 = categoryRepository.save(FE_일정(후디)); - Category 공통_일정 = categoryRepository.save(공통_일정(후디)); - - /* BE 일정 */ - scheduleRepository.save(new Schedule(BE_일정, "BE 1", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); - scheduleRepository.save(new Schedule(BE_일정, "BE 2", 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_10일_11시_59분, "")); - scheduleRepository.save(new Schedule(BE_일정, "BE 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_20시_0분, "")); - - /* FE 일정 */ - scheduleRepository.save(new Schedule(FE_일정, "FE 1", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분, "")); - scheduleRepository.save(new Schedule(FE_일정, "FE 2", 날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_20일_11시_59분, "")); - scheduleRepository.save(new Schedule(FE_일정, "FE 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, "")); - - /* 공통 일정 */ - scheduleRepository.save(new Schedule(공통_일정, "공통 1", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_16일_16시_1분, "")); - scheduleRepository.save(new Schedule(공통_일정, "공통 2", 날짜_2022년_7월_27일_0시_0분, 날짜_2022년_7월_27일_11시_59분, "")); - scheduleRepository.save(new Schedule(공통_일정, "공통 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); - - List categoryIds = List.of(BE_일정.getId(), FE_일정.getId()); - LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; - LocalDateTime endDateTime = 날짜_2022년_8월_15일_23시_59분; - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, - startDateTime, endDateTime); - - // then - assertThat(actual) - .extracting(IntegrationSchedule::getTitle) - .containsOnly("BE 1", "BE 2", "BE 3", "FE 1", "FE 2", "FE 3"); - } - - @DisplayName("시작일시와 종료일시로 특정 카테고리의 일정을 조회할 때 범위 밖의 일정은 제외된다.") - @Test - void 시작일시와_종료일시로_특정_카테고리의_일정을_조회할_때_범위_밖의_일정은_제외된다() { - // given - Member 후디 = memberRepository.save(후디()); - - Category BE_일정 = categoryRepository.save(BE_일정(후디)); - Category FE_일정 = categoryRepository.save(FE_일정(후디)); - - /* BE 일정 */ - scheduleRepository.save(new Schedule(BE_일정, "BE 1 포함", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); - scheduleRepository.save(new Schedule(BE_일정, "BE 2 포함", 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_10일_11시_59분, "")); - scheduleRepository.save(new Schedule(BE_일정, "BE 3 포함", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_20시_0분, "")); - scheduleRepository.save(new Schedule(BE_일정, "BE 3 미포함", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_17시_0분, "")); - - /* FE 일정 */ - scheduleRepository.save(new Schedule(FE_일정, "FE 1 포함", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분, "")); - scheduleRepository.save(new Schedule(FE_일정, "FE 2 포함", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, "")); - scheduleRepository.save(new Schedule(FE_일정, "FE 3 미포함", 날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_20일_11시_59분, "")); - - List categoryIds = List.of(BE_일정.getId(), FE_일정.getId()); - LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; - LocalDateTime endDateTime = 날짜_2022년_7월_17일_23시_59분; - - // when - List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, - startDateTime, endDateTime); - - // then - assertThat(actual).extracting(IntegrationSchedule::getTitle) - .containsOnly("BE 1 포함", "BE 2 포함", "BE 3 포함", "FE 1 포함", "FE 2 포함"); - } -} diff --git a/backend/src/test/java/com/allog/dallog/domain/member/application/MemberServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/member/application/MemberServiceTest.java index 7dcea0a5..221a6e0f 100644 --- a/backend/src/test/java/com/allog/dallog/domain/member/application/MemberServiceTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/member/application/MemberServiceTest.java @@ -1,18 +1,22 @@ package com.allog.dallog.domain.member.application; -import static com.allog.dallog.common.fixtures.MemberFixtures.리버_이메일; -import static com.allog.dallog.common.fixtures.MemberFixtures.매트; -import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; -import static com.allog.dallog.common.fixtures.MemberFixtures.파랑_이메일; -import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.common.fixtures.AuthFixtures; +import com.allog.dallog.common.fixtures.SubscriptionFixtures; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; import com.allog.dallog.domain.member.dto.MemberResponse; import com.allog.dallog.domain.member.dto.MemberUpdateRequest; import com.allog.dallog.domain.member.exception.NoSuchMemberException; +import com.allog.dallog.domain.subscription.domain.Subscription; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -22,106 +26,77 @@ class MemberServiceTest extends ServiceTest { @Autowired private MemberService memberService; - @DisplayName("회원을 저장한다.") - @Test - void 회원을_저장한다() { - // given & when - MemberResponse 파랑 = memberService.save(파랑()); - - // then - assertThat(파랑).isNotNull(); - } - - @DisplayName("이메일로 회원을 찾는다.") - @Test - void 이메일로_회원을_찾는다() { - // given - MemberResponse 파랑 = memberService.save(파랑()); + @Autowired + private MemberRepository memberRepository; - // when - Member actual = memberService.getByEmail(파랑_이메일); + @Autowired + private SubscriptionRepository subscriptionRepository; - // then - assertThat(actual.getId()).isEqualTo(파랑.getId()); - } + @Autowired + private CategoryRepository categoryRepository; @DisplayName("id를 통해 회원을 단건 조회한다.") @Test void id를_통해_회원을_단건_조회한다() { // given - MemberResponse 파랑 = memberService.save(파랑()); + Long 파랑_id = parseMemberId(AuthFixtures.파랑_인증_코드_토큰_요청()); // when & then - assertThat(memberService.findById(파랑.getId())) - .usingRecursiveComparison() - .isEqualTo(파랑); + assertThat(memberService.findById(파랑_id).getId()) + .isEqualTo(파랑_id); } - @DisplayName("회원의 이름을 수정한다.") + @DisplayName("구독 id를 기반으로 member 정보를 조회한다.") @Test - void 회원의_이름을_수정한다() { + void 구독_id를_기반으로_member_정보를_조회한다() { // given - MemberResponse 매트 = memberService.save(매트()); + Long 매트_id = parseMemberId(AuthFixtures.매트_인증_코드_토큰_요청()); + Member 매트 = memberRepository.getById(매트_id); - String 패트_이름 = "패트"; - MemberUpdateRequest 매트_수정_요청 = new MemberUpdateRequest(패트_이름); + Category BE_일정 = categoryRepository.save(BE_일정(매트)); + Subscription 색상_1_BE_일정_구독 = subscriptionRepository.save(SubscriptionFixtures.색상1_구독(매트, BE_일정)); // when - memberService.update(매트.getId(), 매트_수정_요청); + MemberResponse actual = memberService.findBySubscriptionId(색상_1_BE_일정_구독.getId()); // then - MemberResponse actual = memberService.findById(매트.getId()); - assertThat(actual.getDisplayName()).isEqualTo(패트_이름); + assertAll(() -> { + assertThat(actual.getId()).isEqualTo(매트.getId()); + assertThat(actual.getEmail()).isEqualTo(매트.getEmail()); + assertThat(actual.getDisplayName()).isEqualTo(매트.getDisplayName()); + assertThat(actual.getProfileImageUrl()).isEqualTo(매트.getProfileImageUrl()); + assertThat(actual.getSocialType()).isEqualTo(매트.getSocialType()); + }); } - @DisplayName("회원을 제거한다.") + @DisplayName("회원의 이름을 수정한다.") @Test - void 회원을_제거한다() { + void 회원의_이름을_수정한다() { // given - MemberResponse 후디 = memberService.save(후디()); - - // when - memberService.deleteById(후디.getId()); - - // then - assertThatThrownBy(() -> memberService.findById(후디.getId())) - .isInstanceOf(NoSuchMemberException.class); - } + Long 매트_id = parseMemberId(AuthFixtures.매트_인증_코드_토큰_요청()); - @DisplayName("주어진 이메일로 가입된 회원이 있으면 true를 반환한다.") - @Test - void 주어진_이메일로_가입된_회원이_있으면_true를_반환한다() { - // given - memberService.save(파랑()); + String 패트_이름 = "패트"; + MemberUpdateRequest 매트_수정_요청 = new MemberUpdateRequest(패트_이름); // when - boolean actual = memberService.existsByEmail(파랑_이메일); + memberService.update(매트_id, 매트_수정_요청); // then - assertThat(actual).isTrue(); + MemberResponse actual = memberService.findById(매트_id); + assertThat(actual.getDisplayName()).isEqualTo(패트_이름); } - @DisplayName("주어진 이메일로 가입된 회원이 없으면 false를 반환한다.") + @DisplayName("회원을 제거한다.") @Test - void 주어진_이메일로_가입된_회원이_없으면_false를_반환한다() { + void 회원을_제거한다() { // given - memberService.save(파랑()); + Long 후디_id = parseMemberId(AuthFixtures.후디_인증_코드_토큰_요청()); // when - boolean actual = memberService.existsByEmail(리버_이메일); + memberService.deleteById(후디_id); // then - assertThat(actual).isFalse(); - } - - @DisplayName("회원이 존재하지 않으면 예외를 던진다.") - @Test - void 회원이_존재하지_않으면_예외를_던진다() { - // given - Long id = 0L; - - // when & then - assertThatThrownBy(() -> memberService.validateExistsMember(id)) + assertThatThrownBy(() -> memberService.findById(후디_id)) .isInstanceOf(NoSuchMemberException.class); } } diff --git a/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberRepositoryTest.java index e63ffef3..2cdd1a04 100644 --- a/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberRepositoryTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberRepositoryTest.java @@ -3,10 +3,10 @@ import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; import static com.allog.dallog.common.fixtures.MemberFixtures.파랑_이메일; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.allog.dallog.common.annotation.RepositoryTest; -import com.allog.dallog.domain.category.domain.CategoryRepository; -import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; +import com.allog.dallog.domain.member.exception.NoSuchMemberException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -16,11 +16,15 @@ class MemberRepositoryTest extends RepositoryTest { @Autowired private MemberRepository memberRepository; - @Autowired - private CategoryRepository categoryRepository; + @DisplayName("중복된 이메일이 존재하는 경우 true를 반환한다.") + @Test + void 중복된_이메일이_존재하는_경우_true를_반환한다() { + // given + memberRepository.save(파랑()); - @Autowired - private SubscriptionRepository subscriptionRepository; + // when & then + assertThat(memberRepository.existsByEmail(파랑_이메일)).isTrue(); + } @DisplayName("이메일을 통해 회원을 찾는다.") @Test @@ -29,19 +33,31 @@ class MemberRepositoryTest extends RepositoryTest { Member 파랑 = memberRepository.save(파랑()); // when - Member actual = memberRepository.findByEmail(파랑_이메일).get(); + Member actual = memberRepository.getByEmail(파랑_이메일); // then assertThat(actual.getId()).isEqualTo(파랑.getId()); } - @DisplayName("중복된 이메일이 존재하는 경우 true를 반환한다.") + @DisplayName("존재하지 않는 email을 조회할 경우 예외를 던진다.") @Test - void 중복된_이메일이_존재하는_경우_true를_반환한다() { + void 존재하지_않는_email을_조회할_경우_예외를_던진다() { // given - memberRepository.save(파랑()); + String email = "dev.hyeonic@gmail.com"; + + // given & when & then + assertThatThrownBy(() -> memberRepository.getByEmail(email)) + .isInstanceOf(NoSuchMemberException.class); + } + + @DisplayName("존재하지 않는 id이면 예외를 던진다.") + @Test + void 존재하지_않는_id이면_예외를_던진다() { + // given + Long id = 0L; // when & then - assertThat(memberRepository.existsByEmail(파랑_이메일)).isTrue(); + assertThatThrownBy(() -> memberRepository.validateExistsById(id)) + .isInstanceOf(NoSuchMemberException.class); } } diff --git a/backend/src/test/java/com/allog/dallog/domain/schedule/application/ScheduleServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/application/ScheduleServiceTest.java index ec9653dc..c66ce384 100644 --- a/backend/src/test/java/com/allog/dallog/domain/schedule/application/ScheduleServiceTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/application/ScheduleServiceTest.java @@ -1,9 +1,10 @@ package com.allog.dallog.domain.schedule.application; +import static com.allog.dallog.common.fixtures.AuthFixtures.리버_인증_코드_토큰_요청; +import static com.allog.dallog.common.fixtures.AuthFixtures.후디_인증_코드_토큰_요청; import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.대한민국_공휴일_생성_요청; -import static com.allog.dallog.common.fixtures.MemberFixtures.리버; -import static com.allog.dallog.common.fixtures.MemberFixtures.후디; import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; @@ -11,11 +12,13 @@ import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_시작일시; import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_제목; import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_종료일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_생성_요청; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_생성_요청; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_시작일시; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_종료일시; +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; @@ -25,14 +28,16 @@ import com.allog.dallog.domain.category.application.CategoryService; import com.allog.dallog.domain.category.dto.response.CategoryResponse; import com.allog.dallog.domain.category.exception.NoSuchCategoryException; -import com.allog.dallog.domain.member.application.MemberService; -import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; import com.allog.dallog.domain.schedule.exception.InvalidScheduleException; import com.allog.dallog.domain.schedule.exception.NoSuchScheduleException; +import com.allog.dallog.domain.subscription.application.SubscriptionService; import java.time.LocalDateTime; +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -46,15 +51,15 @@ class ScheduleServiceTest extends ServiceTest { private CategoryService categoryService; @Autowired - private MemberService memberService; + private SubscriptionService subscriptionService; @DisplayName("새로운 일정을 생성한다.") @Test void 새로운_일정을_생성한다() { // given & when - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); // then assertThat(알록달록_회의.getTitle()).isEqualTo(알록달록_회의_제목); @@ -64,15 +69,15 @@ class ScheduleServiceTest extends ServiceTest { @Test void 새로운_일정을_생성_할_때_일정_제목의_길이가_50을_초과하는_경우_예외를_던진다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); String 잘못된_일정_제목 = "일이삼사오육칠팔구십일이삼사오육칠팔구십일일이삼사오육칠팔구십일이삼사오육칠팔구십일일이삼사오육칠팔구십일"; ScheduleCreateRequest 잘못된_일정_생성_요청 = new ScheduleCreateRequest(잘못된_일정_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 알록달록_회의_메모); // when & then - assertThatThrownBy(() -> scheduleService.save(후디.getId(), BE_일정.getId(), 잘못된_일정_생성_요청)). + assertThatThrownBy(() -> scheduleService.save(후디_id, BE_일정.getId(), 잘못된_일정_생성_요청)). isInstanceOf(InvalidScheduleException.class); } @@ -80,15 +85,15 @@ class ScheduleServiceTest extends ServiceTest { @Test void 새로운_일정을_생성_할_때_일정_메모의_길이가_255를_초과하는_경우_예외를_던진다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); String 잘못된_일정_메모 = "1".repeat(256); ScheduleCreateRequest 잘못된_일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 잘못된_일정_메모); // when & then - assertThatThrownBy(() -> scheduleService.save(후디.getId(), BE_일정.getId(), 잘못된_일정_생성_요청)). + assertThatThrownBy(() -> scheduleService.save(후디_id, BE_일정.getId(), 잘못된_일정_생성_요청)). isInstanceOf(InvalidScheduleException.class); } @@ -96,15 +101,15 @@ class ScheduleServiceTest extends ServiceTest { @Test void 새로운_일정을_생성_할_때_종료일시가_시작일시_이전이라면_예외를_던진다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); LocalDateTime 시작일시 = 날짜_2022년_7월_15일_16시_0분; LocalDateTime 종료일시 = 날짜_2022년_7월_1일_0시_0분; ScheduleCreateRequest 일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 시작일시, 종료일시, 알록달록_회의_메모); // when & then - assertThatThrownBy(() -> scheduleService.save(후디.getId(), BE_일정.getId(), 일정_생성_요청)). + assertThatThrownBy(() -> scheduleService.save(후디_id, BE_일정.getId(), 일정_생성_요청)). isInstanceOf(InvalidScheduleException.class); } @@ -112,16 +117,16 @@ class ScheduleServiceTest extends ServiceTest { @Test void 일정_생성_요청자가_카테고리의_생성자가_아닌경우_예외를_던진다() { // given - MemberResponse 리버 = memberService.save(리버()); - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); LocalDateTime 시작일시 = 날짜_2022년_7월_15일_16시_0분; LocalDateTime 종료일시 = 날짜_2022년_7월_31일_0시_0분; ScheduleCreateRequest 일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 시작일시, 종료일시, 알록달록_회의_메모); // when & then - assertThatThrownBy(() -> scheduleService.save(리버.getId(), BE_일정.getId(), 일정_생성_요청)). + assertThatThrownBy(() -> scheduleService.save(리버_id, BE_일정.getId(), 일정_생성_요청)). isInstanceOf(NoPermissionException.class); } @@ -129,14 +134,14 @@ class ScheduleServiceTest extends ServiceTest { @Test void 일정_생성시_전달한_카테고리가_존재하지_않는다면_예외를_던진다() { // given - MemberResponse 후디 = memberService.save(후디()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); LocalDateTime 시작일시 = 날짜_2022년_7월_15일_16시_0분; LocalDateTime 종료일시 = 날짜_2022년_7월_31일_0시_0분; ScheduleCreateRequest 일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 시작일시, 종료일시, 알록달록_회의_메모); // when & then - assertThatThrownBy(() -> scheduleService.save(후디.getId(), 0L, 일정_생성_요청)). + assertThatThrownBy(() -> scheduleService.save(후디_id, 0L, 일정_생성_요청)). isInstanceOf(NoSuchCategoryException.class); } @@ -144,15 +149,15 @@ class ScheduleServiceTest extends ServiceTest { @Test void 일정_생성시_전달한_카테고리가_외부_연동_카테고리라면_예외를_던진다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse 대한민국_공휴일 = categoryService.save(후디.getId(), 대한민국_공휴일_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse 대한민국_공휴일 = categoryService.save(후디_id, 대한민국_공휴일_생성_요청); LocalDateTime 시작일시 = 날짜_2022년_7월_15일_16시_0분; LocalDateTime 종료일시 = 날짜_2022년_7월_31일_0시_0분; ScheduleCreateRequest 일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 시작일시, 종료일시, 알록달록_회의_메모); // when & then - assertThatThrownBy(() -> scheduleService.save(후디.getId(), 대한민국_공휴일.getId(), 일정_생성_요청)). + assertThatThrownBy(() -> scheduleService.save(후디_id, 대한민국_공휴일.getId(), 일정_생성_요청)). isInstanceOf(NoPermissionException.class); } @@ -160,9 +165,9 @@ class ScheduleServiceTest extends ServiceTest { @Test void 일정의_ID로_단건_일정을_조회한다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); // when ScheduleResponse response = scheduleService.findById(알록달록_회의.getId()); @@ -181,27 +186,53 @@ class ScheduleServiceTest extends ServiceTest { @Test void 존재하지_않는_일정_ID로_단건_일정을_조회하면_예외를_던진다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); Long 잘못된_아이디 = 0L; // when & then assertThatThrownBy(() -> scheduleService.findById(잘못된_아이디)); } + @DisplayName("월별 일정 조회 시, 통합일정 정보를 반환한다.") + @Test + void 월별_일정_조회_시_통합일정_정보를_반환한다() { + // given + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(리버_id, BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(리버_id, BE_일정.getId(), 알록달록_회의_생성_요청); + ScheduleResponse 알록달록_회식 = scheduleService.save(리버_id, BE_일정.getId(), 알록달록_회식_생성_요청); + + // when + List schedules = scheduleService.findInternalByMemberIdAndDateRange(리버_id, + new DateRangeRequest("2022-07-01T00:00", "2022-08-15T23:59")); + + // then + assertThat(schedules).hasSize(2); + assertAll( + () -> { + assertThat(schedules.get(0).getId()).isEqualTo(String.valueOf(알록달록_회의.getId())); + assertThat(schedules.get(0).getCategoryType()).isEqualTo(NORMAL); + assertThat(schedules.get(1).getId()).isEqualTo(String.valueOf(알록달록_회식.getId())); + assertThat(schedules.get(1).getCategoryType()).isEqualTo(NORMAL); + } + ); + } + @DisplayName("일정을 수정한다.") @Test void 일정을_수정한다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - ScheduleResponse 기존_일정 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + ScheduleResponse 기존_일정 = scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); - ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(BE_일정.getId(), 레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, + 레벨_인터뷰_메모); // when - scheduleService.update(기존_일정.getId(), 후디.getId(), 일정_수정_요청); + scheduleService.update(기존_일정.getId(), 후디_id, 일정_수정_요청); // then ScheduleResponse actual = scheduleService.findById(기존_일정.getId()); @@ -220,15 +251,16 @@ class ScheduleServiceTest extends ServiceTest { @Test void 일정_수정_시_일정의_카테고리에_대한_권한이_없을_경우_예외가_발생한다() { // given - MemberResponse 리버 = memberService.save(리버()); - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - ScheduleResponse 기존_일정 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + ScheduleResponse 기존_일정 = scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); - ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(BE_일정.getId(), 레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, + 레벨_인터뷰_메모); // when & then - assertThatThrownBy(() -> scheduleService.update(기존_일정.getId(), 리버.getId(), 일정_수정_요청)) + assertThatThrownBy(() -> scheduleService.update(기존_일정.getId(), 리버_id, 일정_수정_요청)) .isInstanceOf(NoPermissionException.class); } @@ -236,27 +268,57 @@ class ScheduleServiceTest extends ServiceTest { @Test void 일정_수정_시_존재하지_않은_일정일_경우_예외가_발생한다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - ScheduleResponse 기존_일정 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + ScheduleResponse 기존_일정 = scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); - ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(BE_일정.getId(), 레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, + 레벨_인터뷰_메모); // when & then - assertThatThrownBy(() -> scheduleService.update(기존_일정.getId() + 1, 후디.getId(), 일정_수정_요청)) + assertThatThrownBy(() -> scheduleService.update(기존_일정.getId() + 1, 후디_id, 일정_수정_요청)) .isInstanceOf(NoSuchScheduleException.class); } + @DisplayName("일정의 카테고리도 수정한다.") + @Test + void 일정의_카테고리도_수정한다() { + // given + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + ScheduleResponse 기존_일정 = scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); + + CategoryResponse FE_일정 = categoryService.save(후디_id, FE_일정_생성_요청); + ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(FE_일정.getId(), 레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, + 레벨_인터뷰_메모); + + // when + scheduleService.update(기존_일정.getId(), 후디_id, 일정_수정_요청); + + // then + ScheduleResponse actual = scheduleService.findById(기존_일정.getId()); + assertAll( + () -> { + assertThat(actual.getId()).isEqualTo(기존_일정.getId()); + assertThat(actual.getCategoryType()).isEqualTo(FE_일정.getCategoryType()); + assertThat(actual.getTitle()).isEqualTo(레벨_인터뷰_제목); + assertThat(actual.getStartDateTime()).isEqualTo(레벨_인터뷰_시작일시); + assertThat(actual.getEndDateTime()).isEqualTo(레벨_인터뷰_종료일시); + assertThat(actual.getMemo()).isEqualTo(레벨_인터뷰_메모); + } + ); + } + @DisplayName("일정을 삭제한다.") @Test void 일정을_삭제한다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); // when - scheduleService.deleteById(알록달록_회의.getId(), 후디.getId()); + scheduleService.delete(알록달록_회의.getId(), 후디_id); // then assertThatThrownBy(() -> scheduleService.findById(알록달록_회의.getId())) @@ -267,13 +329,13 @@ class ScheduleServiceTest extends ServiceTest { @Test void 일정_삭제_시_일정의_카테고리에_대한_권한이_없을_경우_예외가_발생한다() { // given - MemberResponse 리버 = memberService.save(리버()); - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); // when & then - assertThatThrownBy(() -> scheduleService.deleteById(알록달록_회의.getId(), 리버.getId())) + assertThatThrownBy(() -> scheduleService.delete(알록달록_회의.getId(), 리버_id)) .isInstanceOf(NoPermissionException.class); } @@ -281,12 +343,12 @@ class ScheduleServiceTest extends ServiceTest { @Test void 일정_삭제_시_존재하지_않은_일정일_경우_예외가_발생한다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디_id, BE_일정.getId(), 알록달록_회의_생성_요청); // when & then - assertThatThrownBy(() -> scheduleService.deleteById(알록달록_회의.getId() + 1, 후디.getId())) + assertThatThrownBy(() -> scheduleService.delete(알록달록_회의.getId() + 1, 후디_id)) .isInstanceOf(NoSuchScheduleException.class); } } diff --git a/backend/src/test/java/com/allog/dallog/domain/schedule/application/SubscribingSchedulesFinderTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/application/SubscribingSchedulesFinderTest.java new file mode 100644 index 00000000..4ebd97da --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/application/SubscribingSchedulesFinderTest.java @@ -0,0 +1,114 @@ +package com.allog.dallog.domain.schedule.application; + +import static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_인증_코드_토큰_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_외부_일정_생성_요청; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_11일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_1분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_21일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_28일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_17시_0분; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetail; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetailRepository; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; +import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; +import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponse; +import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponses; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class SubscribingSchedulesFinderTest extends ServiceTest { + + @Autowired + private SubscribingSchedulesFinder subscribingSchedulesFinder; + + @Autowired + private CategoryService categoryService; + + @Autowired + private ExternalCategoryDetailRepository externalCategoryDetailRepository; + + @Autowired + private ScheduleService scheduleService; + + @Autowired + private CategoryRepository categoryRepository; + + @DisplayName("시작일시와 종료일시로 유저의 달력을 일정 유형에 따라 분류하고 정렬하여 반환한다.") + @Test + void 시작일시와_종료일시로_유저의_달력을_일정_유형에_따라_분류하고_정렬하여_반환한다() { + // given + Long memberId = parseMemberId(MEMBER_인증_코드_토큰_요청()); + + CategoryResponse BE_일정_응답 = categoryService.save(memberId, BE_일정_생성_요청); + Category BE_일정 = categoryRepository.getById(BE_일정_응답.getId()); + + /* 장기간 일정 */ + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("장기간 첫번째", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("장기간 두번째", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분, "")); + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("장기간 세번째", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("장기간 네번째", 날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_15일_16시_0분, "")); + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("장기간 다섯번째", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_17시_0분, "")); + + /* 종일 일정 */ + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("종일 첫번째", 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_11일_0시_0분, "")); + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("종일 두번째", 날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_21일_0시_0분, "")); + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("종일 세번째", 날짜_2022년_7월_27일_0시_0분, 날짜_2022년_7월_28일_0시_0분, "")); + + /* 몇시간 일정 */ + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("몇시간 첫번째", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_20시_0분, "")); + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("몇시간 두번째", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, "")); + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("몇시간 세번째", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + scheduleService.save(memberId, BE_일정.getId(), + new ScheduleCreateRequest("몇시간 네번째", 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_18시_0분, "")); + + CategoryResponse 우아한테크코스_외부_일정_응답 = categoryService.save(memberId, 우아한테크코스_외부_일정_생성_요청); + Category 우아한테크코스 = categoryRepository.getById(우아한테크코스_외부_일정_응답.getId()); + externalCategoryDetailRepository.save(new ExternalCategoryDetail(우아한테크코스, "dfggsdfasdasadsgs")); + + // when + MemberScheduleResponses memberScheduleResponses = subscribingSchedulesFinder.findMySubscribingSchedules( + memberId, new DateRangeRequest("2022-07-01T00:00", "2022-08-15T23:59")); + + // then + assertAll(() -> { + assertThat(memberScheduleResponses.getLongTerms()).extracting(MemberScheduleResponse::getTitle) + .contains("장기간 첫번째", "장기간 두번째", "장기간 세번째", "장기간 네번째", "장기간 다섯번째"); + assertThat(memberScheduleResponses.getAllDays()).extracting(MemberScheduleResponse::getTitle) + .contains("종일 첫번째", "종일 두번째", "종일 세번째"); + assertThat(memberScheduleResponses.getFewHours()).extracting(MemberScheduleResponse::getTitle) + .contains("몇시간 첫번째", "몇시간 두번째", "몇시간 세번째", "몇시간 네번째"); + }); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/IntegrationScheduleTest.java similarity index 55% rename from backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleTest.java rename to backend/src/test/java/com/allog/dallog/domain/schedule/domain/IntegrationScheduleTest.java index 213af9a4..23c37701 100644 --- a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/IntegrationScheduleTest.java @@ -1,13 +1,13 @@ -package com.allog.dallog.domain.integrationschedule.domain; +package com.allog.dallog.domain.schedule.domain; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_시작일시; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_종료일시; +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import com.allog.dallog.domain.category.domain.CategoryType; import java.time.LocalDateTime; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -24,18 +24,18 @@ class IntegrationScheduleTest { // when & then assertDoesNotThrow( () -> new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 알록달록_회의_메모, - CategoryType.NORMAL.name())); + NORMAL)); } - @DisplayName("LongTerm인지 확인 할 떄, 일정의 시작일시와 종료일시가 다르면 true를 반환한다.") + @DisplayName("LongTerm인지 확인 할 때, AllDays가 아니고 일정의 시작일과 종료일이 다르면 true를 반환한다.") @Test - void LongTerm인지_확인_할_떄_일정의_시작일시와_종료일시가_다르면_true를_반환한다() { + void LongTerm인지_확인_할_때_AllDays가_아니고_일정의_시작일과_종료일이_다르면_true를_반환한다() { // given String id = "1"; Long categoryId = 1L; IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, LocalDateTime.of(2022, 7, 1, 0, 1), - LocalDateTime.of(2022, 7, 2, 0, 0), 알록달록_회의_메모, CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 7, 2, 0, 0), 알록달록_회의_메모, NORMAL); // when boolean actual = integrationSchedule.isLongTerms(); @@ -44,15 +44,15 @@ class IntegrationScheduleTest { assertThat(actual).isTrue(); } - @DisplayName("LongTerm인지 확인 할 떄, 일정의 시작일시와 종료일시가 같으면 false를 반환한다.") + @DisplayName("LongTerm인지 확인 할 때, 일정의 시작일과 종료일이 같으면 false를 반환한다.") @Test - void LongTerm인지_확인_할_때_일정의_시작일시와_종료일시가_다르면_false를_반환한다() { + void LongTerm인지_확인_할_때_일정의_시작일과_종료일이_같으면_false를_반환한다() { // given String id = "1"; Long categoryId = 1L; IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, LocalDateTime.of(2022, 7, 1, 0, 1), - LocalDateTime.of(2022, 7, 1, 23, 59), 알록달록_회의_메모, CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 7, 1, 23, 59), 알록달록_회의_메모, NORMAL); // when boolean actual = integrationSchedule.isLongTerms(); @@ -61,15 +61,32 @@ class IntegrationScheduleTest { assertThat(actual).isFalse(); } - @DisplayName("AllDays인지 확인 할 떄, 일정의 시작일시와 종료일시가 같고 자정이면 true를 반환한다.") + @DisplayName("LongTerm인지 확인 할 때, 일정의 시작일과 종료일이 달라도 AllDays면 false를 반환한다.") @Test - void AllDays인지_확인_할_때_일정의_시작일시와_종료일시가_같고_자정이면_true를_반환한다() { + void LongTerm인지_확인_할_때_일정의_시작일과_종료일이_달라도_AllDays면_false를_반환한다() { // given String id = "1"; Long categoryId = 1L; IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, LocalDateTime.of(2022, 7, 1, 0, 0), - LocalDateTime.of(2022, 7, 1, 23, 59), 알록달록_회의_메모, CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 7, 2, 0, 0), 알록달록_회의_메모, NORMAL); + + // when + boolean actual = integrationSchedule.isLongTerms(); + + // then + assertThat(actual).isFalse(); + } + + @DisplayName("AllDays인지 확인 할 때, 일정의 일차가 하루고 시작시간과 종료시간 모두 자정이면 true를 반환한다.") + @Test + void AllDays인지_확인_할_때_일정의_일차가_하루고_시작시간과_종료시간이_모두_자정이면_true를_반환한다() { + // given + String id = "1"; + Long categoryId = 1L; + IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, + LocalDateTime.of(2022, 7, 1, 0, 0), + LocalDateTime.of(2022, 7, 2, 0, 0), 알록달록_회의_메모, NORMAL); // when boolean actual = integrationSchedule.isAllDays(); @@ -78,15 +95,15 @@ class IntegrationScheduleTest { assertThat(actual).isTrue(); } - @DisplayName("AllDays인지 확인 할 떄, 일정의 시작일시와 종료일시가 같지만 자정이 아니면 false를 반환한다.") + @DisplayName("AllDays인지 확인 할 때, 일정의 일차가 하루여도 시작시간과 종료시간이 자정이 아니면 false를 반환한다.") @Test - void AllDays인지_확인_할_때_일정의_시작일시와_종료일시가_같지만_자정이_아니면_false를_반환한다() { + void AllDays인지_확인_할_때_일정의_일차가_하루여도_시작시간과_종료시간이__자정이_아니면_false를_반환한다() { // given String id = "1"; Long categoryId = 1L; IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, LocalDateTime.of(2022, 7, 1, 0, 0), - LocalDateTime.of(2022, 7, 1, 11, 58), 알록달록_회의_메모, CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 7, 2, 0, 1), 알록달록_회의_메모, NORMAL); // when boolean actual = integrationSchedule.isAllDays(); @@ -95,15 +112,15 @@ class IntegrationScheduleTest { assertThat(actual).isFalse(); } - @DisplayName("FewHours인지 확인 할 떄, 일정의 시작일시와 종료일시가 같고 자정이 아니면 true를 반환한다.") + @DisplayName("FewHours인지 확인 할 때, 일정의 시작일과 종료일이 같으면 true를 반환한다.") @Test - void FewHours인지_확인_할_때_일정의_시작일시와_종료일시가_같고_자정이_아니면_true를_반환한다() { + void FewHours인지_확인_할_때_일정의_시작일과_종료일이_같으면_true를_반환한다() { // given String id = "1"; Long categoryId = 1L; IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, LocalDateTime.of(2022, 7, 1, 0, 0), - LocalDateTime.of(2022, 7, 1, 11, 58), 알록달록_회의_메모, CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 7, 1, 11, 59), 알록달록_회의_메모, NORMAL); // when boolean actual = integrationSchedule.isFewHours(); @@ -112,15 +129,15 @@ class IntegrationScheduleTest { assertThat(actual).isTrue(); } - @DisplayName("FewHours인지 확인 할 떄, 일정의 시작일시와 종료일시가 같지만 자정이면 false를 반환한다.") + @DisplayName("FewHours인지 확인 할 때, 일정의 시작일과 종료일이 다르면 false를 반환한다.") @Test - void FewHours인지_확인_할_때_일정의_시작일시와_종료일시가_같지만_자정이면_false를_반환한다() { + void FewHours인지_확인_할_때_일정의_시작일과_종료일이_다르면_false를_반환한다() { // given String id = "1"; Long categoryId = 1L; IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, LocalDateTime.of(2022, 7, 1, 0, 0), - LocalDateTime.of(2022, 7, 1, 23, 59), 알록달록_회의_메모, CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 7, 2, 0, 0), 알록달록_회의_메모, NORMAL); // when boolean actual = integrationSchedule.isFewHours(); diff --git a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedulesTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/IntegrationSchedulesTest.java similarity index 86% rename from backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedulesTest.java rename to backend/src/test/java/com/allog/dallog/domain/schedule/domain/IntegrationSchedulesTest.java index 6e0a05f1..506f41b9 100644 --- a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedulesTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/IntegrationSchedulesTest.java @@ -1,8 +1,8 @@ -package com.allog.dallog.domain.integrationschedule.domain; +package com.allog.dallog.domain.schedule.domain; +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; import static org.assertj.core.api.Assertions.assertThat; -import com.allog.dallog.domain.category.domain.CategoryType; import java.time.LocalDateTime; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -16,15 +16,15 @@ class IntegrationSchedulesTest { Long categoryId = 1L; IntegrationSchedule 첫번째로_정렬되어야_하는_일정 = new IntegrationSchedule("1", categoryId, "일정1", LocalDateTime.of(2022, 3, 1, 0, 0), - LocalDateTime.of(2022, 3, 2, 0, 0), "일정1", CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 3, 2, 0, 0), "일정1", NORMAL); IntegrationSchedule 두번째로_정렬되어야_하는_일정 = new IntegrationSchedule("2", categoryId, "일정2", LocalDateTime.of(2022, 3, 3, 0, 0), - LocalDateTime.of(2022, 3, 4, 0, 0), "일정2", CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 3, 4, 0, 0), "일정2", NORMAL); IntegrationSchedule 세번째로_정렬되어야_하는_일정 = new IntegrationSchedule("3", categoryId, "일정3", LocalDateTime.of(2022, 3, 5, 0, 0), - LocalDateTime.of(2022, 3, 7, 0, 0), "일정3", CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 3, 7, 0, 0), "일정3", NORMAL); // when IntegrationSchedules integrationSchedules = new IntegrationSchedules(); @@ -45,15 +45,15 @@ class IntegrationSchedulesTest { Long categoryId = 1L; IntegrationSchedule 첫번째로_정렬되어야_하는_일정 = new IntegrationSchedule("1", categoryId, "일정1", LocalDateTime.of(2022, 3, 1, 0, 0), - LocalDateTime.of(2022, 3, 10, 0, 0), "일정1", CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 3, 10, 0, 0), "일정1", NORMAL); IntegrationSchedule 두번째로_정렬되어야_하는_일정 = new IntegrationSchedule("2", categoryId, "일정2", LocalDateTime.of(2022, 3, 1, 0, 0), - LocalDateTime.of(2022, 3, 7, 0, 0), "일정2", CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 3, 7, 0, 0), "일정2", NORMAL); IntegrationSchedule 세번째로_정렬되어야_하는_일정 = new IntegrationSchedule("3", categoryId, "일정3", LocalDateTime.of(2022, 3, 5, 0, 0), - LocalDateTime.of(2022, 3, 5, 0, 0), "일정3", CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 3, 5, 0, 0), "일정3", NORMAL); // when IntegrationSchedules integrationSchedules = new IntegrationSchedules(); @@ -74,15 +74,15 @@ class IntegrationSchedulesTest { Long categoryId = 1L; IntegrationSchedule 첫번째로_정렬되어야_하는_일정 = new IntegrationSchedule("1", categoryId, "가", LocalDateTime.of(2022, 3, 1, 0, 0), - LocalDateTime.of(2022, 3, 10, 0, 0), "일정1", CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 3, 10, 0, 0), "일정1", NORMAL); IntegrationSchedule 두번째로_정렬되어야_하는_일정 = new IntegrationSchedule("2", categoryId, "나", LocalDateTime.of(2022, 3, 1, 0, 0), - LocalDateTime.of(2022, 3, 10, 0, 0), "일정2", CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 3, 10, 0, 0), "일정2", NORMAL); IntegrationSchedule 세번째로_정렬되어야_하는_일정 = new IntegrationSchedule("3", categoryId, "다", LocalDateTime.of(2022, 3, 1, 0, 0), - LocalDateTime.of(2022, 3, 10, 0, 0), "일정3", CategoryType.NORMAL.name()); + LocalDateTime.of(2022, 3, 10, 0, 0), "일정3", NORMAL); // when IntegrationSchedules integrationSchedules = new IntegrationSchedules(); diff --git a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/PeriodTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/PeriodTest.java similarity index 89% rename from backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/PeriodTest.java rename to backend/src/test/java/com/allog/dallog/domain/schedule/domain/PeriodTest.java index 42fe1469..565d86ed 100644 --- a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/PeriodTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/PeriodTest.java @@ -1,4 +1,4 @@ -package com.allog.dallog.domain.integrationschedule.domain; +package com.allog.dallog.domain.schedule.domain; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; @@ -26,36 +26,36 @@ class PeriodTest { assertThat(dayDifference).isEqualTo(2); } - @DisplayName("시작일시와 종료일시의 시간 차이를 반환한다.") + @DisplayName("시작시간과 종료시간이 모두 자정이면 true를 반환한다.") @Test - void 시작일시와_종료일시의_시간_차이를_반환한다() { + void 시작시간과_종료시간이_모두_자정이면_true를_반환한다() { // given - LocalDateTime startDateTime = LocalDateTime.of(2022, 1, 2, 11, 0); - LocalDateTime endDateTime = LocalDateTime.of(2022, 1, 4, 23, 0); + LocalDateTime startDateTime = LocalDateTime.of(2022, 1, 2, 0, 0); + LocalDateTime endDateTime = LocalDateTime.of(2022, 1, 4, 0, 0); Period period = new Period(startDateTime, endDateTime); // when - long hourDifference = period.calculateHourDifference(); + boolean actual = period.isMidnightToMidnight(); // then - assertThat(hourDifference).isEqualTo(12); + assertThat(actual).isTrue(); } - @DisplayName("시작일시와 종료일시의 분 차이를 반환한다.") + @DisplayName("시작시간과 종료시간 중 하나라도 자정이 아니면 false를 반환한다.") @Test - void 시작일시와_종료일시의_분_차이를_반환한다() { + void 시작시간과_종료시간_중_하나라도_자정이_아니면_false를_반환한다() { // given - LocalDateTime startDateTime = LocalDateTime.of(2022, 1, 2, 11, 17); - LocalDateTime endDateTime = LocalDateTime.of(2022, 1, 4, 11, 19); + LocalDateTime startDateTime = LocalDateTime.of(2022, 1, 2, 10, 10); + LocalDateTime endDateTime = LocalDateTime.of(2022, 1, 4, 0, 0); Period period = new Period(startDateTime, endDateTime); // when - long minuteDifference = period.calculateMinuteDifference(); + boolean actual = period.isMidnightToMidnight(); // then - assertThat(minuteDifference).isEqualTo(2); + assertThat(actual).isFalse(); } @DisplayName("기간 뺄셈시 상대 기간이 우측에 걸쳐있을 때의 결과를 계산한다.") @@ -145,9 +145,7 @@ class PeriodTest { List actual = basePeriod.slice(otherPeriod); // then - assertAll(() -> { - assertThat(actual).hasSize(0); - }); + assertThat(actual).hasSize(0); } @DisplayName("기간 뺄셈시 상대 기간과 겹치지 않으면 자기자신을 리스트로 반환한다.") diff --git a/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleRepositoryTest.java index 3ac9064a..ad387d14 100644 --- a/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleRepositoryTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleRepositoryTest.java @@ -3,11 +3,31 @@ import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정; import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라; import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_11시_59분; import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_1분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_17일_23시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_17시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_23시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.매고라_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.매고라_제목; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_메모; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_제목; import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; @@ -19,6 +39,8 @@ import com.allog.dallog.domain.category.domain.CategoryRepository; import com.allog.dallog.domain.member.domain.Member; import com.allog.dallog.domain.member.domain.MemberRepository; +import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -35,7 +57,7 @@ class ScheduleRepositoryTest extends RepositoryTest { @Autowired private MemberRepository memberRepository; - @DisplayName("특정 카테고리들에 속한 일정을 전부 삭제한다") + @DisplayName("특정 카테고리들에 속한 일정을 전부 삭제한다.") @Test void 특정_카테고리들에_속한_일정을_전부_삭제한다() { // given @@ -75,4 +97,289 @@ class ScheduleRepositoryTest extends RepositoryTest { // then assertThat(scheduleRepository.findAll()).hasSize(0); } + + @DisplayName("카테코리와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.") + @Test + void 카테고리와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(List.of(BE_일정), + 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분); + + // then + assertThat(actual).hasSize(1); + } + + @DisplayName("조회하기 위한 category 리스트의 크기가 0인 경우 빈 리스트를 반환한다.") + @Test + void 조회하기_위한_category_리스트의_크기가_0인_경우_빈_리스트를_반환한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categories = Collections.emptyList(); + LocalDateTime startDate = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDate = 날짜_2022년_7월_31일_0시_0분; + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(categories, startDate, endDate); + + // then + assertThat(actual).hasSize(0); + } + + @DisplayName("카테고리가 여러 개 일 때, 카테고리와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.") + @Test + void 카테고리가_여러_개_일_때_카테고리와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + Category FE_일정 = categoryRepository.save(FE_일정(관리자)); + Category 매트_아고라 = categoryRepository.save(매트_아고라(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + Schedule 레벨_인터뷰 = new Schedule(FE_일정, 레벨_인터뷰_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 레벨_인터뷰_메모); + + Schedule 매고라 = new Schedule(매트_아고라, 매고라_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 매고라_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + scheduleRepository.save(레벨_인터뷰); + scheduleRepository.save(매고라); + + List categories = List.of(BE_일정); + LocalDateTime startDate = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDate = 날짜_2022년_7월_31일_0시_0분; + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(categories, startDate, + endDate); + + // then + assertThat(actual).hasSize(1); + } + + @DisplayName("카테고리가 여러 개 일 때, 카테고리 리스트와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.") + @Test + void 카테고리가_여러_개_일_때_카테고리_리스트와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + Category FE_일정 = categoryRepository.save(FE_일정(관리자)); + Category 매트_아고라 = categoryRepository.save(매트_아고라(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + Schedule 레벨_인터뷰 = new Schedule(FE_일정, 레벨_인터뷰_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 레벨_인터뷰_메모); + + Schedule 매고라 = new Schedule(매트_아고라, 매고라_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 매고라_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + scheduleRepository.save(레벨_인터뷰); + scheduleRepository.save(매고라); + + List categories = List.of(BE_일정, FE_일정, 매트_아고라); + LocalDateTime startDate = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDate = 날짜_2022년_7월_31일_0시_0분; + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(categories, startDate, endDate); + + // then + assertThat(actual).hasSize(3); + } + + @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 시작날짜가 종료일시와 같으면 조회한다.") + @Test + void 시작일시와_종료일시를_전달할_때_일정의_시작일시와_같으면_조회된다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categories = List.of(BE_일정); + LocalDateTime startDate = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDate = 날짜_2022년_7월_15일_16시_0분; + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(categories, startDate, endDate); + + // then + assertThat(actual).hasSize(1); + } + + @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 시작날짜가 종료일시 이후이면 조회되지 않는다.") + @Test + void 카테고리와_시작일시_종료일시를_전달할_때_일정의_시작날짜가_종료일시_이후이면_조회되지_않는다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categories = List.of(BE_일정); + LocalDateTime startDate = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDate = 날짜_2022년_7월_7일_16시_0분; + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(categories, startDate, endDate); + + // then + assertThat(actual).hasSize(0); + } + + @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 종료날짜가 시작일시와 같으면 조회된다.") + @Test + void 카테고리와_시작일시와_종료일시를_전달할_때_일정의_종료날짜가_시작일시와_같으면_조회된다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categories = List.of(BE_일정); + LocalDateTime startDate = 날짜_2022년_7월_16일_16시_0분; + LocalDateTime endDate = 날짜_2022년_7월_31일_0시_0분; + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(categories, startDate, endDate); + + // then + assertThat(actual).hasSize(1); + } + + @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 종료날짜가 시작일시 이전이면 조회되지 않는다.") + @Test + void 카테고리와_시작일시와_종료일시를_전달할_때_일정의_종료날짜가_시작일시_이전이면_조회되지_않는다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categories = List.of(BE_일정); + LocalDateTime startDate = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDate = 날짜_2022년_7월_7일_16시_0분; + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(categories, startDate, endDate); + + // then + assertThat(actual).hasSize(0); + } + + @DisplayName("시작일시와 종료일시로 특정 카테고리의 일정을 조회한다.") + @Test + void 시작일시와_종료일시로_특정_카테고리의_일정을_조회한다() { + // given + Member 후디 = memberRepository.save(후디()); + + Category BE_일정 = categoryRepository.save(BE_일정(후디)); + Category FE_일정 = categoryRepository.save(FE_일정(후디)); + Category 공통_일정 = categoryRepository.save(공통_일정(후디)); + + /* BE 일정 */ + scheduleRepository.save(new Schedule(BE_일정, "BE 1", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 2", 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_10일_11시_59분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_20시_0분, "")); + + /* FE 일정 */ + scheduleRepository.save(new Schedule(FE_일정, "FE 1", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분, "")); + scheduleRepository.save(new Schedule(FE_일정, "FE 2", 날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_20일_11시_59분, "")); + scheduleRepository.save(new Schedule(FE_일정, "FE 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, "")); + + /* 공통 일정 */ + scheduleRepository.save(new Schedule(공통_일정, "공통 1", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + scheduleRepository.save(new Schedule(공통_일정, "공통 2", 날짜_2022년_7월_27일_0시_0분, 날짜_2022년_7월_27일_11시_59분, "")); + scheduleRepository.save(new Schedule(공통_일정, "공통 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + + List categories = List.of(BE_일정, FE_일정); + LocalDateTime startDate = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDate = 날짜_2022년_8월_15일_23시_59분; + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(categories, startDate, endDate); + + // then + assertThat(actual) + .extracting(IntegrationSchedule::getTitle) + .containsOnly("BE 1", "BE 2", "BE 3", "FE 1", "FE 2", "FE 3"); + } + + @DisplayName("시작일시와 종료일시로 특정 카테고리의 일정을 조회할 때 범위 밖의 일정은 제외된다.") + @Test + void 시작일시와_종료일시로_특정_카테고리의_일정을_조회할_때_범위_밖의_일정은_제외된다() { + // given + Member 후디 = memberRepository.save(후디()); + + Category BE_일정 = categoryRepository.save(BE_일정(후디)); + Category FE_일정 = categoryRepository.save(FE_일정(후디)); + + /* BE 일정 */ + scheduleRepository.save(new Schedule(BE_일정, "BE 1 포함", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 2 포함", 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_10일_11시_59분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 3 포함", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_20시_0분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 3 미포함", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_17시_0분, "")); + + /* FE 일정 */ + scheduleRepository.save(new Schedule(FE_일정, "FE 1 포함", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분, "")); + scheduleRepository.save(new Schedule(FE_일정, "FE 2 포함", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, "")); + scheduleRepository.save(new Schedule(FE_일정, "FE 3 미포함", 날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_20일_11시_59분, "")); + + List categories = List.of(BE_일정, FE_일정); + LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDateTime = 날짜_2022년_7월_17일_23시_59분; + + // when + List actual = scheduleRepository.getByCategoriesAndBetween(categories, + startDateTime, endDateTime); + + // then + assertThat(actual).extracting(IntegrationSchedule::getTitle) + .containsOnly("BE 1 포함", "BE 2 포함", "BE 3 포함", "FE 1 포함", "FE 2 포함"); + } } diff --git a/backend/src/test/java/com/allog/dallog/domain/schedule/domain/scheduler/SchedulerTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/scheduler/SchedulerTest.java index d25c0648..8ad61de1 100644 --- a/backend/src/test/java/com/allog/dallog/domain/schedule/domain/scheduler/SchedulerTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/scheduler/SchedulerTest.java @@ -17,12 +17,13 @@ import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import com.allog.dallog.domain.category.domain.Category; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; -import com.allog.dallog.domain.integrationschedule.domain.Period; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.schedule.domain.Period; import java.time.LocalDateTime; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -40,21 +41,21 @@ class SchedulerTest { String 일정_메모 = "일정 메모"; IntegrationSchedule 일정1 = new IntegrationSchedule("1", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_7일_16시_0분, - 날짜_2022년_7월_10일_0시_0분, 일정_메모, "NORMAL"); + 날짜_2022년_7월_10일_0시_0분, 일정_메모, NORMAL); IntegrationSchedule 일정2 = new IntegrationSchedule("2", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_10일_11시_59분, - 날짜_2022년_7월_15일_16시_0분, 일정_메모, "NORMAL"); + 날짜_2022년_7월_15일_16시_0분, 일정_메모, NORMAL); IntegrationSchedule 일정3 = new IntegrationSchedule("3", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_16일_16시_0분, - 날짜_2022년_7월_16일_16시_1분, 일정_메모, "NORMAL"); + 날짜_2022년_7월_16일_16시_1분, 일정_메모, NORMAL); IntegrationSchedule 일정4 = new IntegrationSchedule("4", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_16일_18시_0분, - 날짜_2022년_7월_16일_20시_0분, 일정_메모, "NORMAL"); + 날짜_2022년_7월_16일_20시_0분, 일정_메모, NORMAL); IntegrationSchedule 일정5 = new IntegrationSchedule("5", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_16일_20시_0분, - 날짜_2022년_7월_20일_0시_0분, 일정_메모, "NORMAL"); + 날짜_2022년_7월_20일_0시_0분, 일정_메모, NORMAL); IntegrationSchedule 일정6 = new IntegrationSchedule("6", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_20일_11시_59분, - 날짜_2022년_7월_27일_0시_0분, 일정_메모, "NORMAL"); + 날짜_2022년_7월_27일_0시_0분, 일정_메모, NORMAL); IntegrationSchedule 일정7 = new IntegrationSchedule("7", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_27일_11시_59분, - 날짜_2022년_7월_31일_0시_0분, 일정_메모, "NORMAL"); + 날짜_2022년_7월_31일_0시_0분, 일정_메모, NORMAL); IntegrationSchedule 일정8 = new IntegrationSchedule("8", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_31일_0시_0분, - 날짜_2022년_8월_15일_14시_0분, 일정_메모, "NORMAL"); + 날짜_2022년_8월_15일_14시_0분, 일정_메모, NORMAL); List 일정_목록 = List.of(일정1, 일정2, 일정3, 일정4, 일정5, 일정6, 일정7, 일정8); diff --git a/backend/src/test/java/com/allog/dallog/domain/subscription/application/SubscriptionServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/subscription/application/SubscriptionServiceTest.java index d0d815fc..e9bbd421 100644 --- a/backend/src/test/java/com/allog/dallog/domain/subscription/application/SubscriptionServiceTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/subscription/application/SubscriptionServiceTest.java @@ -1,15 +1,18 @@ package com.allog.dallog.domain.subscription.application; - +import static com.allog.dallog.common.fixtures.AuthFixtures.관리자_인증_코드_토큰_요청; +import static com.allog.dallog.common.fixtures.AuthFixtures.리버_인증_코드_토큰_요청; +import static com.allog.dallog.common.fixtures.AuthFixtures.매트_인증_코드_토큰_요청; +import static com.allog.dallog.common.fixtures.AuthFixtures.파랑_인증_코드_토큰_요청; +import static com.allog.dallog.common.fixtures.AuthFixtures.후디_인증_코드_토큰_요청; import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_이름; import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; import static com.allog.dallog.common.fixtures.CategoryFixtures.내_일정_생성_요청; import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; -import static com.allog.dallog.common.fixtures.MemberFixtures.매트; -import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; -import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상1_구독; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; @@ -17,16 +20,21 @@ import com.allog.dallog.common.annotation.ServiceTest; import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; import com.allog.dallog.domain.category.dto.response.CategoryResponse; -import com.allog.dallog.domain.member.application.MemberService; -import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.domain.Subscription; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; import com.allog.dallog.domain.subscription.dto.request.SubscriptionUpdateRequest; import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; import com.allog.dallog.domain.subscription.dto.response.SubscriptionsResponse; import com.allog.dallog.domain.subscription.exception.ExistSubscriptionException; import com.allog.dallog.domain.subscription.exception.InvalidSubscriptionException; import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -35,24 +43,32 @@ class SubscriptionServiceTest extends ServiceTest { - @Autowired - private MemberService memberService; - @Autowired private CategoryService categoryService; @Autowired private SubscriptionService subscriptionService; + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private SubscriptionRepository subscriptionRepository; + + @Autowired + private MemberRepository memberRepository; + @DisplayName("새로운 구독을 생성한다.") @Test void 새로운_구독을_생성한다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); // when - SubscriptionResponse response = subscriptionService.save(후디.getId(), BE_일정.getId()); + SubscriptionResponse response = subscriptionService.save(리버_id, BE_일정.getId()); // then assertThat(response.getCategory().getName()).isEqualTo(BE_일정_이름); @@ -62,13 +78,13 @@ class SubscriptionServiceTest extends ServiceTest { @Test void 자신이_생성하지_않은_개인_카테고리를_구독시_예외가_발생한다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse 후디_개인_학습_일정 = categoryService.save(후디.getId(), 내_일정_생성_요청); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse 후디_개인_학습_일정 = categoryService.save(후디_id, 내_일정_생성_요청); - MemberResponse 매트 = memberService.save(매트()); + Long 매트_id = parseMemberId(매트_인증_코드_토큰_요청()); // when & then - assertThatThrownBy(() -> subscriptionService.save(매트.getId(), 후디_개인_학습_일정.getId())) + assertThatThrownBy(() -> subscriptionService.save(매트_id, 후디_개인_학습_일정.getId())) .isInstanceOf(NoPermissionException.class) .hasMessage("구독 권한이 없는 카테고리입니다."); } @@ -77,12 +93,11 @@ class SubscriptionServiceTest extends ServiceTest { @Test void 이미_존재하는_구독_정보를_저장할_경우_예외를_던진다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - subscriptionService.save(후디.getId(), BE_일정.getId()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); // when & then - assertThatThrownBy(() -> subscriptionService.save(후디.getId(), BE_일정.getId())) + assertThatThrownBy(() -> subscriptionService.save(후디_id, BE_일정.getId())) .isInstanceOf(ExistSubscriptionException.class); } @@ -90,9 +105,11 @@ class SubscriptionServiceTest extends ServiceTest { @Test void 구독_id를_기반으로_단건_조회한다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - SubscriptionResponse 빨간색_구독 = subscriptionService.save(후디.getId(), BE_일정.getId()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + SubscriptionResponse 빨간색_구독 = subscriptionService.save(리버_id, BE_일정.getId()); // when SubscriptionResponse foundResponse = subscriptionService.findById(빨간색_구독.getId()); @@ -116,35 +133,61 @@ class SubscriptionServiceTest extends ServiceTest { @Test void 회원_정보를_기반으로_구독_정보를_조회한다() { // given - MemberResponse 관리자 = memberService.save(관리자()); - CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); - CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); - CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); + Long 관리자_id = parseMemberId(관리자_인증_코드_토큰_요청()); + Long 매트_id = parseMemberId(매트_인증_코드_토큰_요청()); + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + CategoryResponse 공통_일정 = categoryService.save(관리자_id, 공통_일정_생성_요청); + CategoryResponse BE_일정 = categoryService.save(매트_id, BE_일정_생성_요청); + CategoryResponse FE_일정 = categoryService.save(리버_id, FE_일정_생성_요청); + + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + subscriptionService.save(후디_id, 공통_일정.getId()); + subscriptionService.save(후디_id, BE_일정.getId()); + subscriptionService.save(후디_id, FE_일정.getId()); - MemberResponse 후디 = memberService.save(후디()); - subscriptionService.save(후디.getId(), 공통_일정.getId()); - subscriptionService.save(후디.getId(), BE_일정.getId()); - subscriptionService.save(후디.getId(), FE_일정.getId()); + // when + SubscriptionsResponse subscriptionsResponse = subscriptionService.findByMemberId(후디_id); + + // then + // TODO: 개인 일정 구독 정보를 포함하여 3 + 1 = 4개, N + 1 문제 개선 예정 + assertThat(subscriptionsResponse.getSubscriptions()).hasSize(4); + } + + @DisplayName("category id를 기반으로 구독 정보를 조회한다.") + @Test + void category_id를_기반으로_구독_정보를_조회한다() { + // given + Long 매트_id = parseMemberId(매트_인증_코드_토큰_요청()); + Long 파랑_id = parseMemberId(파랑_인증_코드_토큰_요청()); + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + + CategoryResponse BE_일정 = categoryService.save(매트_id, BE_일정_생성_요청); + subscriptionService.save(파랑_id, BE_일정.getId()); + subscriptionService.save(리버_id, BE_일정.getId()); + subscriptionService.save(후디_id, BE_일정.getId()); // when - SubscriptionsResponse subscriptionsResponse = subscriptionService.findByMemberId(후디.getId()); + List actual = subscriptionService.findByCategoryId(BE_일정.getId()); // then - assertThat(subscriptionsResponse.getSubscriptions()).hasSize(3); + assertThat(actual).hasSize(4); } @DisplayName("구독 정보를 수정한다.") @Test void 구독_정보를_수정한다() { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - SubscriptionResponse response = subscriptionService.save(후디.getId(), BE_일정.getId()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + SubscriptionResponse response = subscriptionService.save(리버_id, BE_일정.getId()); Color color = Color.COLOR_1; // when SubscriptionUpdateRequest request = new SubscriptionUpdateRequest(color, true); - subscriptionService.update(response.getId(), 후디.getId(), request); + subscriptionService.update(response.getId(), 리버_id, request); // then assertAll(() -> { @@ -158,15 +201,17 @@ class SubscriptionServiceTest extends ServiceTest { @ValueSource(strings = {"#111", "#1111", "#11111", "123456", "#**1234", "##12345", "334172#", "#00FF00"}) void 구독_정보_수정_시_존재하지_않는_색상인_경우_예외를_던진다(final String colorCode) { // given - MemberResponse 후디 = memberService.save(후디()); - CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); - SubscriptionResponse response = subscriptionService.save(후디.getId(), BE_일정.getId()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + CategoryResponse BE_일정 = categoryService.save(후디_id, BE_일정_생성_요청); + + Long 리버_id = parseMemberId(리버_인증_코드_토큰_요청()); + SubscriptionResponse response = subscriptionService.save(리버_id, BE_일정.getId()); // when SubscriptionUpdateRequest request = new SubscriptionUpdateRequest(colorCode, true); // then - assertThatThrownBy(() -> subscriptionService.update(response.getId(), 후디.getId(), request)) + assertThatThrownBy(() -> subscriptionService.update(response.getId(), 리버_id, request)) .isInstanceOf(InvalidSubscriptionException.class); } @@ -174,35 +219,35 @@ class SubscriptionServiceTest extends ServiceTest { @Test void 구독_정보를_삭제한다() { // given - MemberResponse 관리자 = memberService.save(관리자()); - CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); - CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); - CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); + Long 관리자_id = parseMemberId(관리자_인증_코드_토큰_요청()); + CategoryResponse 공통_일정 = categoryService.save(관리자_id, 공통_일정_생성_요청); + CategoryResponse BE_일정 = categoryService.save(관리자_id, BE_일정_생성_요청); + CategoryResponse FE_일정 = categoryService.save(관리자_id, FE_일정_생성_요청); - MemberResponse 후디 = memberService.save(후디()); - SubscriptionResponse response = subscriptionService.save(후디.getId(), 공통_일정.getId()); - subscriptionService.save(후디.getId(), BE_일정.getId()); - subscriptionService.save(후디.getId(), FE_일정.getId()); + Long 후디_id = parseMemberId(후디_인증_코드_토큰_요청()); + SubscriptionResponse response = subscriptionService.save(후디_id, 공통_일정.getId()); + subscriptionService.save(후디_id, BE_일정.getId()); + subscriptionService.save(후디_id, FE_일정.getId()); // when - subscriptionService.deleteById(response.getId(), 후디.getId()); + subscriptionService.delete(response.getId(), 후디_id); // then - assertThat(subscriptionService.findByMemberId(후디.getId()).getSubscriptions()).hasSize(2); + assertThat(subscriptionService.findByMemberId(후디_id).getSubscriptions()).hasSize(3); } @DisplayName("자신의 구독 정보가 아닌 구독을 삭제할 경우 예외를 던진다.") @Test void 자신의_구독_정보가_아닌_구독을_삭제할_경우_예외를_던진다() { // given - MemberResponse 관리자 = memberService.save(관리자()); - MemberResponse 파랑 = memberService.save(파랑()); + Long 관리자_id = parseMemberId(관리자_인증_코드_토큰_요청()); + Long 파랑_id = parseMemberId(파랑_인증_코드_토큰_요청()); - CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); - SubscriptionResponse 공통_일정_구독 = subscriptionService.save(파랑.getId(), 공통_일정.getId()); + CategoryResponse 공통_일정 = categoryService.save(관리자_id, 공통_일정_생성_요청); + SubscriptionResponse 공통_일정_구독 = subscriptionService.save(파랑_id, 공통_일정.getId()); // when & then - assertThatThrownBy(() -> subscriptionService.deleteById(공통_일정_구독.getId(), 관리자.getId())) + assertThatThrownBy(() -> subscriptionService.delete(공통_일정_구독.getId(), 관리자_id)) .isInstanceOf(NoPermissionException.class); } @@ -210,13 +255,12 @@ class SubscriptionServiceTest extends ServiceTest { @Test void 자신이_만든_카테고리에_대한_구독을_삭제할_경우_예외를_던진다() { // given - MemberResponse 관리자 = memberService.save(관리자()); - - CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); - SubscriptionResponse 공통_일정_구독 = subscriptionService.save(관리자.getId(), 공통_일정.getId()); + Member 관리자 = memberRepository.save(관리자()); + Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + Subscription 공통_일정_구독 = subscriptionRepository.save(색상1_구독(관리자, 공통_일정)); // when & then - assertThatThrownBy(() -> subscriptionService.deleteById(공통_일정_구독.getId(), 관리자.getId())) + assertThatThrownBy(() -> subscriptionService.delete(공통_일정_구독.getId(), 관리자.getId())) .isInstanceOf(NoPermissionException.class); } } diff --git a/backend/src/test/java/com/allog/dallog/domain/subscription/domain/ColorTest.java b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/ColorTest.java index cf6aeae8..fd05a69f 100644 --- a/backend/src/test/java/com/allog/dallog/domain/subscription/domain/ColorTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/ColorTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.allog.dallog.domain.subscription.application.ColorPicker; import com.allog.dallog.domain.subscription.exception.InvalidSubscriptionException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -17,10 +18,14 @@ class ColorTest { @Test void 랜덤으로_색상을_가져온다() { // given - ColorPickerStrategy testStrategy = () -> 0; + ColorPicker colorPicker = () -> 0; + int randomIndex = colorPicker.pickNumber(); - // when & then - assertThat(Color.pickAny(testStrategy)).isEqualTo(Color.COLOR_1); + // when + Color actual = Color.pick(randomIndex); + + // then + assertThat(actual).isEqualTo(Color.COLOR_1); } @DisplayName("color code에 맞는 색상을 가져온다.") diff --git a/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepositoryTest.java index 2061e292..d7b93054 100644 --- a/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepositoryTest.java +++ b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepositoryTest.java @@ -4,6 +4,7 @@ import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정; import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.리버; import static com.allog.dallog.common.fixtures.MemberFixtures.매트; import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; import static com.allog.dallog.common.fixtures.MemberFixtures.후디; @@ -11,13 +12,19 @@ import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상2_구독; import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상3_구독; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.allog.dallog.common.annotation.RepositoryTest; +import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.category.domain.Category; import com.allog.dallog.domain.category.domain.CategoryRepository; import com.allog.dallog.domain.member.domain.Member; import com.allog.dallog.domain.member.domain.MemberRepository; +import com.allog.dallog.domain.subscription.exception.ExistSubscriptionException; +import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -66,6 +73,37 @@ class SubscriptionRepositoryTest extends RepositoryTest { assertThat(actual).isTrue(); } + @DisplayName("회원의 특정 구독 정보 여부를 확인한다.") + @Test + void 회원의_특정_구독_정보_여부를_확인한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + + Member 후디 = memberRepository.save(후디()); + Subscription 색상1_구독 = 색상1_구독(후디, 공통_일정); + subscriptionRepository.save(색상1_구독); + + // when + boolean actual = subscriptionRepository.existsByIdAndMemberId(색상1_구독.getId(), 후디.getId()); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("회원의 존재하지 않는 구독 정보 여부를 확인한다.") + @Test + void 회원의_존재하지_않는_구독_정보_여부를_확인한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + // when + boolean actual = subscriptionRepository.existsByIdAndMemberId(0L, 관리자.getId()); + + // then + assertThat(actual).isFalse(); + } + @DisplayName("회원 정보를 기반으로 구독 정보를 조회한다.") @Test void 회원_정보를_기반으로_구독_정보를_조회한다() { @@ -100,35 +138,45 @@ class SubscriptionRepositoryTest extends RepositoryTest { assertThat(subscriptions).isEmpty(); } - @DisplayName("회원의 특정 구독 정보 여부를 확인한다.") + @DisplayName("member id 리스트를 기반으로 구독 정보를 조회한다.") @Test - void 회원의_특정_구독_정보_여부를_확인한다() { + void member_id_리스트를_기반으로_구독_정보를_조회한다() { // given Member 관리자 = memberRepository.save(관리자()); Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + Member 매트 = memberRepository.save(매트()); + Member 리버 = memberRepository.save(리버()); + Member 파랑 = memberRepository.save(파랑()); Member 후디 = memberRepository.save(후디()); - Subscription 색상1_구독 = 색상1_구독(후디, 공통_일정); - subscriptionRepository.save(색상1_구독); + + subscriptionRepository.save(색상1_구독(매트, 공통_일정)); + subscriptionRepository.save(색상1_구독(리버, 공통_일정)); + subscriptionRepository.save(색상1_구독(파랑, 공통_일정)); + subscriptionRepository.save(색상1_구독(후디, 공통_일정)); + + List memberIds = Stream.of(매트, 리버, 파랑, 후디) + .map(Member::getId) + .collect(Collectors.toList()); // when - boolean actual = subscriptionRepository.existsByIdAndMemberId(색상1_구독.getId(), 후디.getId()); + List actual = subscriptionRepository.findByMemberIdIn(memberIds); // then - assertThat(actual).isTrue(); + assertThat(actual).hasSize(4); } - @DisplayName("회원의 존재하지 않는 구독 정보 여부를 확인한다.") + @DisplayName("member id 리스트가 비어있는 경우 빈 리스트를 반환한다.") @Test - void 회원의_존재하지_않는_구독_정보_여부를_확인한다() { + void member_id_리스트가_비어있는_경우_빈_리스트를_반환한다() { // given - Member 관리자 = memberRepository.save(관리자()); + List memberIds = List.of(); // when - boolean actual = subscriptionRepository.existsByIdAndMemberId(0L, 관리자.getId()); + List actual = subscriptionRepository.findByMemberIdIn(memberIds); // then - assertThat(actual).isFalse(); + assertThat(actual).isEmpty(); } @DisplayName("특정 카테고리들에 속한 구독을 전부 삭제한다") @@ -162,4 +210,45 @@ class SubscriptionRepositoryTest extends RepositoryTest { // then assertThat(subscriptionRepository.findAll()).hasSize(0); } + + @DisplayName("존재하지 않는 id인 경우 예외를 던진다.") + @Test + void 존재하지_않는_id인_경우_예외를_던진다() { + // given + Long id = 0L; + + // when & then + assertThatThrownBy(() -> subscriptionRepository.getById(id)) + .isInstanceOf(NoSuchSubscriptionException.class); + } + + @DisplayName("특정 member가 특정 category를 구독한 경우 예외를 던진다.") + @Test + void 특정_member가_특정_category를_구독한_경우_예외를_던진다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + Category FE_일정 = categoryRepository.save(FE_일정(관리자)); + + // when + Member 매트 = memberRepository.save(매트()); + subscriptionRepository.save(색상1_구독(매트, BE_일정)); // BE만 구독 + + // then + assertThatThrownBy(() -> + subscriptionRepository.validateNotExistsByMemberIdAndCategoryId(매트.getId(), BE_일정.getId())) + .isInstanceOf(ExistSubscriptionException.class); + } + + @DisplayName("특정 구독 id가 특정 member의 구독이 아닌 경우 예외를 던진다.") + @Test + void 특정_구독_id가_특정_member의_구독이_아닌_경우_예외를_던진다() { + // given + Member 매트 = memberRepository.save(매트()); + + // when & then + assertThatThrownBy(() -> + subscriptionRepository.validateExistsByIdAndMemberId(0L, 매트.getId())) + .isInstanceOf(NoPermissionException.class); + } } diff --git a/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionsTest.java b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionsTest.java new file mode 100644 index 00000000..8aed8cff --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionsTest.java @@ -0,0 +1,147 @@ +package com.allog.dallog.domain.subscription.domain; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.setId; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.내_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_일정; +import static com.allog.dallog.common.fixtures.IntegrationScheduleFixtures.달록_여행; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; +import static com.allog.dallog.domain.subscription.domain.Color.COLOR_1; +import static com.allog.dallog.domain.subscription.domain.Color.COLOR_2; +import static com.allog.dallog.domain.subscription.domain.Color.COLOR_3; +import static com.allog.dallog.domain.subscription.domain.Color.COLOR_4; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.member.domain.Member; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SubscriptionsTest { + + @DisplayName("체크된 카테고리 중 내부 카테고리의 아이디를 찾는다.") + @Test + void 체크된_카테고리_중_내부_카테고리의_아이디를_찾는다() { + // given + Member 파랑 = 파랑(); + Category 공통_일정 = setId(공통_일정(파랑), 1L); + Category BE_일정 = setId(BE_일정(파랑), 2L); + Category 내_일정 = setId(내_일정(파랑), 3L); + Category 우아한테크코스_일정 = setId(우아한테크코스_일정(파랑), 4L); + + Subscription 공통_일정_구독 = new Subscription(파랑, 공통_일정, COLOR_1); + Subscription BE_일정_구독 = new Subscription(파랑, BE_일정, COLOR_2); + Subscription 내_일정_구독 = new Subscription(파랑, 내_일정, COLOR_3); + Subscription 우아한테크코스_일정_구독 = new Subscription(파랑, 우아한테크코스_일정, COLOR_4); + + BE_일정_구독.change(COLOR_2, false); + + Subscriptions subscriptions = + new Subscriptions(List.of(공통_일정_구독, BE_일정_구독, 내_일정_구독, 우아한테크코스_일정_구독)); + + // when & then + assertThat(subscriptions.findInternalCategory()).isEqualTo(List.of(공통_일정, 내_일정)); + } + + @DisplayName("체크된 카테고리 중 외부 카테고리의 아이디를 찾는다.") + @Test + void 체크된_카테고리_중_외부_카테고리의_아이디를_찾는다() { + // given + Member 파랑 = 파랑(); + Category 공통_일정 = setId(공통_일정(파랑), 1L); + Category BE_일정 = setId(BE_일정(파랑), 2L); + Category 내_일정 = setId(내_일정(파랑), 3L); + Category 우아한테크코스_일정 = setId(우아한테크코스_일정(파랑), 4L); + + Subscription 공통_일정_구독 = new Subscription(파랑, 공통_일정, COLOR_1); + Subscription BE_일정_구독 = new Subscription(파랑, BE_일정, COLOR_2); + Subscription 내_일정_구독 = new Subscription(파랑, 내_일정, COLOR_3); + Subscription 우아한테크코스_일정_구독 = new Subscription(파랑, 우아한테크코스_일정, COLOR_4); + + Subscriptions subscriptions = + new Subscriptions(List.of(공통_일정_구독, BE_일정_구독, 내_일정_구독, 우아한테크코스_일정_구독)); + + // when & then + assertThat(subscriptions.findExternalCategory()).isEqualTo(List.of(우아한테크코스_일정)); + } + + @DisplayName("특정 스케줄의 구독 색상을 찾는다.") + @Test + void 특정_스케줄의_구독_색상을_찾는다() { + // given + Member 파랑 = 파랑(); + Category 공통_일정 = setId(공통_일정(파랑), 1L); + Category BE_일정 = setId(BE_일정(파랑), 2L); + Category 내_일정 = setId(내_일정(파랑), 3L); + Category 우아한테크코스_일정 = setId(우아한테크코스_일정(파랑), 4L); + + Subscription 공통_일정_구독 = new Subscription(파랑, 공통_일정, COLOR_1); + Subscription BE_일정_구독 = new Subscription(파랑, BE_일정, COLOR_2); + Subscription 내_일정_구독 = new Subscription(파랑, 내_일정, COLOR_3); + Subscription 우아한테크코스_일정_구독 = new Subscription(파랑, 우아한테크코스_일정, COLOR_4); + + Subscriptions subscriptions = + new Subscriptions(List.of(공통_일정_구독, BE_일정_구독, 내_일정_구독, 우아한테크코스_일정_구독)); + + // when & then + assertThat(subscriptions.findColor(달록_여행)).isEqualTo(COLOR_2); + } + + @DisplayName("구독하지 않은 스케줄의 구독 색상을 찾는 경우 예외를 던진다") + @Test + void 구독하지_않은_스케줄의_구독_색상을_찾는_경우_예외를_던진다() { + // given + Member 파랑 = 파랑(); + Category 공통_일정 = setId(공통_일정(파랑), 1L); + setId(BE_일정(파랑), 2L); + + Subscription 공통_일정_구독 = new Subscription(파랑, 공통_일정, COLOR_1); + + Subscriptions subscriptions = new Subscriptions(List.of(공통_일정_구독)); + + // when & then + assertThatThrownBy(() -> subscriptions.findColor(달록_여행)).isInstanceOf(NoSuchCategoryException.class); + } + + @DisplayName("구독한 카테고리중 내부 카테고리를 찾아 반환한다.") + @Test + void 구독한_카테고리중_내부_카테고리를_찾아_반환한다() { + // given + Member 파랑 = 파랑(); + Category 공통_일정 = setId(공통_일정(파랑), 1L); + setId(BE_일정(파랑), 2L); + + Subscription 공통_일정_구독 = new Subscription(파랑, 공통_일정, COLOR_1); + + Subscriptions subscriptions = new Subscriptions(List.of(공통_일정_구독)); + + // when + List categories = subscriptions.findInternalCategory(); + + // then + assertThat(categories).hasSize(1); + } + + @DisplayName("구독한 카테고리중 외부 카테고리를 찾아 반환한다.") + @Test + void 구독한_카테고리중_외부_카테고리를_찾아_반환한다() { + // given + Member 파랑 = 파랑(); + Category 공통_일정 = setId(공통_일정(파랑), 1L); + setId(BE_일정(파랑), 2L); + + Subscription 공통_일정_구독 = new Subscription(파랑, 공통_일정, COLOR_1); + + Subscriptions subscriptions = new Subscriptions(List.of(공통_일정_구독)); + + // when + List categories = subscriptions.findExternalCategory(); + + // then + assertThat(categories).hasSize(0); + } +} diff --git a/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubExternalCalendarClient.java b/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubExternalCalendarClient.java index d26f7013..9551ce56 100644 --- a/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubExternalCalendarClient.java +++ b/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubExternalCalendarClient.java @@ -8,7 +8,7 @@ import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; -import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.schedule.domain.IntegrationSchedule; import java.util.List; public class StubExternalCalendarClient implements ExternalCalendarClient { diff --git a/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubOAuthClient.java b/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubOAuthClient.java index 0b8654a5..e194aee9 100644 --- a/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubOAuthClient.java +++ b/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubOAuthClient.java @@ -1,13 +1,8 @@ package com.allog.dallog.infrastructure.oauth.client; -import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_ACCESS_TOKEN; -import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_CREATOR; -import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_EXPIRES_IN; -import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_MEMBER; -import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_SCOPE; -import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_TOKEN_TYPE; +import com.allog.dallog.common.fixtures.OAuthFixtures; import com.allog.dallog.domain.auth.application.OAuthClient; import com.allog.dallog.domain.auth.dto.OAuthMember; import com.allog.dallog.domain.auth.dto.response.OAuthAccessTokenResponse; @@ -16,15 +11,11 @@ public class StubOAuthClient implements OAuthClient { @Override public OAuthMember getOAuthMember(final String code, final String redirectUri) { - if (code.equals(STUB_MEMBER_인증_코드)) { - return STUB_OAUTH_MEMBER(); - } - return STUB_OAUTH_CREATOR(); + return OAuthFixtures.parseOAuthMember(code); } @Override public OAuthAccessTokenResponse getAccessToken(final String refreshToken) { - return new OAuthAccessTokenResponse(STUB_OAUTH_ACCESS_TOKEN, STUB_OAUTH_EXPIRES_IN, STUB_OAUTH_SCOPE, - STUB_OAUTH_TOKEN_TYPE); + return new OAuthAccessTokenResponse(STUB_OAUTH_ACCESS_TOKEN); } } diff --git a/backend/src/test/java/com/allog/dallog/presentation/AuthControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/AuthControllerTest.java index c253c6ee..4c8f9128 100644 --- a/backend/src/test/java/com/allog/dallog/presentation/AuthControllerTest.java +++ b/backend/src/test/java/com/allog/dallog/presentation/AuthControllerTest.java @@ -1,5 +1,6 @@ package com.allog.dallog.presentation; +import static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER; import static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_인증_코드_토큰_요청; import static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_인증_코드_토큰_응답; import static com.allog.dallog.common.fixtures.AuthFixtures.OAUTH_PROVIDER; @@ -44,14 +45,14 @@ class AuthControllerTest extends ControllerTest { given(authService.generateGoogleLink(any())).willReturn(OAuth_로그인_링크); // when & then - mockMvc.perform(get("/api/auth/{oauthProvider}/oauth-uri?redirectUri={redirectUri}", OAUTH_PROVIDER, + mockMvc.perform(get("/api/auth/{oauthProvider}/oauth-uri?redirectUri={redirectUri}", GOOGLE_PROVIDER, "https://dallog.me/oauth")) .andDo(print()) - .andDo(document("auth/link", + .andDo(document("auth/generateLink", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( - parameterWithName("oauthProvider").description("OAuth 로그인 제공자") + parameterWithName("oauthProvider").description("OAuth 로그인 제공자 (GOOGLE)") ), requestParameters( parameterWithName("redirectUri").description("OAuth Redirect URI") @@ -67,7 +68,6 @@ class AuthControllerTest extends ControllerTest { @Test void OAuth_로그인을_하면_token과_상태코드_200을_반환한다() throws Exception { // given -// TokenRequest tokenRequest = new TokenRequest(STUB_MEMBER_인증_코드, "https://dallog.me/oauth"); given(authService.generateToken(any())).willReturn(MEMBER_인증_코드_토큰_응답()); // when & then @@ -76,7 +76,7 @@ class AuthControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(MEMBER_인증_코드_토큰_요청()))) .andDo(print()) - .andDo(document("auth/token", + .andDo(document("auth/generateToken", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -86,6 +86,9 @@ class AuthControllerTest extends ControllerTest { fieldWithPath("code").type(JsonFieldType.STRING).description("OAuth 로그인 인증 코드"), fieldWithPath("redirectUri").type(JsonFieldType.STRING) .description("OAuth Redirect URI") + ), + responseFields( + fieldWithPath("accessToken").type(JsonFieldType.STRING).description("달록 Access Token") ) )) .andExpect(status().isOk()); @@ -103,7 +106,7 @@ class AuthControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(MEMBER_인증_코드_토큰_요청()))) .andDo(print()) - .andDo(document("auth/exception/token", + .andDo(document("auth/generateToken/failByResourceServerError", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( diff --git a/backend/src/test/java/com/allog/dallog/presentation/CategoryControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/CategoryControllerTest.java index 780bf34f..62e91c5f 100644 --- a/backend/src/test/java/com/allog/dallog/presentation/CategoryControllerTest.java +++ b/backend/src/test/java/com/allog/dallog/presentation/CategoryControllerTest.java @@ -25,6 +25,9 @@ import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; @@ -42,7 +45,6 @@ import com.allog.dallog.domain.category.dto.response.CategoryResponse; import com.allog.dallog.domain.category.exception.InvalidCategoryException; import com.allog.dallog.domain.category.exception.NoSuchCategoryException; -import com.allog.dallog.domain.composition.application.CategorySubscriptionService; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -65,15 +67,12 @@ class CategoryControllerTest extends ControllerTest { @MockBean private CategoryService categoryService; - @MockBean - private CategorySubscriptionService categorySubscriptionService; - @DisplayName("카테고리를 생성한다.") @Test void 카테고리를_생성한다() throws Exception { // given CategoryResponse 카테고리 = BE_일정_응답(후디_응답); - given(categorySubscriptionService.save(any(), any(CategoryCreateRequest.class))).willReturn(카테고리); + given(categoryService.save(any(), any(CategoryCreateRequest.class))).willReturn(카테고리); // when & then mockMvc.perform(post("/api/categories") @@ -83,11 +82,26 @@ class CategoryControllerTest extends ControllerTest { .content(objectMapper.writeValueAsString(BE_일정_생성_요청)) ) .andDo(print()) - .andDo(document("categories/save", + .andDo(document("category/save", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestHeaders( - headerWithName("Authorization").description("JWT 토큰")) + headerWithName("Authorization").description("JWT 토큰")), + requestFields( + fieldWithPath("name").description("카테고리 이름 (최대 20글자)"), + fieldWithPath("categoryType").description("카테고리 타입 (NORMAL | PERSONAL | GOOGLE)") + ), + responseFields( + fieldWithPath("id").description("카테고리 ID"), + fieldWithPath("name").description("카테고리 이름"), + fieldWithPath("categoryType").description("카테고리 타입 (NORMAL | PERSONAL | GOOGLE)"), + fieldWithPath("creator.id").description("카테고리 생성자 ID"), + fieldWithPath("creator.email").description("카테고리 생성자 이메일"), + fieldWithPath("creator.displayName").description("카테고리 생성자 이름"), + fieldWithPath("creator.profileImageUrl").description("카테고리 생성자 프로필 이미지 URL"), + fieldWithPath("creator.socialType").description("카테고리 생성자의 소셜 타입"), + fieldWithPath("createdAt").description("카테고리 생성일자") + ) ) ) .andExpect(status().isCreated()); @@ -100,7 +114,7 @@ class CategoryControllerTest extends ControllerTest { CategoryCreateRequest 잘못된_카테고리_생성_요청 = new CategoryCreateRequest(INVALID_CATEGORY_NAME, NORMAL); willThrow(new InvalidCategoryException(CATEGORY_NAME_OVER_LENGTH_EXCEPTION_MESSAGE)) - .given(categorySubscriptionService) + .given(categoryService) .save(any(), any(CategoryCreateRequest.class)); // when & then @@ -111,7 +125,7 @@ class CategoryControllerTest extends ControllerTest { .content(objectMapper.writeValueAsString(잘못된_카테고리_생성_요청)) ) .andDo(print()) - .andDo(document("categories/save/badRequest", + .andDo(document("category/save/failByInvalidNameFormat", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestHeaders( @@ -138,7 +152,7 @@ class CategoryControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) - .andDo(document("categories/findAll", + .andDo(document("category/findAllByName/allByNoName", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestParameters( @@ -157,7 +171,7 @@ class CategoryControllerTest extends ControllerTest { int page = 0; int size = 10; - List 일정_목록 = List.of(공통_일정(관리자()), BE_일정(관리자()), FE_일정(관리자())); + List 일정_목록 = List.of(BE_일정(관리자()), FE_일정(관리자())); CategoriesResponse categoriesResponse = new CategoriesResponse(page, 일정_목록); given(categoryService.findNormalByName(any(), any())).willReturn(categoriesResponse); @@ -167,7 +181,7 @@ class CategoryControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) - .andDo(document("categories/findAllLikeName", + .andDo(document("category/findAllByName/fileterByName", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestParameters( @@ -189,7 +203,7 @@ class CategoryControllerTest extends ControllerTest { List 일정_목록 = List.of(공통_일정(관리자()), BE_일정(관리자()), FE_일정(관리자())); CategoriesResponse categoriesResponse = new CategoriesResponse(page, 일정_목록); - given(categoryService.findMineByName(any(), any(), any())).willReturn(categoriesResponse); + given(categoryService.findMyCategories(any(), any(), any())).willReturn(categoriesResponse); // when & then mockMvc.perform(get("/api/categories/me?name={name}&page={page}&size={size}", "", page, size) @@ -198,7 +212,7 @@ class CategoryControllerTest extends ControllerTest { .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) ) .andDo(print()) - .andDo(document("categories/findMine", + .andDo(document("category/findMineByName/allByNoName", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestParameters( @@ -220,7 +234,7 @@ class CategoryControllerTest extends ControllerTest { List 일정_목록 = List.of(공통_일정(관리자()), BE_일정(관리자()), FE_일정(관리자())); CategoriesResponse categoriesResponse = new CategoriesResponse(page, 일정_목록); - given(categoryService.findMineByName(any(), any(), any())).willReturn(categoriesResponse); + given(categoryService.findMyCategories(any(), any(), any())).willReturn(categoriesResponse); // when & then mockMvc.perform(get("/api/categories/me?name={name}&page={page}&size={size}", "E", page, size) @@ -229,7 +243,7 @@ class CategoryControllerTest extends ControllerTest { .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) ) .andDo(print()) - .andDo(document("categories/findMineLikeName", + .andDo(document("category/findMineByName/fileterByName", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestParameters( @@ -256,7 +270,7 @@ class CategoryControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) - .andDo(document("categories/findById", + .andDo(document("category/findById", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -281,7 +295,7 @@ class CategoryControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) - .andDo(document("categories/findById/notFound", + .andDo(document("category/findById/failByNoCategory", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -310,7 +324,7 @@ class CategoryControllerTest extends ControllerTest { .content(objectMapper.writeValueAsString(카테고리_수정_요청)) ) .andDo(print()) - .andDo(document("categories/update", + .andDo(document("category/update", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -340,7 +354,7 @@ class CategoryControllerTest extends ControllerTest { .content(objectMapper.writeValueAsString(카테고리_수정_요청)) ) .andDo(print()) - .andDo(document("categories/update/notFound", + .andDo(document("category/update/failByNoCategory", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -370,7 +384,7 @@ class CategoryControllerTest extends ControllerTest { .content(objectMapper.writeValueAsString(카테고리_수정_요청)) ) .andDo(print()) - .andDo(document("categories/update/badRequest", + .andDo(document("category/update/failByInvalidNameFormat", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -388,7 +402,7 @@ class CategoryControllerTest extends ControllerTest { Long categoryId = 1L; willDoNothing() .given(categoryService) - .deleteById(any(), any()); + .delete(any(), any()); // when & then mockMvc.perform(delete("/api/categories/{categoryId}", categoryId) @@ -397,7 +411,7 @@ class CategoryControllerTest extends ControllerTest { .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) ) .andDo(print()) - .andDo(document("categories/delete", + .andDo(document("category/delete", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -416,7 +430,7 @@ class CategoryControllerTest extends ControllerTest { willThrow(new NoSuchCategoryException("존재하지 않는 카테고리를 삭제할 수 없습니다.")) .willDoNothing() .given(categoryService) - .deleteById(any(), any()); + .delete(any(), any()); // when & then mockMvc.perform(delete("/api/categories/{categoryId}", categoryId) @@ -425,7 +439,7 @@ class CategoryControllerTest extends ControllerTest { .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) ) .andDo(print()) - .andDo(document("categories/delete/notFound", + .andDo(document("category/delete/failByNoCategory", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( diff --git a/backend/src/test/java/com/allog/dallog/presentation/ControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/ControllerTest.java index fac37efe..8a389d49 100644 --- a/backend/src/test/java/com/allog/dallog/presentation/ControllerTest.java +++ b/backend/src/test/java/com/allog/dallog/presentation/ControllerTest.java @@ -5,10 +5,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; @AutoConfigureRestDocs @Import(ExternalApiConfig.class) +@ActiveProfiles("test") abstract class ControllerTest { @Autowired diff --git a/backend/src/test/java/com/allog/dallog/presentation/ExternalCalendarControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/ExternalCalendarControllerTest.java index 14fde50a..1a0fae81 100644 --- a/backend/src/test/java/com/allog/dallog/presentation/ExternalCalendarControllerTest.java +++ b/backend/src/test/java/com/allog/dallog/presentation/ExternalCalendarControllerTest.java @@ -22,9 +22,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.category.application.CategoryService; import com.allog.dallog.domain.category.dto.request.ExternalCategoryCreateRequest; -import com.allog.dallog.domain.category.exception.DuplicatedExternalCategoryException; -import com.allog.dallog.domain.composition.application.CategorySubscriptionService; +import com.allog.dallog.domain.category.exception.ExistExternalCategoryException; import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarService; import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendarsResponse; @@ -49,7 +49,7 @@ class ExternalCalendarControllerTest extends ControllerTest { private ExternalCalendarService externalCalendarService; @MockBean - private CategorySubscriptionService categorySubscriptionService; + private CategoryService categoryService; @DisplayName("외부 캘린더의 일정을 조회하면 상태코드 200을 반환한다.") @Test @@ -79,8 +79,7 @@ class ExternalCalendarControllerTest extends ControllerTest { @Test void 외부_캘린더를_카테고리로_저장하면_상태코드_201을_반환한다() throws Exception { // given - given(categorySubscriptionService.save(any(), any(ExternalCategoryCreateRequest.class))).willReturn( - 공통_일정_응답(후디_응답)); + given(categoryService.save(any(), any(ExternalCategoryCreateRequest.class))).willReturn(공통_일정_응답(후디_응답)); // when & then mockMvc.perform(post("/api/external-calendars/me") @@ -107,8 +106,8 @@ class ExternalCalendarControllerTest extends ControllerTest { @Test void 외부_캘린더를_중복하여_저장하면_상태코드_400을_반환한다() throws Exception { // given - willThrow(new DuplicatedExternalCategoryException()) - .given(categorySubscriptionService) + willThrow(new ExistExternalCategoryException()) + .given(categoryService) .save(any(), any(ExternalCategoryCreateRequest.class)); // when & then diff --git a/backend/src/test/java/com/allog/dallog/presentation/MemberControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/MemberControllerTest.java index d060262d..a0fab2db 100644 --- a/backend/src/test/java/com/allog/dallog/presentation/MemberControllerTest.java +++ b/backend/src/test/java/com/allog/dallog/presentation/MemberControllerTest.java @@ -15,13 +15,13 @@ import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.allog.dallog.domain.auth.application.AuthService; -import com.allog.dallog.domain.composition.application.RegisterService; import com.allog.dallog.domain.member.application.MemberService; import com.allog.dallog.domain.member.dto.MemberUpdateRequest; import com.allog.dallog.domain.member.exception.NoSuchMemberException; @@ -44,9 +44,6 @@ class MemberControllerTest extends ControllerTest { @MockBean private MemberService memberService; - @MockBean - private RegisterService registerService; - @DisplayName("자신의 회원 정보를 조회한다.") @Test void 자신의_회원_정보를_조회한다() throws Exception { @@ -61,11 +58,18 @@ class MemberControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) - .andDo(document("members/me", + .andDo(document("member/findMe", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestHeaders( headerWithName("Authorization").description("JWT 토큰") + ), + responseFields( + fieldWithPath("id").description("회원 ID"), + fieldWithPath("email").description("회원 이메일"), + fieldWithPath("displayName").description("회원 이름"), + fieldWithPath("profileImageUrl").description("회원 프로필 이미지 URL"), + fieldWithPath("socialType").description("회원 소셜 타입") ) )) .andExpect(status().isOk()); @@ -84,7 +88,7 @@ class MemberControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) - .andDo(document("members/exception/notfound", + .andDo(document("member/findMe/failNoMember", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestHeaders( @@ -111,7 +115,7 @@ class MemberControllerTest extends ControllerTest { .content(objectMapper.writeValueAsString(회원_수정_요청)) ) .andDo(print()) - .andDo(document("members/update", + .andDo(document("member/update", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestHeaders( @@ -128,8 +132,8 @@ class MemberControllerTest extends ControllerTest { void 등록된_회원이_회원탈퇴_한다() throws Exception { // given willDoNothing() - .given(registerService) - .deleteByMemberId(any()); + .given(memberService) + .deleteById(any()); // when & then mockMvc.perform(delete("/api/members/me") @@ -137,7 +141,7 @@ class MemberControllerTest extends ControllerTest { .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) ) .andDo(print()) - .andDo(document("members/delete", + .andDo(document("member/delete", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestHeaders( diff --git a/backend/src/test/java/com/allog/dallog/presentation/ScheduleControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/ScheduleControllerTest.java index 07e224ef..70c93da3 100644 --- a/backend/src/test/java/com/allog/dallog/presentation/ScheduleControllerTest.java +++ b/backend/src/test/java/com/allog/dallog/presentation/ScheduleControllerTest.java @@ -30,11 +30,8 @@ import com.allog.dallog.domain.auth.application.AuthService; import com.allog.dallog.domain.auth.exception.NoPermissionException; import com.allog.dallog.domain.category.exception.NoSuchCategoryException; -import com.allog.dallog.domain.composition.application.CalendarService; -import com.allog.dallog.domain.composition.application.SchedulerService; -import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; -import com.allog.dallog.domain.integrationschedule.dao.IntegrationScheduleDao; import com.allog.dallog.domain.schedule.application.ScheduleService; +import com.allog.dallog.domain.schedule.application.SubscribingSchedulesFinder; import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponse; @@ -63,16 +60,7 @@ class ScheduleControllerTest extends ControllerTest { private ScheduleService scheduleService; @MockBean - private SchedulerService schedulerService; - - @MockBean - private CalendarService calendarService; - - @MockBean - private IntegrationScheduleDao integrationScheduleDao; - - @MockBean - private ExternalCalendarClient externalCalendarClient; + private SubscribingSchedulesFinder subscribingSchedulesFinder; @DisplayName("일정 정보를 등록하면 상태코드 201을 반환한다.") @Test @@ -90,7 +78,7 @@ class ScheduleControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) - .andDo(document("schedules/save", + .andDo(document("schedule/save", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()) )) @@ -113,7 +101,7 @@ class ScheduleControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) - .andDo(document("schedules/save/forbidden", + .andDo(document("schedule/save/failByNoPermission", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()) )) @@ -136,7 +124,7 @@ class ScheduleControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andDo(print()) - .andDo(document("schedules/save/notfound", + .andDo(document("schedule/save/failByNoCategory", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()) )) @@ -156,7 +144,7 @@ class ScheduleControllerTest extends ControllerTest { .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) .accept(MediaType.APPLICATION_JSON)) .andDo(print()) - .andDo(document("schedules/findone", + .andDo(document("schedule/findById", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()) )) @@ -176,7 +164,7 @@ class ScheduleControllerTest extends ControllerTest { .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) .accept(MediaType.APPLICATION_JSON)) .andDo(print()) - .andDo(document("schedules/findone/notfound", + .andDo(document("schedule/findById/failByNoSchedule", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()) )) @@ -188,8 +176,10 @@ class ScheduleControllerTest extends ControllerTest { @Test void 일정을_수정하는데_성공하면_204를_반환한다() throws Exception { // given + Long categoryId = 1L; Long scheduleId = 1L; - ScheduleUpdateRequest 수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + ScheduleUpdateRequest 수정_요청 = new ScheduleUpdateRequest(categoryId, 레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, + 레벨_인터뷰_메모); willDoNothing() .given(scheduleService) .update(any(), any(), any()); @@ -200,7 +190,7 @@ class ScheduleControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(수정_요청))) .andDo(print()) - .andDo(document("schedules/update", + .andDo(document("schedule/update", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -214,8 +204,10 @@ class ScheduleControllerTest extends ControllerTest { @Test void 일정을_수정하는데_해당_일정의_카테고리에_대한_권한이_없다면_403을_반환한다() throws Exception { // given + Long categoryId = 1L; Long scheduleId = 1L; - ScheduleUpdateRequest 수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + ScheduleUpdateRequest 수정_요청 = new ScheduleUpdateRequest(categoryId, 레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, + 레벨_인터뷰_메모); willThrow(new NoPermissionException()) .given(scheduleService) .update(any(), any(), any()); @@ -226,7 +218,7 @@ class ScheduleControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(수정_요청))) .andDo(print()) - .andDo(document("schedules/update/forbidden", + .andDo(document("schedule/update/failByNoPermission", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()) )) @@ -237,8 +229,10 @@ class ScheduleControllerTest extends ControllerTest { @Test void 일정을_수정하는데_일정이_존재하지_않는_경우_404를_반환한다() throws Exception { // given + Long categoryId = 1L; Long scheduleId = 1L; - ScheduleUpdateRequest 수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + ScheduleUpdateRequest 수정_요청 = new ScheduleUpdateRequest(categoryId, 레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, + 레벨_인터뷰_메모); willThrow(new NoSuchScheduleException()) .given(scheduleService) .update(any(), any(), any()); @@ -249,7 +243,7 @@ class ScheduleControllerTest extends ControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(수정_요청))) .andDo(print()) - .andDo(document("schedules/update/notfound", + .andDo(document("schedule/update/failByNoSchedule", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()) )) @@ -263,13 +257,13 @@ class ScheduleControllerTest extends ControllerTest { Long scheduleId = 1L; willDoNothing() .given(scheduleService) - .deleteById(any(), any()); + .delete(any(), any()); // when & then mockMvc.perform(RestDocumentationRequestBuilders.delete("/api/schedules/{scheduleId}", scheduleId) .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) .andDo(print()) - .andDo(document("schedules/delete", + .andDo(document("schedule/delete", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -286,13 +280,13 @@ class ScheduleControllerTest extends ControllerTest { Long scheduleId = 1L; willThrow(new NoPermissionException()) .given(scheduleService) - .deleteById(any(), any()); + .delete(any(), any()); // when & then mockMvc.perform(delete("/api/schedules/{scheduleId}", scheduleId) .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) .andDo(print()) - .andDo(document("schedules/delete/forbidden", + .andDo(document("schedule/delete/failByNoPermission", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()) )) @@ -306,13 +300,13 @@ class ScheduleControllerTest extends ControllerTest { Long scheduleId = 1L; willThrow(new NoSuchScheduleException()) .given(scheduleService) - .deleteById(any(), any()); + .delete(any(), any()); // when & then mockMvc.perform(delete("/api/schedules/{scheduleId}", scheduleId) .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) .andDo(print()) - .andDo(document("schedules/delete/notfound", + .andDo(document("schedule/delete/failByNoSchedule", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()) )) @@ -347,7 +341,7 @@ class ScheduleControllerTest extends ControllerTest { MemberScheduleResponses memberScheduleResponses = new MemberScheduleResponses(List.of(장기간_일정_1, 장기간_일정_2), List.of(종일_일정_1, 종일_일정_2), List.of(짧은_일정_1, 짧은_일정_2)); - given(calendarService.findSchedulesByMemberId(any(), any())) + given(subscribingSchedulesFinder.findMySubscribingSchedules(any(), any())) .willReturn(memberScheduleResponses); // when & then @@ -355,7 +349,7 @@ class ScheduleControllerTest extends ControllerTest { get("/api/members/me/schedules?startDateTime={startDate}&endDateTime={endDate}", startDate, endDate) .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) .andDo(print()) - .andDo(document("schedules/findAllByMember", + .andDo(document("schedule/findSchedulesByMemberId", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestParameters( diff --git a/backend/src/test/java/com/allog/dallog/presentation/SchedulerControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/SchedulerControllerTest.java deleted file mode 100644 index 85c174a9..00000000 --- a/backend/src/test/java/com/allog/dallog/presentation/SchedulerControllerTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.allog.dallog.presentation; - -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; -import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import com.allog.dallog.domain.auth.application.AuthService; -import com.allog.dallog.domain.composition.application.SchedulerService; -import com.allog.dallog.domain.integrationschedule.domain.Period; -import com.allog.dallog.domain.schedule.dto.response.PeriodResponse; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; - -@WebMvcTest(SchedulerController.class) -class SchedulerControllerTest extends ControllerTest { - - private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; - private static final String AUTHORIZATION_HEADER_VALUE = "Bearer aaaaaaaa.bbbbbbbb.cccccccc"; - - @MockBean - private AuthService authService; - - @MockBean - private SchedulerService schedulerService; - - @DisplayName("일정 조율 결과를 반환한다.") - @Test - void 일정_조율_결과를_반환한다() throws Exception { - // given - String startDateTime = "2022-07-01T00:00"; - String endDateTime = "2022-07-31T00:00"; - - given(schedulerService.getAvailablePeriods(any(), any())) - .willReturn(List.of( - new PeriodResponse(new Period(날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_10일_0시_0분)), - new PeriodResponse(new Period(날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분)), - new PeriodResponse(new Period(날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_20시_0분)), - new PeriodResponse(new Period(날짜_2022년_7월_20일_11시_59분, 날짜_2022년_7월_27일_0시_0분)) - )); - - // when & then - mockMvc.perform(RestDocumentationRequestBuilders.get( - "/api/scheduler/categories/{categoryId}/available-periods?startDateTime={startDateTime}&endDateTime={endDateTime}", - 1L, startDateTime, endDateTime) - .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) - .andDo(print()) - .andDo(document("scheduler/category/available-periods", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - pathParameters( - parameterWithName("categoryId").description("카테고리 ID") - ), - requestParameters( - parameterWithName("startDateTime").description("일정 조회 시작 범위 (yyyy-mm-dd'T'HH:mm)"), - parameterWithName("endDateTime").description("일정 조회 마지막 범위 (yyyy-mm-dd'T'HH:mm)") - ) - )) - .andExpect(status().isOk()); - } - -} diff --git a/backend/src/test/java/com/allog/dallog/presentation/SubscriptionControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/SubscriptionControllerTest.java index 06bb1753..1718082e 100644 --- a/backend/src/test/java/com/allog/dallog/presentation/SubscriptionControllerTest.java +++ b/backend/src/test/java/com/allog/dallog/presentation/SubscriptionControllerTest.java @@ -85,30 +85,6 @@ class SubscriptionControllerTest extends ControllerTest { .andExpect(status().isCreated()); } - @DisplayName("자신의 구독 목록을 가져온다.") - @Test - void 자신의_구독_목록을_가져온다() throws Exception { - // given - CategoryResponse 공통_일정_응답 = 공통_일정_응답(관리자_응답); - - SubscriptionsResponse subscriptionsResponse = new SubscriptionsResponse( - List.of(색상1_구독_응답(공통_일정_응답), 색상2_구독_응답(공통_일정_응답), 색상3_구독_응답(공통_일정_응답))); - - given(subscriptionService.findByMemberId(any())) - .willReturn(subscriptionsResponse); - - // when & then - mockMvc.perform(get("/api/members/me/subscriptions", 공통_일정_응답.getId()) - .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) - .accept(MediaType.APPLICATION_JSON)) - .andDo(print()) - .andDo(document("subscription/findMine", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()) - )) - .andExpect(status().isOk()); - } - @DisplayName("회원이 이미 카테고리를 구독한 경우 예외를 던진다.") @Test void 회원이_이미_카테고리를_구독한_경우_예외를_던진다() throws Exception { @@ -122,7 +98,7 @@ class SubscriptionControllerTest extends ControllerTest { .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) .accept(MediaType.APPLICATION_JSON)) .andDo(print()) - .andDo(document("subscription/exist", + .andDo(document("subscription/save/failByAlreadyExisting", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -147,7 +123,7 @@ class SubscriptionControllerTest extends ControllerTest { .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) .accept(MediaType.APPLICATION_JSON)) .andDo(print()) - .andDo(document("subscription/private-category", + .andDo(document("subscription/save/failBySubscribingPrivateCategoryOfOther", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( @@ -182,7 +158,7 @@ class SubscriptionControllerTest extends ControllerTest { .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) - .andDo(document("subscription/me", + .andDo(document("subscription/findMine", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestHeaders( @@ -234,7 +210,7 @@ class SubscriptionControllerTest extends ControllerTest { given(authService.extractMemberId(any())).willReturn(매트_응답.getId()); willDoNothing().given(subscriptionService) - .deleteById(색상1_구독_응답.getId(), 매트_응답.getId()); + .delete(색상1_구독_응답.getId(), 매트_응답.getId()); // when & then mockMvc.perform(delete("/api/members/me/subscriptions/{subscriptionId}", 색상1_구독_응답.getId()) @@ -253,22 +229,22 @@ class SubscriptionControllerTest extends ControllerTest { .andExpect(status().isNoContent()); } - @DisplayName("자신이 가지고 있지 않은 구독 정보인 경우 예외를 던진다.") + @DisplayName("구독 제거시 자신이 가지고 있지 않은 구독 정보인 경우 예외를 던진다.") @Test - void 자신이_가지고_있지_않은_구독_정보인_경우_예외를_던진다() throws Exception { + void 구독_제거시_자신이_가지고_있지_않은_구독_정보인_경우_예외를_던진다() throws Exception { // given given(authService.extractMemberId(any())).willReturn(매트_응답.getId()); willThrow(new NoPermissionException()) .willDoNothing() .given(subscriptionService) - .deleteById(any(), any()); + .delete(any(), any()); // when & then mockMvc.perform(delete("/api/members/me/subscriptions/{subscriptionId}", 1L) .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) - .andDo(document("subscription/permission", + .andDo(document("subscription/delete/failByNoPermission", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml index a00ea450..03c30d37 100644 --- a/backend/src/test/resources/application.yml +++ b/backend/src/test/resources/application.yml @@ -1,50 +1,3 @@ spring: - main: - allow-bean-definition-overriding: true - - datasource: - url: jdbc:h2:~/dallog;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE - username: sa - - sql: - init: - mode: NEVER - - jpa: - properties: - hibernate: - format_sql: true - show-sql: true - - hibernate: - ddl-auto: create - - data: - web: - pageable: - max-page-size: 100 - -logging: - level: - org.hibernate.type.descriptor.sql.BasicBinder: TRACE - -oauth: - google: - client-id: hyeonic - client-secret: 123 - oauth-end-point: https://accounts.google.com/o/oauth2/v2/auth - response-type: code - scopes: - - https://www.googleapis.com/auth/userinfo.profile - - https://www.googleapis.com/auth/userinfo.email - token-uri: https://oauth2.googleapis.com/token - -cors: - allow-origin: - urls: http://localhost:3000 - -security: - jwt: - token: - secret-key: fsmjgbdafmjgbasmfgadbsgmadfhgbfamjghbvmssdgsdfgdf - expire-length: 3600000 + profiles: + active: test diff --git a/backend/~/Desktop/jacoco/index.xml b/backend/~/Desktop/jacoco/index.xml deleted file mode 100644 index bde0fd01..00000000 --- a/backend/~/Desktop/jacoco/index.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 4d793cf9..de2dcbb3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,8 @@ "pretty": "prettier . --write", "check-lint": "eslint . --ext .js,.jsx,.ts,.tsx", "check-prettier": "prettier -c ./src", - "test": "jest --setupFiles ./setupFile.js" + "test": "jest --setupFiles ./setupFile.js", + "report": "webpack-bundle-analyzer --port 4200 dist/stats.json" }, "dependencies": { "@emotion/react": "^11.9.3", @@ -55,6 +56,7 @@ "babel-loader": "^8.2.5", "clean-webpack-plugin": "^4.0.0", "css-loader": "^6.7.1", + "css-minimizer-webpack-plugin": "^4.1.0", "eslint": "^8.19.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", @@ -65,6 +67,7 @@ "html-webpack-plugin": "^5.5.0", "jest": "^28.1.3", "jest-environment-jsdom": "^28.1.3", + "mini-css-extract-plugin": "^2.6.1", "msw": "^0.43.0", "prettier": "^2.7.1", "react-icons": "^4.4.0", @@ -72,6 +75,7 @@ "ts-loader": "^9.3.1", "typescript": "^4.7.4", "webpack": "^5.73.0", + "webpack-bundle-analyzer": "^4.6.1", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.9.3" }, diff --git a/frontend/src/@types/index.ts b/frontend/src/@types/index.ts index 0cb0d39d..30daa7d5 100644 --- a/frontend/src/@types/index.ts +++ b/frontend/src/@types/index.ts @@ -6,6 +6,12 @@ interface FieldsetCssPropType { label?: SerializedStyles; } +interface SelectCssPropType { + select?: SerializedStyles; + optionBox?: SerializedStyles; + option?: SerializedStyles; +} + interface InputRefType { [index: string]: React.RefObject; } @@ -17,4 +23,4 @@ interface ModalPosType { left?: number; } -export { FieldsetCssPropType, InputRefType, ModalPosType }; +export { FieldsetCssPropType, InputRefType, ModalPosType, SelectCssPropType }; diff --git a/frontend/src/@types/scheduler.ts b/frontend/src/@types/scheduler.ts deleted file mode 100644 index dee1743a..00000000 --- a/frontend/src/@types/scheduler.ts +++ /dev/null @@ -1,6 +0,0 @@ -interface SchedulingResponseType { - startDateTime: string; - endDateTime: string; -} - -export { SchedulingResponseType }; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a9fa4487..2755e89f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,26 +1,27 @@ import { AxiosError } from 'axios'; +import { lazy, Suspense } from 'react'; import { QueryClient, QueryClientProvider } from 'react-query'; +import { ReactQueryDevtools } from 'react-query/devtools'; import { Route, BrowserRouter as Router, Routes } from 'react-router-dom'; import useSnackBar from '@/hooks/useSnackBar'; import ErrorBoundary from '@/components/@common/ErrorBoundary/ErrorBoundary'; -import NavBar from '@/components/NavBar/NavBar'; -import ProtectRoute from '@/components/ProtectRoute/ProtectRoute'; -import PublicRoute from '@/components/PublicRoute/PublicRoute'; -import SideBar from '@/components/SideBar/SideBar'; -import SnackBar from '@/components/SnackBar/SnackBar'; -import AuthPage from '@/pages/AuthPage/AuthPage'; -import CategoryPage from '@/pages/CategoryPage/CategoryPage'; -import MainPage from '@/pages/MainPage/MainPage'; -import NotFoundPage from '@/pages/NotFoundPage/NotFoundPage'; -import PrivacyPolicyPage from '@/pages/PrivacyPolicyPage/PrivacyPolicyPage'; -import SchedulingPage from '@/pages/SchedulingPage/SchedulingPage'; import { PATH } from '@/constants'; import { ERROR_MESSAGE } from './constants/message'; +const NavBar = lazy(() => import('@/components/NavBar/NavBar')); +const ProtectRoute = lazy(() => import('@/components/ProtectRoute/ProtectRoute')); +const SideBar = lazy(() => import('@/components/SideBar/SideBar')); +const SnackBar = lazy(() => import('@/components/SnackBar/SnackBar')); +const AuthPage = lazy(() => import('@/pages/AuthPage/AuthPage')); +const CategoryPage = lazy(() => import('@/pages/CategoryPage/CategoryPage')); +const MainPage = lazy(() => import('@/pages/MainPage/MainPage')); +const NotFoundPage = lazy(() => import('@/pages/NotFoundPage/NotFoundPage')); +const PrivacyPolicyPage = lazy(() => import('@/pages/PrivacyPolicyPage/PrivacyPolicyPage')); + function App() { const { openSnackBar } = useSnackBar(); @@ -34,6 +35,7 @@ function App() { queries: { retry: 1, retryDelay: 0, + staleTime: 1 * 60 * 1000, onError, }, mutations: { @@ -48,23 +50,23 @@ function App() { - - - - }> + }> + + + } /> } /> } /> } /> - - }> - } /> - } /> - - + }> + } /> + + + + ); } diff --git a/frontend/src/api/scheduler.ts b/frontend/src/api/scheduler.ts deleted file mode 100644 index f846ff22..00000000 --- a/frontend/src/api/scheduler.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { SchedulingResponseType } from '@/@types/scheduler'; - -import { DATE_TIME } from '@/constants/date'; - -import dallogApi from '.'; - -const schedulerApi = { - endpoint: (categoryId: number) => `/api/scheduler/categories/${categoryId}/available-periods`, - - headers: { - 'Content-Type': 'application/json', - }, - - get: async ( - accessToken: string, - categoryId: number, - startDateTime: string, - endDateTime: string - ) => { - const response = await dallogApi.get( - `${schedulerApi.endpoint(categoryId)}?startDateTime=${startDateTime}T${ - DATE_TIME.START - }&endDateTime=${endDateTime}T${DATE_TIME.END}`, - { - headers: { ...schedulerApi.headers, Authorization: `Bearer ${accessToken}` }, - } - ); - - return response; - }, -}; - -export default schedulerApi; diff --git a/frontend/src/assets/how_to_use_1.png b/frontend/src/assets/how_to_use_1.png deleted file mode 100644 index 19330c39..00000000 Binary files a/frontend/src/assets/how_to_use_1.png and /dev/null differ diff --git a/frontend/src/assets/how_to_use_2.png b/frontend/src/assets/how_to_use_2.png deleted file mode 100644 index e1fbd040..00000000 Binary files a/frontend/src/assets/how_to_use_2.png and /dev/null differ diff --git a/frontend/src/assets/how_to_use_3.png b/frontend/src/assets/how_to_use_3.png deleted file mode 100644 index 7dcb9867..00000000 Binary files a/frontend/src/assets/how_to_use_3.png and /dev/null differ diff --git a/frontend/src/components/@common/Fieldset/Fieldset.tsx b/frontend/src/components/@common/Fieldset/Fieldset.tsx index db0eb11d..d2c8ca9c 100644 --- a/frontend/src/components/@common/Fieldset/Fieldset.tsx +++ b/frontend/src/components/@common/Fieldset/Fieldset.tsx @@ -16,6 +16,8 @@ interface FieldsetProps extends React.HTMLAttributes { onChange?: (e: React.ChangeEvent) => void; isValid?: boolean; errorMessage?: string; + min?: string | number; + max?: string | number; } function Fieldset({ @@ -32,6 +34,8 @@ function Fieldset({ labelText, isValid, errorMessage, + min, + max, }: FieldsetProps) { const theme = useTheme(); @@ -53,6 +57,8 @@ function Fieldset({ ref={refProp} disabled={disabled} onChange={onChange} + min={min} + max={max} /> {errorMessage && {errorMessage}} diff --git a/frontend/src/components/@common/Select/Select.stories.tsx b/frontend/src/components/@common/Select/Select.stories.tsx new file mode 100644 index 00000000..ee9ec077 --- /dev/null +++ b/frontend/src/components/@common/Select/Select.stories.tsx @@ -0,0 +1,19 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import { TIMES } from '@/constants/date'; + +import Select from './Select'; + +export default { + title: 'Components/@Common/Select', + component: Select, +} as ComponentMeta; + +const Template: ComponentStory = (args) => + + + + ))} + + + + ); +} + +export default Select; diff --git a/frontend/src/components/MyCategoryItem/MyCategoryItem.style.ts b/frontend/src/components/MyCategoryItem/MyCategoryItem.style.ts deleted file mode 100644 index 587c27cd..00000000 --- a/frontend/src/components/MyCategoryItem/MyCategoryItem.style.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { css, Theme } from '@emotion/react'; - -const buttonStyle = ({ colors }: Theme) => css` - position: relative; - - width: 8rem; - height: 8rem; - - background: transparent; - - color: ${colors.GRAY_700}; - - &:hover { - border-radius: 50%; - - background: ${colors.GRAY_100}; - - filter: none; - } - - &:hover span { - visibility: visible; - } -`; - -const menuTitle = ({ colors }: Theme) => css` - visibility: hidden; - position: absolute; - top: 120%; - left: 50%; - transform: translateX(-50%); - - padding: 2rem 3rem; - - background: ${colors.GRAY_700}ee; - - font-size: 3rem; - font-weight: normal; - color: ${colors.WHITE}; - white-space: nowrap; -`; - -const categoryItemStyle = ({ colors, flex }: Theme) => css` - ${flex.row} - - justify-content: space-around; - - height: 20rem; - border-bottom: 1px solid ${colors.GRAY_400}; - - font-size: 4rem; -`; - -const itemStyle = css` - flex: 1 1 0; - text-align: center; -`; - -const grayTextStyle = ({ colors }: Theme) => css` - color: ${colors.GRAY_600}; -`; - -export { buttonStyle, categoryItemStyle, grayTextStyle, itemStyle, menuTitle }; diff --git a/frontend/src/components/MyCategoryItem/MyCategoryItem.tsx b/frontend/src/components/MyCategoryItem/MyCategoryItem.tsx deleted file mode 100644 index e041a8dd..00000000 --- a/frontend/src/components/MyCategoryItem/MyCategoryItem.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { useMutation, useQueryClient } from 'react-query'; -import { useRecoilValue } from 'recoil'; - -import useToggle from '@/hooks/useToggle'; - -import { CategoryType } from '@/@types/category'; - -import { userState } from '@/recoil/atoms'; - -import Button from '@/components/@common/Button/Button'; -import ModalPortal from '@/components/@common/ModalPortal/ModalPortal'; -import CategoryModifyModal from '@/components/CategoryModifyModal/CategoryModifyModal'; - -import { CACHE_KEY } from '@/constants/api'; -import { CATEGORY_TYPE } from '@/constants/category'; -import { CONFIRM_MESSAGE, TOOLTIP_MESSAGE } from '@/constants/message'; - -import { getISODateString } from '@/utils/date'; - -import categoryApi from '@/api/category'; - -import { FiEdit3 } from 'react-icons/fi'; -import { RiDeleteBin6Line } from 'react-icons/ri'; - -import { - buttonStyle, - categoryItemStyle, - grayTextStyle, - itemStyle, - menuTitle, -} from './MyCategoryItem.style'; - -interface MyCategoryItemProps { - category: CategoryType; -} - -function MyCategoryItem({ category }: MyCategoryItemProps) { - const { accessToken } = useRecoilValue(userState); - - const { state: isCategoryModifyModalOpen, toggleState: toggleCategoryModifyModalOpen } = - useToggle(); - - const queryClient = useQueryClient(); - const { mutate } = useMutation(() => categoryApi.delete(accessToken, category.id), { - onSuccess: () => onSuccessDeleteCategory(), - }); - - const handleClickDeleteButton = () => { - if (confirm(CONFIRM_MESSAGE.DELETE)) { - mutate(); - } - }; - - const onSuccessDeleteCategory = () => { - queryClient.invalidateQueries(CACHE_KEY.CATEGORIES); - queryClient.invalidateQueries(CACHE_KEY.MY_CATEGORIES); - queryClient.invalidateQueries(CACHE_KEY.SUBSCRIPTIONS); - }; - - const canEditCategory = category.categoryType !== CATEGORY_TYPE.PERSONAL; - - return ( -
- {getISODateString(category.createdAt)} - - {category.name} - - {category.categoryType === CATEGORY_TYPE.GOOGLE && ' (구글)'} - {category.categoryType === CATEGORY_TYPE.PERSONAL && ' (기본)'} - - -
- - - - - -
-
- ); -} - -export default MyCategoryItem; diff --git a/frontend/src/components/MyCategoryList/MyCategoryList.fallback.tsx b/frontend/src/components/MyCategoryList/MyCategoryList.fallback.tsx deleted file mode 100644 index 8f7c2f26..00000000 --- a/frontend/src/components/MyCategoryList/MyCategoryList.fallback.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import Skeleton from '@/components/@common/Skeleton/Skeleton'; -import { - categoryTableHeaderStyle, - categoryTableStyle, - itemStyle, -} from '@/components/CategoryList/CategoryList.styles'; -import { - categoryItem, - item, -} from '@/components/SubscribedCategoryItem/SubscribedCategoryItem.styles'; - -function CategoryListFallback() { - return ( -
-
- 생성 날짜 - 카테고리 이름 - 수정 / 삭제 -
-
- {new Array(6).fill(0).map((el, index) => ( -
- - - -
- ))} -
-
- ); -} - -export default CategoryListFallback; diff --git a/frontend/src/components/MyCategoryList/MyCategoryList.styles.ts b/frontend/src/components/MyCategoryList/MyCategoryList.styles.ts deleted file mode 100644 index 17a620cf..00000000 --- a/frontend/src/components/MyCategoryList/MyCategoryList.styles.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { css, Theme } from '@emotion/react'; - -const itemStyle = css` - flex: 1 1 0; - text-align: center; -`; - -const headerStyle = ({ flex, colors }: Theme) => css` - ${flex.row} - - justify-content: space-around; - - width: 100%; - height: 12rem; - border-bottom: 2px solid ${colors.GRAY_400}; - - background: ${colors.GRAY_100}; - - font-size: 4rem; - font-weight: 700; -`; - -const buttonStyle = ({ colors }: Theme) => css` - width: 8rem; - height: 8rem; - - background: transparent; - - color: ${colors.GRAY_700}; - - &:hover { - border-radius: 50%; - - background: ${colors.GRAY_100}; - - filter: none; - } -`; - -const listStyle = css` - width: 100%; - height: 100%; - - overflow-y: overlay; -`; - -export { buttonStyle, listStyle, headerStyle, itemStyle }; diff --git a/frontend/src/components/MyCategoryList/MyCategoryList.tsx b/frontend/src/components/MyCategoryList/MyCategoryList.tsx deleted file mode 100644 index d724cff5..00000000 --- a/frontend/src/components/MyCategoryList/MyCategoryList.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { AxiosError, AxiosResponse } from 'axios'; -import { useQuery } from 'react-query'; -import { useRecoilValue } from 'recoil'; - -import { CategoryType } from '@/@types/category'; - -import { userState } from '@/recoil/atoms'; - -import MyCategoryItem from '@/components/MyCategoryItem/MyCategoryItem'; - -import { CACHE_KEY } from '@/constants/api'; - -import categoryApi from '@/api/category'; - -import { headerStyle, itemStyle, listStyle } from './MyCategoryList.styles'; - -function MyCategoryList() { - const { accessToken } = useRecoilValue(userState); - - const { data } = useQuery, AxiosError>( - CACHE_KEY.MY_CATEGORIES, - () => categoryApi.getMy(accessToken) - ); - - return ( - <> -
- 생성 날짜 - 카테고리 이름 - 수정 / 삭제 -
-
- {data?.data.map((category) => ( - - ))} -
- - ); -} - -export default MyCategoryList; diff --git a/frontend/src/components/NavBar/NavBar.tsx b/frontend/src/components/NavBar/NavBar.tsx index c996ef20..42d0ba60 100644 --- a/frontend/src/components/NavBar/NavBar.tsx +++ b/frontend/src/components/NavBar/NavBar.tsx @@ -15,11 +15,13 @@ import ProfileFallback from '@/components/Profile/Profile.fallback'; import { PATH } from '@/constants'; import { TRANSPARENT } from '@/constants/style'; -import { BiCategory } from 'react-icons/bi'; -import { FaUserCircle } from 'react-icons/fa'; -import { FiCalendar } from 'react-icons/fi'; -import { HiChevronDoubleLeft, HiMenu } from 'react-icons/hi'; -import { IoPeopleOutline } from 'react-icons/io5'; +import { + MdCalendarToday, + MdMenu, + MdMenuOpen, + MdOutlineCategory, + MdPersonOutline, +} from 'react-icons/md'; import BlackLogo from '../../assets/dallog_black.png'; import { logo, logoImg, logoText, menu, menus, menuTitle, navBar } from './NavBar.styles'; @@ -47,10 +49,6 @@ function NavBar() { navigate(PATH.CATEGORY); }; - const handleClickSchedulingMenuButton = () => { - navigate(PATH.SCHEDULING); - }; - const handleClickProfileMenuButton = () => { toggleProfileModalOpen(); }; @@ -60,7 +58,7 @@ function NavBar() {
{accessToken && ( )} @@ -73,19 +71,15 @@ function NavBar() { {accessToken && ( <> - handleClickCompleteButton(user.displayName)} > - + 완료 @@ -113,7 +112,7 @@ function Profile() {
{user.displayName}
diff --git a/frontend/src/components/PublicRoute/PublicRoute.tsx b/frontend/src/components/PublicRoute/PublicRoute.tsx deleted file mode 100644 index b6b15b61..00000000 --- a/frontend/src/components/PublicRoute/PublicRoute.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { Outlet } from 'react-router-dom'; - -import useUserValue from '@/hooks/useUserValue'; - -function PublicRoute() { - const { isAuthenticating } = useUserValue(); - - if (isAuthenticating) { - return <>; - } - - return ; -} - -export default PublicRoute; diff --git a/frontend/src/components/ScheduleAddButton/ScheduleAddButton.tsx b/frontend/src/components/ScheduleAddButton/ScheduleAddButton.tsx index e835f153..6d4bba2b 100644 --- a/frontend/src/components/ScheduleAddButton/ScheduleAddButton.tsx +++ b/frontend/src/components/ScheduleAddButton/ScheduleAddButton.tsx @@ -2,7 +2,7 @@ import { useTheme } from '@emotion/react'; import Button from '@/components/@common/Button/Button'; -import { BsCalendarPlusFill } from 'react-icons/bs'; +import { MdEditCalendar } from 'react-icons/md'; import { scheduleAddButton } from './ScheduleAddButton.styles'; @@ -15,7 +15,7 @@ function ScheduleAddButton({ onClick }: ScheduleAddButtonProps) { return ( ); } diff --git a/frontend/src/components/ScheduleAddModal/ScheduleAddModal.styles.ts b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.styles.ts index a42993b6..45397a4b 100644 --- a/frontend/src/components/ScheduleAddModal/ScheduleAddModal.styles.ts +++ b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.styles.ts @@ -136,16 +136,45 @@ const labelStyle = ({ colors }: Theme) => css` color: ${colors.GRAY_800}; `; +const dateTimePickerStyle = ({ flex }: Theme) => css` + ${flex.row}; + + justify-content: space-between; + align-items: flex-end; + + width: 100%; +`; + +const dateFieldsetStyle = (isAllDay: boolean) => { + return { + div: css` + width: ${isAllDay ? '100%' : '45%'}; + `, + input: css` + height: 11.75rem; + `, + }; +}; + +const selectTimeStyle = { + select: css` + width: 45%; + `, +}; + export { arrow, categorySelect, cancelButton, checkboxStyle, controlButtons, + dateFieldsetStyle, dateTime, + dateTimePickerStyle, form, labelStyle, saveButton, scheduleAddModal, selectBoxStyle, + selectTimeStyle, }; diff --git a/frontend/src/components/ScheduleAddModal/ScheduleAddModal.tsx b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.tsx index f8d94c94..cd25851e 100644 --- a/frontend/src/components/ScheduleAddModal/ScheduleAddModal.tsx +++ b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.tsx @@ -16,13 +16,14 @@ import { userState } from '@/recoil/atoms'; import Button from '@/components/@common/Button/Button'; import Fieldset from '@/components/@common/Fieldset/Fieldset'; +import Select from '@/components/@common/Select/Select'; import Spinner from '@/components/@common/Spinner/Spinner'; import { CACHE_KEY } from '@/constants/api'; -import { DATE_TIME } from '@/constants/date'; +import { DATE_TIME, TIMES } from '@/constants/date'; import { VALIDATION_MESSAGE, VALIDATION_SIZE } from '@/constants/validate'; -import { getDate, getDateTime } from '@/utils/date'; +import { getDate, getEndTime, getISODateString, getNextDate, getStartTime } from '@/utils/date'; import categoryApi from '@/api/category'; import scheduleApi from '@/api/schedule'; @@ -33,12 +34,15 @@ import { categorySelect, checkboxStyle, controlButtons, + dateFieldsetStyle, dateTime, + dateTimePickerStyle, form, labelStyle, saveButton, scheduleAddModal, selectBoxStyle, + selectTimeStyle, } from './ScheduleAddModal.styles'; interface ScheduleAddModalProps { @@ -58,11 +62,9 @@ function ScheduleAddModal({ dateInfo, closeModal }: ScheduleAddModalProps) { const { isLoading: isGetCategoryLoading, data } = useQuery< AxiosResponse, AxiosError - >(CACHE_KEY.MY_CATEGORIES, () => categoryApi.getMy(accessToken), { - onSuccess: (data) => onSuccessGetCategories(data), - }); + >(CACHE_KEY.MY_CATEGORIES, () => categoryApi.getMy(accessToken)); - const categoryId = useControlledInput(); + const categoryId = useControlledInput(String(data?.data[0].id)); const { mutate: postSchedule } = useMutation< AxiosResponse<{ schedules: ScheduleType[] }>, @@ -75,19 +77,11 @@ function ScheduleAddModal({ dateInfo, closeModal }: ScheduleAddModalProps) { }, }); - const dateFieldset = isAllDay - ? { - type: 'date', - initialValue: getDate(dateInfo), - } - : { - type: 'datetime-local', - initialValue: getDateTime(dateInfo), - }; - const validationSchedule = useValidateSchedule({ - initialStartDateTime: dateFieldset.initialValue, - initialEndDateTime: dateFieldset.initialValue, + initialStartDate: getDate(dateInfo), + initialStartTime: isAllDay ? DATE_TIME.START : getStartTime(), + initialEndDate: getDate(dateInfo), + initialEndTime: isAllDay ? DATE_TIME.END : getEndTime(), }); const handleClickAllDayButton = () => { @@ -99,28 +93,18 @@ function ScheduleAddModal({ dateInfo, closeModal }: ScheduleAddModalProps) { const body = { title: validationSchedule.title.inputValue, - startDateTime: validationSchedule.startDateTime.inputValue, - endDateTime: validationSchedule.endDateTime.inputValue, + startDateTime: `${validationSchedule.startDate.inputValue}T${validationSchedule.startTime.inputValue}`, + endDateTime: `${ + isAllDay + ? getISODateString( + getNextDate(new Date(validationSchedule.endDate.inputValue), 1).toISOString() + ) + : validationSchedule.endDate.inputValue + }T${validationSchedule.endTime.inputValue}`, memo: validationSchedule.memo.inputValue, }; - if (!isAllDay) { - postSchedule(body); - - return; - } - - const allDayBody = { - ...body, - startDateTime: `${body.startDateTime}T${DATE_TIME.START}`, - endDateTime: `${body.endDateTime}T${DATE_TIME.END}`, - }; - - postSchedule(allDayBody); - }; - - const onSuccessGetCategories = (data: AxiosResponse) => { - categoryId.setInputValue(`${data.data[0].id}`); + postSchedule(body); }; const onSuccessPostSchedule = () => { @@ -152,29 +136,53 @@ function ScheduleAddModal({ dateInfo, closeModal }: ScheduleAddModalProps) { autoFocus labelText="제목" /> -
+
-
+
+
+ {!isAllDay && ( + + )} +
카테고리 diff --git a/frontend/src/components/ScheduleModal/ScheduleModal.tsx b/frontend/src/components/ScheduleModal/ScheduleModal.tsx index 5dd8ce5a..9f7e4e5f 100644 --- a/frontend/src/components/ScheduleModal/ScheduleModal.tsx +++ b/frontend/src/components/ScheduleModal/ScheduleModal.tsx @@ -17,9 +17,12 @@ import { CONFIRM_MESSAGE } from '@/constants/message'; import categoryApi from '@/api/category'; import scheduleApi from '@/api/schedule'; -import { FiCalendar, FiEdit3 } from 'react-icons/fi'; -import { GrClose } from 'react-icons/gr'; -import { RiDeleteBin6Line } from 'react-icons/ri'; +import { + MdClose, + MdDeleteOutline, + MdOutlineCalendarToday, + MdOutlineModeEdit, +} from 'react-icons/md'; import { buttonStyle, @@ -101,23 +104,23 @@ function ScheduleModal({ {canEditSchedule && ( <> )}
- +

{scheduleInfo.title}

diff --git a/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.styles.ts b/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.styles.ts index 60ce89b9..553bb959 100644 --- a/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.styles.ts +++ b/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.styles.ts @@ -14,23 +14,24 @@ const formStyle = ({ flex }: Theme) => css` gap: 6rem; `; -const categoryStyle = ({ colors }: Theme, colorCode: string) => css` - padding: 0 3rem; - - width: 100%; - height: 12rem; - border: 1px solid ${colors.GRAY_500}; - border-radius: 8px; +const dateFieldsetStyle = (isAllDay: boolean) => { + return { + div: css` + width: ${isAllDay ? '100%' : '45%'}; + `, + input: css` + height: 11.75rem; + `, + }; +}; - background: ${colorCode}; +const dateTimePickerStyle = ({ flex }: Theme) => css` + ${flex.row}; - font-size: 5rem; - color: ${colors.WHITE}; - line-height: 12rem; + justify-content: space-between; + align-items: flex-end; - &:hover { - cursor: default; - } + width: 100%; `; const dateTimeStyle = ({ flex }: Theme) => css` @@ -143,16 +144,24 @@ const categoryBoxStyle = ({ flex }: Theme) => css` width: 100%; `; +const selectTimeStyle = { + select: css` + width: 45%; + `, +}; + export { arrowStyle, cancelButtonStyle, - categoryStyle, + categoryBoxStyle, checkboxStyle, controlButtonsStyle, + dateFieldsetStyle, + dateTimePickerStyle, dateTimeStyle, formStyle, labelStyle, modalStyle, saveButtonStyle, - categoryBoxStyle, + selectTimeStyle, }; diff --git a/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.tsx b/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.tsx index 7958598c..d5623973 100644 --- a/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.tsx +++ b/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.tsx @@ -5,6 +5,7 @@ import { useState } from 'react'; import { useMutation, useQuery, useQueryClient } from 'react-query'; import { useRecoilValue } from 'recoil'; +import useControlledInput from '@/hooks/useControlledInput'; import useValidateSchedule from '@/hooks/useValidateSchedule'; import { CategoryType } from '@/@types/category'; @@ -14,12 +15,14 @@ import { userState } from '@/recoil/atoms'; import Button from '@/components/@common/Button/Button'; import Fieldset from '@/components/@common/Fieldset/Fieldset'; +import Select from '@/components/@common/Select/Select'; import { CACHE_KEY } from '@/constants/api'; -import { DATE_TIME } from '@/constants/date'; +import { CATEGORY_TYPE } from '@/constants/category'; +import { DATE_TIME, TIMES } from '@/constants/date'; import { VALIDATION_MESSAGE, VALIDATION_SIZE } from '@/constants/validate'; -import { checkAllDay, getISODateString } from '@/utils/date'; +import { checkAllDay, getBeforeDate, getISODateString, getNextDate } from '@/utils/date'; import categoryApi from '@/api/category'; import scheduleApi from '@/api/schedule'; @@ -28,14 +31,16 @@ import { arrowStyle, cancelButtonStyle, categoryBoxStyle, - categoryStyle, checkboxStyle, controlButtonsStyle, + dateFieldsetStyle, + dateTimePickerStyle, dateTimeStyle, formStyle, labelStyle, modalStyle, saveButtonStyle, + selectTimeStyle, } from './ScheduleModifyModal.styles'; interface ScheduleModifyModalProps { @@ -54,8 +59,9 @@ function ScheduleModifyModal({ scheduleInfo, closeModal }: ScheduleModifyModalPr const queryClient = useQueryClient(); - const { data } = useQuery, AxiosError>(CACHE_KEY.CATEGORY, () => - categoryApi.getSingle(scheduleInfo.categoryId) + const { data: categoriesGetResponse } = useQuery, AxiosError>( + CACHE_KEY.MY_CATEGORIES, + () => categoryApi.getMy(accessToken) ); const { mutate } = useMutation< @@ -67,29 +73,27 @@ function ScheduleModifyModal({ scheduleInfo, closeModal }: ScheduleModifyModalPr onSuccess: () => onSuccessPatchSchedule(), }); + const categoryId = useControlledInput( + categoriesGetResponse?.data.find((category) => category.id === scheduleInfo.categoryId)?.name + ); + const onSuccessPatchSchedule = () => { queryClient.invalidateQueries(CACHE_KEY.SCHEDULES); closeModal(); }; - const getDateFieldsetProps = (dateTime: string) => - isAllDay - ? { - type: 'date', - defaultValue: getISODateString(dateTime), - } - : { - type: 'datetime-local', - defaultValue: dateTime, - }; - - const startDateFieldsetProps = getDateFieldsetProps(scheduleInfo.startDateTime); - const endDateFieldsetProps = getDateFieldsetProps(scheduleInfo.endDateTime); + const [startDate, startTime] = scheduleInfo.startDateTime.split('T'); + const [endDate, endTime] = scheduleInfo.endDateTime.split('T'); const validationSchedule = useValidateSchedule({ initialTitle: scheduleInfo.title, - initialStartDateTime: startDateFieldsetProps.defaultValue, - initialEndDateTime: endDateFieldsetProps.defaultValue, + initialStartDate: startDate, + initialStartTime: startTime.slice(0, 5), + initialEndDate: + isAllDay && endTime.slice(0, 5) === DATE_TIME.END + ? getISODateString(getBeforeDate(new Date(endDate), 1).toISOString()) + : endDate, + initialEndTime: endTime.slice(0, 5), initialMemo: scheduleInfo.memo, }); @@ -98,37 +102,36 @@ function ScheduleModifyModal({ scheduleInfo, closeModal }: ScheduleModifyModalPr const body = { title: validationSchedule.title.inputValue, - startDateTime: validationSchedule.startDateTime.inputValue, - endDateTime: validationSchedule.endDateTime.inputValue, + startDateTime: `${validationSchedule.startDate.inputValue}T${ + isAllDay ? DATE_TIME.START : validationSchedule.startTime.inputValue + }`, + endDateTime: `${ + isAllDay + ? `${getISODateString( + getNextDate(new Date(validationSchedule.endDate.inputValue), 1).toISOString() + )}T${DATE_TIME.END}` + : `${validationSchedule.endDate.inputValue}T${validationSchedule.endTime.inputValue}` + }`, memo: validationSchedule.memo.inputValue, + categoryId: + categoriesGetResponse?.data.find((category) => category.name === categoryId.inputValue) + ?.id || scheduleInfo.categoryId, }; - if (!isAllDay) { - mutate(body); - - return; - } - - const allDayBody = { - ...body, - startDateTime: `${body.startDateTime}T${DATE_TIME.START}`, - endDateTime: `${body.endDateTime}T${DATE_TIME.END}`, - }; - - mutate(allDayBody); + mutate(body); }; const handleClickAllDayButton = () => { setAllDay((prev) => !prev); }; + const categories = categoriesGetResponse?.data + .filter((category) => category.categoryType !== CATEGORY_TYPE.GOOGLE) + .map((category) => category.name); + return (

-
-
카테고리
-
{data?.data.name}
-
-
+
-
+
+
+ {!isAllDay && ( + + )} +
+ {categories && ( +
+
카테고리
+ - {!subscriptions.length && } - {subscriptions.map((subscription) => ( - - ))} - -
-
-
- -
-
- - -
- {schedulingGetResponse && - schedulingGetResponse.data.map((schedule) => ( -
-
{formatDateTime(schedule.startDateTime)}
- -
{formatDateTime(schedule.endDateTime)}
-
- ))} -
-
- - ); -} - -export default SchedulingPage; diff --git a/frontend/src/pages/StartPage/StartPage.styles.ts b/frontend/src/pages/StartPage/StartPage.styles.ts index bd0ab98d..cdaadc38 100644 --- a/frontend/src/pages/StartPage/StartPage.styles.ts +++ b/frontend/src/pages/StartPage/StartPage.styles.ts @@ -46,7 +46,7 @@ const dateItemStyle = ({ colors }: Theme) => css` const itemStyle = css` position: relative; - left: -100%; + transform: translateX(-100%); width: 200rem; height: 50rem; @@ -61,11 +61,11 @@ const itemStyle = css` @keyframes slideIn { from { opacity: 0; - left: -100%; + transform: translateX(-100%); } to { - left: 0; + transform: translateX(0); opacity: 1; } } @@ -167,7 +167,7 @@ const mainContentStyle = ({ flex }: Theme) => css` gap: 6rem; `; -const firstSectionStyle = ({ flex }: Theme) => css` +const sectionStyle = ({ flex }: Theme) => css` ${flex.row}; align-items: flex-start; @@ -190,76 +190,6 @@ const secondSectionStyle = ({ flex, colors }: Theme) => css` const pageStyle = ({ flex }: Theme) => css` ${flex.column}; - - overflow-x: hidden; -`; - -const methodHeaderStyle = ({ colors }: Theme) => css` - margin-top: 10rem; - - font-size: 5rem; - color: ${colors.GRAY_500}; -`; - -const methodTextStyle = ({ colors }: Theme) => css` - font-size: 6rem; - line-height: 10rem; - color: ${colors.GRAY_500}; -`; - -const methodItemStyle = ({ flex }: Theme, isShowing: boolean, direction: 'left' | 'right') => css` - ${flex.row}; - - position: relative; - left: ${direction === 'left' ? '-100%' : '100%'}; - gap: 20rem; - - width: 70%; - height: 100%; - padding: 20rem; - - opacity: 0; - - @keyframes slideInFromLeft { - from { - opacity: 0; - left: -100%; - } - - to { - left: 0; - opacity: 1; - } - } - - @keyframes slideInFromRight { - from { - opacity: 0; - left: 100%; - } - - to { - left: 0; - opacity: 1; - } - } - - ${isShowing && - css` - animation-name: ${direction === 'left' ? 'slideInFromLeft' : 'slideInFromRight'}; - animation-duration: 0.7s; - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - `} -`; - -const imageStyle = css` - width: 120rem; - height: auto; -`; - -const refStyle = css` - height: 1rem; `; export { @@ -268,19 +198,14 @@ export { detailTextStyle, dateItemStyle, firstItemStyle, - firstSectionStyle, googleLoginButton, loginText, - imageStyle, introductionStyle, mainContentStyle, - methodHeaderStyle, - methodItemStyle, - methodTextStyle, pageStyle, - refStyle, secondItemStyle, secondSectionStyle, + sectionStyle, thirdItemStyle, whiteTextStyle, }; diff --git a/frontend/src/pages/StartPage/StartPage.tsx b/frontend/src/pages/StartPage/StartPage.tsx index 49a41e18..50bcf76b 100644 --- a/frontend/src/pages/StartPage/StartPage.tsx +++ b/frontend/src/pages/StartPage/StartPage.tsx @@ -1,9 +1,6 @@ import { useTheme } from '@emotion/react'; -import { useState } from 'react'; import { useQuery } from 'react-query'; -import useIntersect from '@/hooks/useIntersect'; - import Button from '@/components/@common/Button/Button'; import PageLayout from '@/components/@common/PageLayout/PageLayout'; import Footer from '@/components/Footer/Footer'; @@ -16,28 +13,19 @@ import loginApi from '@/api/login'; import { FcGoogle } from 'react-icons/fc'; -import howToUse1 from '../../assets/how_to_use_1.png'; -import howToUse2 from '../../assets/how_to_use_2.png'; -import howToUse3 from '../../assets/how_to_use_3.png'; import { blackTextStyle, calendarStyle, dateItemStyle, detailTextStyle, firstItemStyle, - firstSectionStyle, googleLoginButton, - imageStyle, introductionStyle, loginText, mainContentStyle, - methodHeaderStyle, - methodItemStyle, - methodTextStyle, pageStyle, - refStyle, secondItemStyle, - secondSectionStyle, + sectionStyle, thirdItemStyle, whiteTextStyle, } from './StartPage.styles'; @@ -45,24 +33,6 @@ import { function StartPage() { const theme = useTheme(); - const [methodAnimation, setMethodAnimation] = useState([false, false, false]); - - const baseMethod = useIntersect(() => { - setMethodAnimation([false, false, false]); - }); - - const firstMethod = useIntersect(() => { - setMethodAnimation([true, false, false]); - }); - - const secondMethod = useIntersect(() => { - setMethodAnimation([true, true, false]); - }); - - const thirdMethod = useIntersect(() => { - setMethodAnimation([true, true, true]); - }); - const { error, refetch } = useQuery(CACHE_KEY.ENTER, loginApi.getUrl, { enabled: false, onSuccess: (data) => onSuccessGetLoginUrl(data), @@ -83,14 +53,13 @@ function StartPage() { return (
-
+
{getThisDate()}
운동 일정
스터디 일정
동아리 일정
-
@@ -117,33 +86,6 @@ function StartPage() {
-
- 달록은 이렇습니다 -
-
- 달록 사용법 1 - - 공유 받고 싶은 카테고리를 구독하여
내 달력에서 일정을 볼 수 있습니다. -
-
-
-
- - 구글 캘린더에서 원하는 캘린더를 -
가져올 수 있습니다. -
- 달록 사용법 2 -
-
-
- 달록 사용법 3 - - 카테고리 내에서 팀원들과
- 겹치지 않는 시간을
- 확인할 수 있습니다. -
-
-
diff --git a/frontend/src/styles/GlobalStyle.tsx b/frontend/src/styles/GlobalStyle.tsx index efdf9a00..21ef8c0c 100644 --- a/frontend/src/styles/GlobalStyle.tsx +++ b/frontend/src/styles/GlobalStyle.tsx @@ -2,8 +2,6 @@ import { css, Global, Theme } from '@emotion/react'; import emotionReset from 'emotion-reset'; const global = ({ colors }: Theme) => css` - @import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.4/dist/web/static/pretendard.css'); - ${emotionReset} *, @@ -17,7 +15,9 @@ const global = ({ colors }: Theme) => css` body { overflow: overlay; - font-family: 'Pretendard', sans-serif; + font-family: Pretendard, -apple-system, BlinkMacSystemFont, system-ui, Roboto, 'Helvetica Neue', + 'Segoe UI', 'Apple SD Gothic Neo', 'Noto Sans KR', 'Malgun Gothic', 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', sans-serif; font-size: 3rem; *::-webkit-scrollbar { diff --git a/frontend/src/utils/date.ts b/frontend/src/utils/date.ts index 4b071421..8face6af 100644 --- a/frontend/src/utils/date.ts +++ b/frontend/src/utils/date.ts @@ -4,12 +4,15 @@ import { DATE_TIME } from '@/constants/date'; import { zeroFill } from '.'; -const checkAllDay = (startDateTime: string | undefined, endDateTime: string | undefined) => { - if (startDateTime === undefined || endDateTime === undefined) { +const checkAllDay = (startDateTime?: string, endDateTime?: string) => { + if (startDateTime === undefined || endDateTime === undefined || startDateTime === endDateTime) { return null; } - return startDateTime.includes(DATE_TIME.START) && endDateTime.includes(DATE_TIME.END); + return ( + startDateTime.startsWith(DATE_TIME.START, DATE_TIME.START_INDEX) && + endDateTime.startsWith(DATE_TIME.END, DATE_TIME.START_INDEX) + ); }; const getBeforeDate = (targetDay: Date, offset: number) => @@ -62,8 +65,8 @@ const getCalendarMonth = (year: number, month: number) => { }); }; -const getDate = (dateInfo: Omit | null) => { - if (dateInfo === null) { +const getDate = (dateInfo?: Omit) => { + if (!dateInfo) { return getISODateString(new Date(+new Date() + 3240 * 10000).toISOString()); } @@ -72,8 +75,8 @@ const getDate = (dateInfo: Omit | null) => { return getISODateString(new Date(+new Date(year, month - 1, date) + 3240 * 10000).toISOString()); }; -const getDateTime = (dateInfo: Omit | null) => { - if (dateInfo === null) { +const getDateTime = (dateInfo?: Omit) => { + if (!dateInfo) { return new Date(+new Date() + 3240 * 10000).toISOString().replace(/\..*/, '').slice(0, -3); } @@ -85,6 +88,25 @@ const getDateTime = (dateInfo: Omit | null) => { .slice(0, -3); }; +const getStartTime = () => { + const [nowHour, nowMinute] = getDateTime().split('T')[1].split(':'); + + if (nowMinute === '00' || nowMinute === '30') return `${zeroFill(nowHour)}:${nowMinute}`; + + if (nowMinute < '30') return `${zeroFill(nowHour)}:30`; + + if (nowHour >= '23') return '00:00'; + + return `${zeroFill(+nowHour + 1)}:00`; +}; + +const getEndTime = (startTime?: string) => { + const [nowHour, nowMinute] = + startTime === undefined ? getStartTime().split(':') : startTime.split(':'); + + return nowHour < '23' ? `${zeroFill(+nowHour + 1)}:${nowMinute}` : `00:${nowMinute}`; +}; + const getDayFromFormattedDate = (date: string) => { return new Date(date).getDay(); }; @@ -97,6 +119,10 @@ const getISODateString = (ISOString: string) => { return ISOString.split('T')[0]; }; +const getISOTimeString = (ISOString: string) => { + return ISOString.split('T')[1]; +}; + const getKoreaISOString = (time: number) => { return new Date(time - new Date().getTimezoneOffset() * 60000).toISOString(); }; @@ -113,7 +139,7 @@ const getNextYearMonth = (targetYear: number, targetMonth: number) => { }; const getOneHourEarlierISOString = (ISOString: string) => { - const hour = ISOString.split('T')[1].split(':')[0]; + const hour = getISOTimeString(ISOString).split(':')[0]; const oneHourEarlierISOString = getKoreaISOString(new Date(ISOString).setHours(Number(hour) - 1)) .replace(/\..*/, '') @@ -123,7 +149,7 @@ const getOneHourEarlierISOString = (ISOString: string) => { }; const getOneHourLaterISOString = (ISOString: string) => { - const hour = ISOString.split('T')[1].split(':')[0]; + const hour = getISOTimeString(ISOString).split(':')[0]; const oneHourEarlierISOString = getKoreaISOString(new Date(ISOString).setHours(Number(hour) + 1)) .replace(/\..*/, '') @@ -152,13 +178,16 @@ export { getDate, getDateTime, getDayFromFormattedDate, + getEndTime, getFormattedDate, getISODateString, + getISOTimeString, getKoreaISOString, getNextDate, getNextYearMonth, getOneHourEarlierISOString, getOneHourLaterISOString, + getStartTime, getThisDate, getThisMonth, getThisYear, diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index bf706698..75333fff 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -1,11 +1,14 @@ -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const path = require('path'); const webpack = require('webpack'); -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); +const path = require('path'); const Dotenv = require('dotenv-webpack'); +const { CleanWebpackPlugin } = require('clean-webpack-plugin'); -const prod = process.env.NODE_ENV === 'production'; +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const prod = process.env.NODE_ENV === 'production'; module.exports = { mode: prod ? 'production' : 'development', devtool: prod ? 'hidden-source-map' : 'eval', @@ -24,7 +27,7 @@ module.exports = { }, { test: /\\.css$/, - use: ['style-loader', 'css-loader'], + use: [prod ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader'], }, { test: /\.(png|jp(e*)g|gif)$/, @@ -53,10 +56,18 @@ module.exports = { }), new CleanWebpackPlugin(), new Dotenv(), + new MiniCssExtractPlugin(), + new BundleAnalyzerPlugin({ + analyzerMode: 'disabled', + generateStatsFile: true, + }), ], devServer: { historyApiFallback: true, port: 3000, hot: true, }, + optimization: { + minimizer: ['...', new CssMinimizerPlugin()], + }, }; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 69d0c369..4a82d1db 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1865,6 +1865,11 @@ schema-utils "^3.0.0" source-map "^0.7.3" +"@polka/url@^1.0.0-next.20": + version "1.0.0-next.21" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" + integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== + "@sinclair/typebox@^0.24.1": version "0.24.37" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.37.tgz#3ea4cf8f3cf8a943c17baf5bb7b33587afa5f76b" @@ -2884,6 +2889,11 @@ javascript-natural-sort "0.7.1" lodash "4.17.21" +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + "@types/aria-query@^4.2.0": version "4.2.2" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" @@ -3756,6 +3766,11 @@ acorn-walk@^7.1.1, acorn-walk@^7.2.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-walk@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" @@ -3766,7 +3781,7 @@ acorn@^7.1.1, acorn@^7.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: +acorn@^8.0.4, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: version "8.8.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== @@ -4573,6 +4588,16 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" +browserslist@^4.0.0, browserslist@^4.16.6, browserslist@^4.20.3: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== + dependencies: + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" + node-releases "^2.0.6" + update-browserslist-db "^1.0.9" + browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.20.2, browserslist@^4.21.3: version "4.21.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" @@ -4764,6 +4789,21 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001400: + version "1.0.30001407" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001407.tgz#92281a6ee67cb90bfd8a6a1201fcc2dc19b60a15" + integrity sha512-4ydV+t4P7X3zH83fQWNDX/mQEzYomossfpViCOx9zHBSMV+rIe3LFqglHHtVyvNl1FhTNxPxs3jei82iqOW04w== + caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001370: version "1.0.30001390" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001390.tgz#158a43011e7068ef7fc73590e9fd91a7cece5e7f" @@ -5060,6 +5100,11 @@ color-support@^1.1.2: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== +colord@^2.9.1: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + colorette@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" @@ -5097,7 +5142,7 @@ commander@^6.2.1: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== -commander@^7.0.0: +commander@^7.0.0, commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== @@ -5366,6 +5411,11 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +css-declaration-sorter@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz#be5e1d71b7a992433fb1c542c7a1b835e45682ec" + integrity sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w== + css-loader@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" @@ -5415,6 +5465,18 @@ css-loader@^6.7.1: postcss-value-parser "^4.2.0" semver "^7.3.5" +css-minimizer-webpack-plugin@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.1.0.tgz#2ab9f7d8148c48f5d498604025e6e62cf9528855" + integrity sha512-Zd+yz4nta4GXi3pMqF6skO8kjzuCUbr62z8SLMGZZtxWxTGTLopOiabPGNDEyjHCRhnhdA1EfHmqLa2Oekjtng== + dependencies: + cssnano "^5.1.8" + jest-worker "^27.5.1" + postcss "^8.4.13" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + css-select@^4.1.3: version "4.3.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" @@ -5426,6 +5488,14 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + css-what@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" @@ -5436,6 +5506,62 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +cssnano-preset-default@^5.2.12: + version "5.2.12" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz#ebe6596ec7030e62c3eb2b3c09f533c0644a9a97" + integrity sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew== + dependencies: + css-declaration-sorter "^6.3.0" + cssnano-utils "^3.1.0" + postcss-calc "^8.2.3" + postcss-colormin "^5.3.0" + postcss-convert-values "^5.1.2" + postcss-discard-comments "^5.1.2" + postcss-discard-duplicates "^5.1.0" + postcss-discard-empty "^5.1.1" + postcss-discard-overridden "^5.1.0" + postcss-merge-longhand "^5.1.6" + postcss-merge-rules "^5.1.2" + postcss-minify-font-values "^5.1.0" + postcss-minify-gradients "^5.1.1" + postcss-minify-params "^5.1.3" + postcss-minify-selectors "^5.2.1" + postcss-normalize-charset "^5.1.0" + postcss-normalize-display-values "^5.1.0" + postcss-normalize-positions "^5.1.1" + postcss-normalize-repeat-style "^5.1.1" + postcss-normalize-string "^5.1.0" + postcss-normalize-timing-functions "^5.1.0" + postcss-normalize-unicode "^5.1.0" + postcss-normalize-url "^5.1.0" + postcss-normalize-whitespace "^5.1.1" + postcss-ordered-values "^5.1.3" + postcss-reduce-initial "^5.1.0" + postcss-reduce-transforms "^5.1.0" + postcss-svgo "^5.1.0" + postcss-unique-selectors "^5.1.1" + +cssnano-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" + integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== + +cssnano@^5.1.8: + version "5.1.13" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.13.tgz#83d0926e72955332dc4802a7070296e6258efc0a" + integrity sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ== + dependencies: + cssnano-preset-default "^5.2.12" + lilconfig "^2.0.3" + yaml "^1.10.2" + +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + cssom@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" @@ -5811,6 +5937,11 @@ dotenv@^8.0.0, dotenv@^8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + duplexify@^3.4.2, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -5831,6 +5962,11 @@ electron-to-chromium@^1.4.202: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.242.tgz#51284820b0e6f6ce6c60d3945a3c4f9e4bd88f5f" integrity sha512-nPdgMWtjjWGCtreW/2adkrB2jyHjClo9PtVhR6rW+oxa4E4Wom642Tn+5LslHP3XPL5MCpkn5/UEY60EXylNeQ== +electron-to-chromium@^1.4.251: + version "1.4.256" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz#c735032f412505e8e0482f147a8ff10cfca45bf4" + integrity sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw== + elliptic@^6.5.3: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -7035,6 +7171,13 @@ graphql@^16.3.0: resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + hamt_plus@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/hamt_plus/-/hamt_plus-1.0.2.tgz#e21c252968c7e33b20f6a1b094cd85787a265601" @@ -8523,7 +8666,7 @@ jest-worker@^26.5.0, jest-worker@^26.6.2: merge-stream "^2.0.0" supports-color "^7.0.0" -jest-worker@^27.4.5: +jest-worker@^27.4.5, jest-worker@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== @@ -8754,6 +8897,11 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lilconfig@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" + integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -8825,12 +8973,17 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.uniq@4.5.0: +lodash.uniq@4.5.0, lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== @@ -8995,6 +9148,11 @@ mdast-util-to-string@^1.0.0: resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + mdurl@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -9168,6 +9326,13 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +mini-css-extract-plugin@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz#9a1251d15f2035c342d99a468ab9da7a0451b71e" + integrity sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg== + dependencies: + schema-utils "^4.0.0" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -9274,6 +9439,11 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" +mrmime@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" + integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -9485,6 +9655,11 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -9677,6 +9852,11 @@ open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -10119,6 +10299,52 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +postcss-calc@^8.2.3: + version "8.2.4" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" + integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== + dependencies: + postcss-selector-parser "^6.0.9" + postcss-value-parser "^4.2.0" + +postcss-colormin@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.0.tgz#3cee9e5ca62b2c27e84fce63affc0cfb5901956a" + integrity sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz#31586df4e184c2e8890e8b34a0b9355313f503ab" + integrity sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g== + dependencies: + browserslist "^4.20.3" + postcss-value-parser "^4.2.0" + +postcss-discard-comments@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" + integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== + +postcss-discard-duplicates@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" + integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== + +postcss-discard-empty@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" + integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== + +postcss-discard-overridden@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" + integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== + postcss-flexbugs-fixes@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.1.tgz#9218a65249f30897deab1033aced8578562a6690" @@ -10137,6 +10363,56 @@ postcss-loader@^4.2.0: schema-utils "^3.0.0" semver "^7.3.4" +postcss-merge-longhand@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz#f378a8a7e55766b7b644f48e5d8c789ed7ed51ce" + integrity sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^5.1.0" + +postcss-merge-rules@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz#7049a14d4211045412116d79b751def4484473a5" + integrity sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + cssnano-utils "^3.1.0" + postcss-selector-parser "^6.0.5" + +postcss-minify-font-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" + integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" + integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== + dependencies: + colord "^2.9.1" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz#ac41a6465be2db735099bbd1798d85079a6dc1f9" + integrity sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg== + dependencies: + browserslist "^4.16.6" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" + integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== + dependencies: + postcss-selector-parser "^6.0.5" + postcss-modules-extract-imports@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" @@ -10198,7 +10474,93 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: +postcss-normalize-charset@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" + integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== + +postcss-normalize-display-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" + integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" + integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" + integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" + integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" + integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz#3d23aede35e160089a285e27bf715de11dc9db75" + integrity sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ== + dependencies: + browserslist "^4.16.6" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" + integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== + dependencies: + normalize-url "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" + integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" + integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-reduce-initial@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz#fc31659ea6e85c492fb2a7b545370c215822c5d6" + integrity sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" + integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: version "6.0.10" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== @@ -10206,6 +10568,21 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-svgo@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" + integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^2.7.0" + +postcss-unique-selectors@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" + integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== + dependencies: + postcss-selector-parser "^6.0.5" + postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" @@ -10219,7 +10596,7 @@ postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0 picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.2.15, postcss@^8.4.7: +postcss@^8.2.15, postcss@^8.4.13, postcss@^8.4.7: version "8.4.16" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c" integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ== @@ -11332,6 +11709,15 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +sirv@^1.0.7: + version "1.0.19" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" + integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== + dependencies: + "@polka/url" "^1.0.0-next.20" + mrmime "^1.0.0" + totalist "^1.0.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -11776,6 +12162,14 @@ style-to-object@0.3.0, style-to-object@^0.3.0: dependencies: inline-style-parser "0.1.1" +stylehacks@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520" + integrity sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q== + dependencies: + browserslist "^4.16.6" + postcss-selector-parser "^6.0.4" + stylis@4.0.13: version "4.0.13" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" @@ -11815,6 +12209,19 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svgo@^2.7.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -12037,6 +12444,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + tough-cookie@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" @@ -12396,6 +12808,14 @@ update-browserslist-db@^1.0.5: escalade "^3.1.1" picocolors "^1.0.0" +update-browserslist-db@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" + integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -12616,6 +13036,21 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== +webpack-bundle-analyzer@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.1.tgz#bee2ee05f4ba4ed430e4831a319126bb4ed9f5a6" + integrity sha512-oKz9Oz9j3rUciLNfpGFjOb49/jEpXNmWdVH8Ls//zNcnLlQdTGXQQMsBbb/gR7Zl8WNLxVCq+0Hqbx3zv6twBw== + dependencies: + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^7.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + webpack-cli@^4.10.0: version "4.10.0" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" @@ -12967,6 +13402,11 @@ write-file-atomic@^4.0.1: imurmurhash "^0.1.4" signal-exit "^3.0.7" +ws@^7.3.1: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + ws@^8.2.3, ws@^8.4.2: version "8.8.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" @@ -13014,7 +13454,7 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0, yaml@^1.7.2: +yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==