Showing preview only (1,012K chars total). Download the full file or copy to clipboard to get everything.
Repository: woowacourse-teams/2022-dallog
Branch: develop
Commit: 111ee13e44c3
Files: 426
Total size: 811.5 KB
Directory structure:
gitextract_eag9zh7a/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-template.md
│ │ └── feature-template.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── backend-cd.yml
│ ├── backend-ci.yml
│ └── frontend-ci.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── backend/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── src/
│ ├── docs/
│ │ └── asciidoc/
│ │ ├── auth.adoc
│ │ ├── category.adoc
│ │ ├── external-calendar.adoc
│ │ ├── index.adoc
│ │ ├── member.adoc
│ │ ├── schedule.adoc
│ │ └── subscription.adoc
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── allog/
│ │ │ └── dallog/
│ │ │ ├── DallogApplication.java
│ │ │ ├── auth/
│ │ │ │ ├── application/
│ │ │ │ │ ├── AuthService.java
│ │ │ │ │ ├── AuthTokenCreator.java
│ │ │ │ │ ├── JwtTokenProvider.java
│ │ │ │ │ ├── OAuthClient.java
│ │ │ │ │ ├── OAuthUri.java
│ │ │ │ │ ├── TokenCreator.java
│ │ │ │ │ └── TokenProvider.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── AuthToken.java
│ │ │ │ │ ├── InMemoryAuthTokenRepository.java
│ │ │ │ │ ├── OAuthToken.java
│ │ │ │ │ ├── OAuthTokenRepository.java
│ │ │ │ │ └── TokenRepository.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── LoginMember.java
│ │ │ │ │ ├── OAuthMember.java
│ │ │ │ │ ├── request/
│ │ │ │ │ │ ├── TokenRenewalRequest.java
│ │ │ │ │ │ └── TokenRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ ├── AccessAndRefreshTokenResponse.java
│ │ │ │ │ ├── AccessTokenResponse.java
│ │ │ │ │ ├── OAuthAccessTokenResponse.java
│ │ │ │ │ └── OAuthUriResponse.java
│ │ │ │ ├── event/
│ │ │ │ │ └── MemberSavedEvent.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── EmptyAuthorizationHeaderException.java
│ │ │ │ │ ├── InvalidTokenException.java
│ │ │ │ │ ├── NoPermissionException.java
│ │ │ │ │ ├── NoSuchOAuthTokenException.java
│ │ │ │ │ └── NoSuchTokenException.java
│ │ │ │ └── presentation/
│ │ │ │ ├── AuthController.java
│ │ │ │ ├── AuthenticationPrincipal.java
│ │ │ │ ├── AuthenticationPrincipalArgumentResolver.java
│ │ │ │ └── AuthorizationExtractor.java
│ │ │ ├── category/
│ │ │ │ ├── application/
│ │ │ │ │ ├── CategoryService.java
│ │ │ │ │ └── ExternalCategoryDetailService.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Category.java
│ │ │ │ │ ├── CategoryRepository.java
│ │ │ │ │ ├── CategoryType.java
│ │ │ │ │ ├── ExternalCategoryDetail.java
│ │ │ │ │ └── ExternalCategoryDetailRepository.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/
│ │ │ │ │ │ ├── CategoryCreateRequest.java
│ │ │ │ │ │ ├── CategoryUpdateRequest.java
│ │ │ │ │ │ └── ExternalCategoryCreateRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ ├── CategoriesResponse.java
│ │ │ │ │ ├── CategoryDetailResponse.java
│ │ │ │ │ └── CategoryResponse.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── ExistExternalCategoryException.java
│ │ │ │ │ ├── InvalidCategoryException.java
│ │ │ │ │ ├── NoSuchCategoryException.java
│ │ │ │ │ └── NoSuchExternalCategoryDetailException.java
│ │ │ │ └── presentaion/
│ │ │ │ └── CategoryController.java
│ │ │ ├── categoryrole/
│ │ │ │ ├── application/
│ │ │ │ │ └── CategoryRoleService.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── CategoryAuthority.java
│ │ │ │ │ ├── CategoryRole.java
│ │ │ │ │ ├── CategoryRoleRepository.java
│ │ │ │ │ └── CategoryRoleType.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/
│ │ │ │ │ │ └── CategoryRoleUpdateRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ ├── MemberWithRoleTypeResponse.java
│ │ │ │ │ └── SubscribersResponse.java
│ │ │ │ └── exception/
│ │ │ │ ├── ManagingCategoryLimitExcessException.java
│ │ │ │ ├── NoCategoryAuthorityException.java
│ │ │ │ ├── NoSuchCategoryRoleException.java
│ │ │ │ └── NotAbleToChangeRoleException.java
│ │ │ ├── externalcalendar/
│ │ │ │ ├── application/
│ │ │ │ │ ├── ExternalCalendarClient.java
│ │ │ │ │ └── ExternalCalendarService.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── ExternalCalendar.java
│ │ │ │ │ └── ExternalCalendarsResponse.java
│ │ │ │ └── presentation/
│ │ │ │ └── ExternalCalendarController.java
│ │ │ ├── global/
│ │ │ │ ├── config/
│ │ │ │ │ ├── JpaConfig.java
│ │ │ │ │ ├── PropertiesConfig.java
│ │ │ │ │ ├── WebConfig.java
│ │ │ │ │ ├── cache/
│ │ │ │ │ │ ├── CacheConfig.java
│ │ │ │ │ │ └── ExpiringConcurrentMapCache.java
│ │ │ │ │ ├── properties/
│ │ │ │ │ │ └── GoogleProperties.java
│ │ │ │ │ └── replication/
│ │ │ │ │ ├── DataSourceConfiguration.java
│ │ │ │ │ ├── DataSourceKey.java
│ │ │ │ │ ├── RandomReplicaKeys.java
│ │ │ │ │ └── RoutingDataSource.java
│ │ │ │ ├── entity/
│ │ │ │ │ └── BaseEntity.java
│ │ │ │ └── error/
│ │ │ │ ├── ControllerAdvice.java
│ │ │ │ └── dto/
│ │ │ │ ├── ErrorReportRequest.java
│ │ │ │ └── ErrorResponse.java
│ │ │ ├── infrastructure/
│ │ │ │ ├── log/
│ │ │ │ │ ├── DiscordAppender.java
│ │ │ │ │ └── dto/
│ │ │ │ │ ├── DiscordWebhookRequest.java
│ │ │ │ │ ├── Embed.java
│ │ │ │ │ └── Field.java
│ │ │ │ └── oauth/
│ │ │ │ ├── client/
│ │ │ │ │ ├── GoogleExternalCalendarClient.java
│ │ │ │ │ └── GoogleOAuthClient.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── GoogleCalendarEventResponse.java
│ │ │ │ │ ├── GoogleCalendarEventsResponse.java
│ │ │ │ │ ├── GoogleCalendarListResponse.java
│ │ │ │ │ ├── GoogleCalendarResponse.java
│ │ │ │ │ ├── GoogleDateFormat.java
│ │ │ │ │ ├── GoogleTokenResponse.java
│ │ │ │ │ └── UserInfo.java
│ │ │ │ ├── exception/
│ │ │ │ │ └── OAuthException.java
│ │ │ │ └── uri/
│ │ │ │ ├── DevGoogleOAuthUri.java
│ │ │ │ └── GoogleOAuthUri.java
│ │ │ ├── member/
│ │ │ │ ├── application/
│ │ │ │ │ └── MemberService.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Member.java
│ │ │ │ │ ├── MemberRepository.java
│ │ │ │ │ └── SocialType.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/
│ │ │ │ │ │ └── MemberUpdateRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ └── MemberResponse.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── InvalidMemberException.java
│ │ │ │ │ └── NoSuchMemberException.java
│ │ │ │ └── presentation/
│ │ │ │ └── MemberController.java
│ │ │ ├── schedule/
│ │ │ │ ├── application/
│ │ │ │ │ ├── CheckedSchedulesFinder.java
│ │ │ │ │ └── ScheduleService.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── IntegrationSchedule.java
│ │ │ │ │ ├── IntegrationScheduleComparator.java
│ │ │ │ │ ├── IntegrationSchedules.java
│ │ │ │ │ ├── Period.java
│ │ │ │ │ ├── Schedule.java
│ │ │ │ │ ├── ScheduleRepository.java
│ │ │ │ │ ├── ScheduleType.java
│ │ │ │ │ ├── TypedSchedules.java
│ │ │ │ │ └── scheduler/
│ │ │ │ │ └── Scheduler.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── MaterialToFindSchedules.java
│ │ │ │ │ ├── request/
│ │ │ │ │ │ ├── DateRangeRequest.java
│ │ │ │ │ │ ├── ScheduleCreateRequest.java
│ │ │ │ │ │ └── ScheduleUpdateRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ ├── IntegrationScheduleResponse.java
│ │ │ │ │ ├── IntegrationScheduleResponses.java
│ │ │ │ │ └── ScheduleResponse.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── InvalidScheduleException.java
│ │ │ │ │ └── NoSuchScheduleException.java
│ │ │ │ └── presentation/
│ │ │ │ └── ScheduleController.java
│ │ │ └── subscription/
│ │ │ ├── application/
│ │ │ │ ├── ColorPicker.java
│ │ │ │ ├── RandomColorPicker.java
│ │ │ │ └── SubscriptionService.java
│ │ │ ├── domain/
│ │ │ │ ├── Color.java
│ │ │ │ ├── Subscription.java
│ │ │ │ ├── SubscriptionRepository.java
│ │ │ │ └── Subscriptions.java
│ │ │ ├── dto/
│ │ │ │ ├── request/
│ │ │ │ │ └── SubscriptionUpdateRequest.java
│ │ │ │ └── response/
│ │ │ │ ├── SubscriptionResponse.java
│ │ │ │ └── SubscriptionsResponse.java
│ │ │ ├── exception/
│ │ │ │ ├── ExistSubscriptionException.java
│ │ │ │ ├── InvalidSubscriptionException.java
│ │ │ │ ├── NoSuchSubscriptionException.java
│ │ │ │ └── NotAbleToUnsubscribeException.java
│ │ │ └── presentation/
│ │ │ └── SubscriptionController.java
│ │ └── resources/
│ │ ├── application-test.yml
│ │ ├── db/
│ │ │ └── prod/
│ │ │ └── schema.sql
│ │ └── logback-spring.xml
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── allog/
│ │ └── dallog/
│ │ ├── acceptance/
│ │ │ ├── AcceptanceTest.java
│ │ │ ├── AuthAcceptanceTest.java
│ │ │ ├── CategoryAcceptanceTest.java
│ │ │ ├── ExternalCalendarAcceptanceTest.java
│ │ │ ├── MemberAcceptanceTest.java
│ │ │ ├── ScheduleAcceptanceTest.java
│ │ │ ├── SubscriptionAcceptanceTest.java
│ │ │ └── fixtures/
│ │ │ ├── AuthAcceptanceFixtures.java
│ │ │ ├── CategoryAcceptanceFixtures.java
│ │ │ ├── CommonAcceptanceFixtures.java
│ │ │ ├── MemberAcceptanceFixtures.java
│ │ │ ├── ScheduleAcceptanceFixtures.java
│ │ │ └── SubscriptionAcceptanceFixtures.java
│ │ ├── auth/
│ │ │ ├── application/
│ │ │ │ ├── AuthServiceTest.java
│ │ │ │ ├── AuthTokenCreatorTest.java
│ │ │ │ ├── JwtTokenProviderTest.java
│ │ │ │ └── StubTokenProvider.java
│ │ │ ├── domain/
│ │ │ │ ├── AuthTokenTest.java
│ │ │ │ ├── InMemoryAuthTokenRepositoryTest.java
│ │ │ │ ├── OAuthTokenRepositoryTest.java
│ │ │ │ └── OAuthTokenTest.java
│ │ │ └── presentation/
│ │ │ └── AuthControllerTest.java
│ │ ├── category/
│ │ │ ├── application/
│ │ │ │ ├── CategoryServiceTest.java
│ │ │ │ └── ExternalCategoryDetailServiceTest.java
│ │ │ ├── domain/
│ │ │ │ ├── CategoryRepositoryTest.java
│ │ │ │ ├── CategoryTest.java
│ │ │ │ ├── CategoryTypeTest.java
│ │ │ │ └── ExternalCategoryDetailRepositoryTest.java
│ │ │ └── presentation/
│ │ │ └── CategoryControllerTest.java
│ │ ├── categoryrole/
│ │ │ ├── application/
│ │ │ │ └── CategoryRoleServiceTest.java
│ │ │ └── domain/
│ │ │ ├── CategoryRoleRepositoryTest.java
│ │ │ ├── CategoryRoleTest.java
│ │ │ └── CategoryRoleTypeTest.java
│ │ ├── common/
│ │ │ ├── Constants.java
│ │ │ ├── DatabaseCleaner.java
│ │ │ ├── annotation/
│ │ │ │ ├── ControllerTest.java
│ │ │ │ ├── RepositoryTest.java
│ │ │ │ └── ServiceTest.java
│ │ │ ├── builder/
│ │ │ │ ├── BuilderSupporter.java
│ │ │ │ └── GivenBuilder.java
│ │ │ ├── config/
│ │ │ │ ├── ExternalApiConfig.java
│ │ │ │ └── TokenConfig.java
│ │ │ └── fixtures/
│ │ │ ├── AuthFixtures.java
│ │ │ ├── CategoryFixtures.java
│ │ │ ├── ExternalCalendarFixtures.java
│ │ │ ├── ExternalCategoryFixtures.java
│ │ │ ├── IntegrationScheduleFixtures.java
│ │ │ ├── MemberFixtures.java
│ │ │ ├── OAuthFixtures.java
│ │ │ ├── OAuthTokenFixtures.java
│ │ │ ├── ScheduleFixtures.java
│ │ │ └── SubscriptionFixtures.java
│ │ ├── externalcalendar/
│ │ │ ├── application/
│ │ │ │ └── ExternalCalendarServiceTest.java
│ │ │ └── presentation/
│ │ │ └── ExternalCalendarControllerTest.java
│ │ ├── infrastructure/
│ │ │ └── oauth/
│ │ │ ├── client/
│ │ │ │ ├── StubExternalCalendarClient.java
│ │ │ │ └── StubOAuthClient.java
│ │ │ └── uri/
│ │ │ └── StubOAuthUri.java
│ │ ├── member/
│ │ │ ├── application/
│ │ │ │ └── MemberServiceTest.java
│ │ │ ├── domain/
│ │ │ │ ├── MemberRepositoryTest.java
│ │ │ │ └── MemberTest.java
│ │ │ └── presentation/
│ │ │ └── MemberControllerTest.java
│ │ ├── schedule/
│ │ │ ├── application/
│ │ │ │ ├── CheckedSchedulesFinderTest.java
│ │ │ │ └── ScheduleServiceTest.java
│ │ │ ├── domain/
│ │ │ │ ├── IntegrationScheduleTest.java
│ │ │ │ ├── IntegrationSchedulesTest.java
│ │ │ │ ├── PeriodTest.java
│ │ │ │ ├── ScheduleRepositoryTest.java
│ │ │ │ ├── ScheduleTest.java
│ │ │ │ └── scheduler/
│ │ │ │ └── SchedulerTest.java
│ │ │ └── presentation/
│ │ │ └── ScheduleControllerTest.java
│ │ └── subscription/
│ │ ├── application/
│ │ │ └── SubscriptionServiceTest.java
│ │ ├── domain/
│ │ │ ├── ColorTest.java
│ │ │ ├── SubscriptionRepositoryTest.java
│ │ │ ├── SubscriptionTest.java
│ │ │ └── SubscriptionsTest.java
│ │ └── presentation/
│ │ └── SubscriptionControllerTest.java
│ └── resources/
│ └── application.yml
├── frontend/
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc.json
│ ├── .storybook/
│ │ ├── main.js
│ │ ├── preview-body.html
│ │ └── preview.js
│ ├── babel.config.js
│ ├── jest.config.js
│ ├── package.json
│ ├── setupFile.js
│ ├── src/
│ │ ├── @types/
│ │ │ ├── calendar.ts
│ │ │ ├── category.ts
│ │ │ ├── custom.d.ts
│ │ │ ├── emotion.d.ts
│ │ │ ├── googleCalendar.ts
│ │ │ ├── index.ts
│ │ │ ├── profile.ts
│ │ │ ├── schedule.ts
│ │ │ ├── subscription.ts
│ │ │ └── util.ts
│ │ ├── App.tsx
│ │ ├── api/
│ │ │ ├── category.ts
│ │ │ ├── googleCalendar.ts
│ │ │ ├── index.ts
│ │ │ ├── login.ts
│ │ │ ├── profile.ts
│ │ │ ├── schedule.ts
│ │ │ └── subscription.ts
│ │ ├── components/
│ │ │ ├── @common/
│ │ │ │ ├── Button/
│ │ │ │ │ ├── Button.stories.tsx
│ │ │ │ │ ├── Button.styles.ts
│ │ │ │ │ ├── Button.test.tsx
│ │ │ │ │ └── Button.tsx
│ │ │ │ ├── ErrorBoundary/
│ │ │ │ │ └── ErrorBoundary.tsx
│ │ │ │ ├── Fieldset/
│ │ │ │ │ ├── Fieldset.stories.tsx
│ │ │ │ │ ├── Fieldset.styles.ts
│ │ │ │ │ ├── Fieldset.test.tsx
│ │ │ │ │ └── Fieldset.tsx
│ │ │ │ ├── ModalPortal/
│ │ │ │ │ ├── ModalPortal.styles.ts
│ │ │ │ │ └── ModalPortal.tsx
│ │ │ │ ├── PageLayout/
│ │ │ │ │ ├── PageLayout.styles.ts
│ │ │ │ │ └── PageLayout.tsx
│ │ │ │ ├── Responsive/
│ │ │ │ │ ├── Responsive.styles.ts
│ │ │ │ │ └── Responsive.tsx
│ │ │ │ ├── Select/
│ │ │ │ │ ├── Select.styles.ts
│ │ │ │ │ └── Select.tsx
│ │ │ │ ├── Skeleton/
│ │ │ │ │ ├── Skeleton.stories.tsx
│ │ │ │ │ ├── Skeleton.styles.ts
│ │ │ │ │ └── Skeleton.tsx
│ │ │ │ └── Spinner/
│ │ │ │ ├── Spinner.stories.tsx
│ │ │ │ ├── Spinner.styles.ts
│ │ │ │ └── Spinner.tsx
│ │ │ ├── AdminCategoryManageModal/
│ │ │ │ ├── AdminCategoryManageModal.styles.ts
│ │ │ │ └── AdminCategoryManageModal.tsx
│ │ │ ├── AdminItem/
│ │ │ │ ├── AdminItem.styles.ts
│ │ │ │ └── AdminItem.tsx
│ │ │ ├── Calendar/
│ │ │ │ ├── Calendar.fallback.tsx
│ │ │ │ ├── Calendar.styles.ts
│ │ │ │ └── Calendar.tsx
│ │ │ ├── CategoryAddModal/
│ │ │ │ ├── CategoryAddModal.stories.tsx
│ │ │ │ ├── CategoryAddModal.styles.ts
│ │ │ │ └── CategoryAddModal.tsx
│ │ │ ├── CategoryControl/
│ │ │ │ ├── CategoryControl.tsx
│ │ │ │ └── CategoryCotrol.styles.ts
│ │ │ ├── CategoryList/
│ │ │ │ ├── CategoryList.fallback.stories.tsx
│ │ │ │ ├── CategoryList.fallback.tsx
│ │ │ │ ├── CategoryList.stories.tsx
│ │ │ │ ├── CategoryList.styles.ts
│ │ │ │ └── CategoryList.tsx
│ │ │ ├── CategoryModifyModal/
│ │ │ │ ├── CategoryModifyModal.styles.ts
│ │ │ │ └── CategoryModifyModal.tsx
│ │ │ ├── DateCell/
│ │ │ │ ├── DateCell.styles.ts
│ │ │ │ └── DateCell.tsx
│ │ │ ├── Footer/
│ │ │ │ ├── Footer.styles.ts
│ │ │ │ └── Footer.tsx
│ │ │ ├── GoogleCategoryManageModal/
│ │ │ │ ├── GoogleCategoryManageModal.styles.ts
│ │ │ │ └── GoogleCategoryManageModal.tsx
│ │ │ ├── GoogleImportModal/
│ │ │ │ ├── GoogleImportModal.styles.ts
│ │ │ │ └── GoogleImportModal.tsx
│ │ │ ├── MoreScheduleModal/
│ │ │ │ ├── MoreScheduleModal.styles.ts
│ │ │ │ └── MoreScheduleModal.tsx
│ │ │ ├── NavBar/
│ │ │ │ ├── NavBar.stories.tsx
│ │ │ │ ├── NavBar.styles.ts
│ │ │ │ └── NavBar.tsx
│ │ │ ├── Profile/
│ │ │ │ ├── Profile.fallback.stories.tsx
│ │ │ │ ├── Profile.fallback.tsx
│ │ │ │ ├── Profile.styles.ts
│ │ │ │ └── Profile.tsx
│ │ │ ├── ProtectRoute/
│ │ │ │ └── ProtectRoute.tsx
│ │ │ ├── Schedule/
│ │ │ │ ├── Schedule.styles.ts
│ │ │ │ └── Schedule.tsx
│ │ │ ├── ScheduleAddButton/
│ │ │ │ ├── ScheduleAddButton.stories.tsx
│ │ │ │ ├── ScheduleAddButton.styles.ts
│ │ │ │ └── ScheduleAddButton.tsx
│ │ │ ├── ScheduleAddModal/
│ │ │ │ ├── ScheduleAddModal.stories.tsx
│ │ │ │ ├── ScheduleAddModal.styles.ts
│ │ │ │ └── ScheduleAddModal.tsx
│ │ │ ├── ScheduleModal/
│ │ │ │ ├── ScheduleModal.styles.ts
│ │ │ │ └── ScheduleModal.tsx
│ │ │ ├── ScheduleModifyModal/
│ │ │ │ ├── ScheduleModifyModal.styles.ts
│ │ │ │ └── ScheduleModifyModal.tsx
│ │ │ ├── SideAdminList/
│ │ │ │ ├── SideAdminList.styles.ts
│ │ │ │ └── SideAdminList.tsx
│ │ │ ├── SideBar/
│ │ │ │ ├── SideBar.fallback.stories.tsx
│ │ │ │ ├── SideBar.fallback.tsx
│ │ │ │ ├── SideBar.styles.ts
│ │ │ │ └── SideBar.tsx
│ │ │ ├── SideBarButton/
│ │ │ │ ├── SideBarButton.styles.ts
│ │ │ │ └── SideBarButton.tsx
│ │ │ ├── SideGoogleList/
│ │ │ │ ├── SideGoogleList.styles.ts
│ │ │ │ └── SideGoogleList.tsx
│ │ │ ├── SideItem/
│ │ │ │ ├── SideItem.styles.ts
│ │ │ │ └── SideItem.tsx
│ │ │ ├── SideSubscribedList/
│ │ │ │ ├── SideSubscribedList.styles.ts
│ │ │ │ └── SideSubscribedList.tsx
│ │ │ ├── SnackBar/
│ │ │ │ ├── SnackBar.styles.ts
│ │ │ │ └── SnackBar.tsx
│ │ │ ├── SubscribedCategoryItem/
│ │ │ │ ├── SubscribedCategoryItem.styles.ts
│ │ │ │ └── SubscribedCategoryItem.tsx
│ │ │ ├── SubscriberItem/
│ │ │ │ ├── SubscriberItem.styles.ts
│ │ │ │ └── SubscriberItem.tsx
│ │ │ ├── SubscriptionModifyModal/
│ │ │ │ ├── SubscriptionModifyModal.styles.ts
│ │ │ │ └── SubscriptionModifyModal.tsx
│ │ │ ├── UnsubscribedCategoryItem/
│ │ │ │ ├── UnsubscribedCategoryItem.styles.ts
│ │ │ │ └── UnsubscribedCategoryItem.tsx
│ │ │ └── WithdrawalModal/
│ │ │ ├── WithdrawalModal.stories.tsx
│ │ │ ├── WithdrawalModal.styles.ts
│ │ │ └── WithdrawalModal.tsx
│ │ ├── constants/
│ │ │ ├── api.ts
│ │ │ ├── category.ts
│ │ │ ├── date.ts
│ │ │ ├── index.ts
│ │ │ ├── message.ts
│ │ │ ├── schedule.ts
│ │ │ ├── style.ts
│ │ │ └── validate.ts
│ │ ├── domains/
│ │ │ └── schedule.ts
│ │ ├── hooks/
│ │ │ ├── @queries/
│ │ │ │ ├── category.ts
│ │ │ │ ├── googleCalendar.ts
│ │ │ │ ├── login.ts
│ │ │ │ ├── profile.ts
│ │ │ │ ├── schedule.ts
│ │ │ │ └── subscription.ts
│ │ │ ├── useCalendar.ts
│ │ │ ├── useControlledInput.ts
│ │ │ ├── useHoverCategoryItem.ts
│ │ │ ├── useIntersect.ts
│ │ │ ├── useModalPosition.ts
│ │ │ ├── useRootFontSize.ts
│ │ │ ├── useSnackBar.ts
│ │ │ ├── useToggle.ts
│ │ │ ├── useUserValue.ts
│ │ │ ├── useValidateCategory.ts
│ │ │ └── useValidateSchedule.ts
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── pages/
│ │ │ ├── AuthPage/
│ │ │ │ └── AuthPage.tsx
│ │ │ ├── CalendarPage/
│ │ │ │ ├── CalendarPage.styles.ts
│ │ │ │ └── CalendarPage.tsx
│ │ │ ├── CategoryPage/
│ │ │ │ ├── CategoryPage.styles.ts
│ │ │ │ └── CategoryPage.tsx
│ │ │ ├── ErrorPage/
│ │ │ │ ├── ErrorPage.styles.ts
│ │ │ │ └── ErrorPage.tsx
│ │ │ ├── NotFoundPage/
│ │ │ │ ├── NotFoundPage.styles.ts
│ │ │ │ └── NotFoundPage.tsx
│ │ │ ├── PrivacyPolicyPage/
│ │ │ │ ├── PrivacyPolicyPage.styles.ts
│ │ │ │ └── PrivacyPolicyPage.tsx
│ │ │ └── StartPage/
│ │ │ ├── StartPage.styles.ts
│ │ │ └── StartPage.tsx
│ │ ├── recoil/
│ │ │ ├── atoms/
│ │ │ │ └── index.ts
│ │ │ └── selectors/
│ │ │ └── index.ts
│ │ ├── styles/
│ │ │ ├── GlobalStyle.tsx
│ │ │ └── theme.ts
│ │ ├── utils/
│ │ │ ├── date.ts
│ │ │ ├── index.ts
│ │ │ └── storage.ts
│ │ └── validation/
│ │ └── index.ts
│ ├── tsconfig.json
│ └── webpack.config.js
└── jenkins/
├── backend-dev.jenkinsfile
├── backend-prod.jenkinsfile
├── frontend-dev.jenkinsfile
└── frontend-prod.jenkinsfile
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug-template.md
================================================
---
name: Bug Template
about: 버그를 이슈에 등록한다.
title: '이슈의 제목을 입력해주세요!'
labels: ''
assignees: ''
---
## 🤷 버그 내용
## ⚠ 버그 재현 방법
1.
2.
3.
## 📸 스크린샷
## 👄 참고 사항
================================================
FILE: .github/ISSUE_TEMPLATE/feature-template.md
================================================
---
name: Feature Template
about: 구현할 기능을 이슈에 등록한다.
title: '이슈의 제목을 입력해주세요!'
labels: ''
assignees: ''
---
## 🤷 구현할 기능
## 🔨 상세 작업 내용
- [ ] To-do 1
- [ ] To-do 2
- [ ] To-do 3
## 📄 참고 사항
## ⏰ 예상 소요 기간
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
- [ ] 🔀 PR 제목의 형식을 잘 작성했나요? e.g. `[feat] PR을 등록한다.`
- [ ] 💯 테스트는 잘 통과했나요?
- [ ] 🏗️ 빌드는 성공했나요?
- [ ] 🧹 불필요한 코드는 제거했나요?
- [ ] 💭 이슈는 등록했나요?
- [ ] 🏷️ 라벨은 등록했나요?
- [ ] 💻 git rebase를 사용했나요?
- [ ] 🌈 알록달록한가요?
## 작업 내용
## 스크린샷
## 주의사항
Closes #{이슈 번호}
================================================
FILE: .github/workflows/backend-cd.yml
================================================
name: Backend CD
on:
push:
branches:
- main
- develop
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend
steps:
- uses: actions/checkout@v2
with:
token: ${{secrets.SUBMODULE_TOKEN}}
submodules: true
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: "11"
distribution: "adopt"
- name: Build with Gradle For RestDocs
run: ./gradlew bootJar
- name: Build with Gradle
run: ./gradlew bootJar
- name: Deploy use SCP
uses: appleboy/scp-action@master
with:
host: ${{secrets.LINODE_HOST}}
username: ${{secrets.LINODE_USERNAME}}
password: ${{secrets.LINODE_PASSWORD}}
source: "./backend/build/libs/*.jar"
target: "/root/cd_application"
strip_components: 3
- name: Run Application use SSH
uses: appleboy/ssh-action@master
with:
host: ${{secrets.LINODE_HOST}}
username: ${{secrets.LINODE_USERNAME}}
password: ${{secrets.LINODE_PASSWORD}}
script_stop: true
script: sh /root/cd_application/run.sh
================================================
FILE: .github/workflows/backend-ci.yml
================================================
name: Backend CI
on:
pull_request:
branches:
- main
- develop
paths:
- backend/**
- .github/** # Github Actions 작업을 위한 포함
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: "11"
distribution: "temurin"
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
junit_files: ${{ github.workspace }}/backend/build/test-results/**/*.xml
================================================
FILE: .github/workflows/frontend-ci.yml
================================================
name: Frontend CI
on:
pull_request:
branches:
- main
- develop
paths:
- frontend/**
- .github/** # Github Actions 작업을 위한 포함
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'yarn'
cache-dependency-path: '**/yarn.lock'
- name: Install node packages
run: yarn --frozen-lockfile
- name: Check lint
run: yarn check-lint
- name: Check prettier
run: yarn check-prettier
- name: Build
run: yarn dev-build
- name: Component test
run: yarn test
================================================
FILE: .gitignore
================================================
.idea
.DS_Store
================================================
FILE: .gitmodules
================================================
[submodule "backend/src/main/resources/config"]
path = backend/src/main/resources/config
url = https://github.com/dallog/config.git
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 dallog
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
<img src="https://user-images.githubusercontent.com/11745691/185735071-5eb23eaa-745b-4d69-a336-b64e5a6f011e.png" />
### 달력이 기록을 공유할 때, 달록 🌙
[<img src="https://img.shields.io/badge/-dallog.me-important?style=flat&logo=google-chrome&logoColor=white" />](https://dallog.me) [<img src="https://img.shields.io/badge/-tech blog-blue?style=flat&logo=google-chrome&logoColor=white" />](https://dallog.github.io) [<img src="https://img.shields.io/badge/release-v1.1.6-critical?style=flat&logo=google-chrome&logoColor=white" />](https://github.com/woowacourse-teams/2022-dallog/releases/tag/v1.1.6)
[](https://dallog.me)
</div>
## 🌙 소개
달록은 우아한테크코스 공유 캘린더입니다. 우아한테크코스 공식 일정, 데일리 팀, 스터디 등 파편화된 여러 일정을 모아 달록에서 관리할 수 있습니다. 사용자는 관심있는 일정 카테고리를 구독하여 개인화된 캘린더를 사용할 수 있습니다.
**[달록을 더 자세히 알아보고 싶다면, 여기로!](https://sites.google.com/woowahan.com/woowacourse-demo-4th/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8/%EB%8B%AC%EB%A1%9D)**
## 🖥 서비스 화면

## 🛠 Tech Stacks
### Front-end

### Back-end

## ⚙️ Infrastructure

## 🔀 CI/CD Pipeline

## 🌈 알록달록하게 일을 더 잘하는 9가지 방법

## 👥 Members
| Backend | Backend | Backend | Backend | Frontend | Frontend |
| :------------------------------------------: | :------------------------------------------------: | :----------------------------------------------: | :------------------------------------------: | :--------------------------------------------: | :-----------------------------------------: |
|  |  |  |  |  |  |
| [매트(최기현)](https://github.com/hyeonic) | [리버(구동희)](https://github.com/gudonghee2000) | [파랑(이하은)](https://github.com/summerlunaa) | [후디(조동현)](https://github.com/devHudi) | [티거(이다예)](https://github.com/daaaayeah) | [나인(장호영)](https://github.com/jhy979) |
================================================
FILE: backend/.gitignore
================================================
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### resources ###
/src/main/resources/static/docs
### logs ###
logs/
================================================
FILE: backend/build.gradle
================================================
plugins {
id 'org.springframework.boot' version '2.7.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'org.asciidoctor.jvm.convert' version '3.3.2'
id 'org.sonarqube' version '3.3'
id 'java'
id 'jacoco'
}
group = 'com.allog'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
ext {
snippetsDir = file('build/generated-snippets')
}
configurations {
asciidoctorExtensions
}
repositories {
mavenCentral()
}
dependencies {
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'
implementation 'org.springframework.boot:spring-boot-starter-cache'
runtimeOnly 'mysql:mysql-connector-java'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured:4.4.0'
// JWT를 위한 의존성
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
// Restdocs를 위한 의존성
asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
// Sonarqube를 위한 의존성
implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
}
test {
outputs.dir snippetsDir
useJUnitPlatform()
finalizedBy 'jacocoTestReport'
}
jacoco {
toolVersion = "0.8.8"
}
jacocoTestReport {
reports {
xml.enabled true
csv.enabled true
html.enabled true
xml.destination file("${buildDir}/jacoco/index.xml")
csv.destination file("${buildDir}/jacoco/index.csv")
html.destination file("${buildDir}/jacoco/index.html")
}
afterEvaluate {
classDirectories.setFrom(
files(classDirectories.files.collect {
fileTree(dir: it, excludes: [
'**/*Application*',
'**/*Exception*',
'**/dto/**',
'**/infrastructure/**',
'**/global/config/**',
'**/BaseEntity*',
'**/ControllerAdvice*',
'**/AuthorizationExtractor*'
])
})
)
}
finalizedBy 'jacocoTestCoverageVerification'
}
jacocoTestCoverageVerification {
violationRules {
rule {
enabled = true
element = "CLASS"
// 모든 클래스 각각 라인 커버리지 75% 만족시 빌드 성공
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.75
}
excludes = [
'*.*Application',
'*.*Exception',
'*.dto.*',
'*.infrastructure.*',
'*.global.config.*',
'*.BaseEntity',
'*.ControllerAdvice',
'*.AuthorizationExtractor'
]
}
}
}
sonarqube {
properties {
property 'sonar.projectKey', 'dallog'
property "sonar.sources", "src"
property "sonar.language", "java"
property "sonar.sourceEncoding", "UTF-8"
property "sonar.profile", "Dallog Custom Java Ruleset"
property "sonar.java.binaries", "${buildDir}/classes"
property "sonar.test.inclusions", "**/*Test.java"
property 'sonar.exclusions', '**/jacoco/**'
property 'sonar.coverage.exclusions', '**/test/**/*, **/*Application*, **/global/config/**, **/dto/**, **/*Exception*, **/infrastructure/**, **/BaseEntity*, **/ControllerAdvice*, **/AuthorizationExtractor*'
property "sonar.coverage.jacoco.xmlReportPaths", "${buildDir}/jacoco/index.xml"
}
}
asciidoctor {
configurations 'asciidoctorExtensions'
baseDirFollowsSourceFile()
inputs.dir snippetsDir
dependsOn test
}
asciidoctor.doFirst {
delete file('src/main/resources/static/docs')
}
task copyDocument(type: Copy) {
dependsOn asciidoctor
from "${asciidoctor.outputDir}"
into file("src/main/resources/static/docs")
}
bootJar {
dependsOn copyDocument
}
================================================
FILE: backend/gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: backend/gradlew
================================================
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
================================================
FILE: backend/gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: backend/src/docs/asciidoc/auth.adoc
================================================
== Auth(인증)
=== OAuth 로그인 링크 생성
==== HTTP Request
include::{snippets}/auth/generateLink/http-request.adoc[]
==== Path Parameters
include::{snippets}/auth/generateLink/path-parameters.adoc[]
==== Request Parameters
include::{snippets}/auth/generateLink/request-parameters.adoc[]
==== HTTP Response
include::{snippets}/auth/generateLink/http-response.adoc[]
==== Response Fields
include::{snippets}/auth/generateLink/response-fields.adoc[]
=== OAuth 로그인
==== HTTP Request
include::{snippets}/auth/generateTokens/http-request.adoc[]
==== Path Parameters
include::{snippets}/auth/generateTokens/path-parameters.adoc[]
==== Request Fields
include::{snippets}/auth/generateTokens/request-fields.adoc[]
==== HTTP Response
include::{snippets}/auth/generateTokens/http-response.adoc[]
==== Response Fields
include::{snippets}/auth/generateTokens/response-fields.adoc[]
=== OAuth 로그인 (Resource Server 에러)
==== HTTP Response
include::{snippets}/auth/generateTokens/failByResourceServerError/http-response.adoc[]
=== RefreshToken을 통한 자동 로그인
==== HTTP Request
include::{snippets}/auth/generateRenewalToken/http-request.adoc[]
==== Request Fields
include::{snippets}/auth/generateRenewalToken/request-fields.adoc[]
==== HTTP Response
include::{snippets}/auth/generateRenewalToken/http-response.adoc[]
==== Response Fields
include::{snippets}/auth/generateRenewalToken/response-fields.adoc[]
=== RefreshToken을 통한 자동 로그인 (Invalid Token 에러)
==== HTTP Response
include::{snippets}/auth/generateRenewalToken/invalidTokenError/http-response.adoc[]
================================================
FILE: backend/src/docs/asciidoc/category.adoc
================================================
== Category(카테고리)
=== 카테고리 생성
==== HTTP Request
include::{snippets}/category/save/http-request.adoc[]
==== Request Fields
include::{snippets}/category/save/request-fields.adoc[]
==== HTTP Response
include::{snippets}/category/save/http-response.adoc[]
==== Response Fields
include::{snippets}/category/save/response-fields.adoc[]
=== 카테고리 생성 (유효하지 않은 카테고리 이름)
==== HTTP Response
include::{snippets}/category/save/failByInvalidNameFormat/http-response.adoc[]
=== 전체 카테고리 조회
==== Request
include::{snippets}/category/findAllByName/allByNoName/http-request.adoc[]
==== HTTP Response
include::{snippets}/category/findAllByName/allByNoName/http-response.adoc[]
=== 전체 카테고리 목록 이름으로 필터링
==== HTTP Request
include::{snippets}/category/findAllByName/filterByName/http-request.adoc[]
==== Request Parameters
include::{snippets}/category/findAllByName/filterByName/request-parameters.adoc[]
==== HTTP Response
include::{snippets}/category/findAllByName/filterByName/http-response.adoc[]
=== 자신이 일정을 추가/수정/삭제할 수 있는 카테고리 목록 조회
==== HTTP Request
include::{snippets}/category/findScheduleEditableCategories/http-request.adoc[]
==== HTTP Response
include::{snippets}/category/findScheduleEditableCategories/http-response.adoc[]
=== 자신이 ADMIN으로 있는 카테고리 목록 조회
==== HTTP Request
include::{snippets}/category/findAdminCategories/http-request.adoc[]
==== HTTP Response
include::{snippets}/category/findAdminCategories/http-response.adoc[]
=== ID를 통한 카테고리 단건 조회
==== HTTP Request
include::{snippets}/category/findDetailCategoryById/http-request.adoc[]
==== Path Parameters
include::{snippets}/category/findDetailCategoryById/path-parameters.adoc[]
==== HTTP Response
include::{snippets}/category/findDetailCategoryById/http-response.adoc[]
=== ID를 통한 카테고리 단건 조회 (존재하지 않는 경우)
==== HTTP Response
include::{snippets}/category/findDetailCategoryById/failByNoCategory/http-response.adoc[]
=== 카테고리 수정
==== HTTP Request
include::{snippets}/category/update/http-request.adoc[]
==== Path Parameters
include::{snippets}/category/update/path-parameters.adoc[]
==== HTTP Response
include::{snippets}/category/update/http-response.adoc[]
=== 카테고리 수정 (존재하지 않는 경우)
==== HTTP Response
include::{snippets}/category/update/failByNoCategory/http-response.adoc[]
=== 카테고리 수정 (유효하지 않은 카테고리 이름)
==== HTTP Response
include::{snippets}/category/update/failByInvalidNameFormat/http-response.adoc[]
=== 카테고리 삭제
==== HTTP Request
include::{snippets}/category/delete/http-request.adoc[]
==== Path Parameters
include::{snippets}/category/delete/path-parameters.adoc[]
==== HTTP Response
include::{snippets}/category/delete/http-response.adoc[]
=== 카테고리 삭제 (존재하지 않는 경우)
==== HTTP Response
include::{snippets}/category/delete/failByNoCategory/http-response.adoc[]
=== 카테고리 역할 수정
역할이 ADMIN인 회원은 카테고리 구독자(일반 구독자, 관리자, 자기자신 모두 포함)의 역할을 수정할 수 있습니다.
==== HTTP Request
include::{snippets}/category/updateRole/http-request.adoc[]
==== Path Parameters
include::{snippets}/category/updateRole/path-parameters.adoc[]
==== HTTP Response
include::{snippets}/category/updateRole/http-response.adoc[]
=== 카테고리 역할 수정 (권한이 없는 경우)
역할이 ADMIN인 경우만 구독자의 역할을 수정할 수 있습니다.
==== HTTP Response
include::{snippets}/category/updateRole/failByNoPermission/http-response.adoc[]
=== 카테고리 역할 수정 (구독을 하지 않은 경우)
카테고리 역할 수정 대상의 회원이 해당 카테고리를 구독하지 않은 상태인경우 역할 또한 존재하지 않으므로 역할을 찾을 수 없습니다.
==== HTTP Response
include::{snippets}/category/updateRole/failByCategoryRoleNotFound/http-response.adoc[]
=== 카테고리 역할 수정 (자신의 역할 수정시, 자신이 유일한 ADMIN인 경우)
자기자신의 카테고리 역할 수정시 자신이 해당 카테고리의 유일한 ADMIN인 경우 역할을 변경할 수 없습니다.
==== HTTP Response
include::{snippets}/category/updateRole/failBySoleAdmin/http-response.adoc[]
=== 카테고리 구독자 목록 조회
==== Request
include::{snippets}/category/findSubscribers/http-request.adoc[]
==== Path Parameters
include::{snippets}/category/findSubscribers/path-parameters.adoc[]
==== HTTP Response
include::{snippets}/category/findSubscribers/http-response.adoc[]
=== 카테고리 구독자 목록 조회 (호출자가 ADMIN이 아닌 경우)
==== HTTP Response
include::{snippets}/category/findSubscribers/failByNoAuthority/http-response.adoc[]
================================================
FILE: backend/src/docs/asciidoc/external-calendar.adoc
================================================
== External Calendar (외부 캘린더)
=== 자신의 외부 캘린더 조회
==== HTTP Request
include::{snippets}/externalCalendar/get/http-request.adoc[]
==== HTTP Response
include::{snippets}/externalCalendar/get/http-response.adoc[]
=== 자신의 외부 캘린더 저장
==== HTTP Request
include::{snippets}/externalCalendar/save/http-request.adoc[]
==== Request Fields
include::{snippets}/externalCalendar/save/request-fields.adoc[]
==== HTTP Response
include::{snippets}/externalCalendar/save/http-response.adoc[]
=== 자신의 외부 캘린더 저장 (중복 저장할 경우)
==== HTTP Response
include::{snippets}/externalCalendar/duplicated-save/http-response.adoc[]
================================================
FILE: backend/src/docs/asciidoc/index.adoc
================================================
= 달록 API 문서
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 3
include::auth.adoc[]
include::member.adoc[]
include::category.adoc[]
include::schedule.adoc[]
include::subscription.adoc[]
include::external-calendar.adoc[]
================================================
FILE: backend/src/docs/asciidoc/member.adoc
================================================
== Member(회원)
=== 내 정보 조회
==== HTTP Request
include::{snippets}/member/findMe/http-request.adoc[]
==== HTTP Response
include::{snippets}/member/findMe/http-response.adoc[]
==== Response Fields
include::{snippets}/member/findMe/response-fields.adoc[]
=== 내 정보 조회 (존재하지 않는 회원 조회 시)
==== HTTP Response
include::{snippets}/member/findMe/failNoMember/http-response.adoc[]
=== 내 정보 수정
==== HTTP Request
include::{snippets}/member/update/http-request.adoc[]
==== Request Fields
include::{snippets}/member/update/request-fields.adoc[]
==== HTTP Response
include::{snippets}/member/update/http-response.adoc[]
================================================
FILE: backend/src/docs/asciidoc/schedule.adoc
================================================
== Schedule(일정)
=== 회원 일정 목록 조회
==== HTTP Request
include::{snippets}/schedule/findSchedulesByMemberId/http-request.adoc[]
==== Request Parameters
include::{snippets}/schedule/findSchedulesByMemberId/request-parameters.adoc[]
==== HTTP Response
include::{snippets}/schedule/findSchedulesByMemberId/http-response.adoc[]
=== 카테고리 별 일정 목록 조회
==== HTTP Request
include::{snippets}/schedule/findSchedulesByCategoryId/http-request.adoc[]
==== Request Parameters
include::{snippets}/schedule/findSchedulesByCategoryId/request-parameters.adoc[]
==== HTTP Response
include::{snippets}/schedule/findSchedulesByCategoryId/http-response.adoc[]
=== 일정 등록
==== HTTP Request
include::{snippets}/schedule/save/http-request.adoc[]
==== HTTP Response
include::{snippets}/schedule/save/http-response.adoc[]
=== 일정 등록 (카테고리 권한이 없을 때)
==== HTTP Response
include::{snippets}/schedule/save/failByNoPermission/http-response.adoc[]
=== 일정 생성 (카테고리가 존재하지 않음)
==== HTTP Response
include::{snippets}/schedule/save/failByNoCategory/http-response.adoc[]
=== 일정 단건 조회
==== HTTP Request
include::{snippets}/schedule/findById/http-request.adoc[]
==== HTTP Response
include::{snippets}/schedule/findById/http-response.adoc[]
=== 일정 단건 조회 (일정이 존재하지 않을 때)
==== HTTP Response
include::{snippets}/schedule/findById/failByNoSchedule/http-response.adoc[]
=== 일정 수정
==== HTTP Request
include::{snippets}/schedule/update/http-request.adoc[]
==== Path Parameters
include::{snippets}/schedule/update/path-parameters.adoc[]
==== HTTP Response
include::{snippets}/schedule/update/http-response.adoc[]
=== 일정 수정 (카테고리 권한이 없을 때)
==== HTTP Response
include::{snippets}/schedule/update/failByNoPermission/http-response.adoc[]
=== 일정 수정 (카테고리가 존재하지 않을 때)
==== HTTP Response
include::{snippets}/schedule/update/failByNoSchedule/http-response.adoc[]
=== 일정 제거
==== HTTP Request
include::{snippets}/schedule/delete/http-request.adoc[]
==== Path Parameters
include::{snippets}/schedule/delete/path-parameters.adoc[]
==== HTTP Response
include::{snippets}/schedule/delete/http-response.adoc[]
=== 일정 제거 (카테고리 권한이 없을 때)
==== HTTP Response
include::{snippets}/schedule/delete/failByNoPermission/http-response.adoc[]
=== 일정 제거 (카테고리가 존재하지 않음)
==== HTTP Response
include::{snippets}/schedule/delete/failByNoSchedule/http-response.adoc[]
================================================
FILE: backend/src/docs/asciidoc/subscription.adoc
================================================
== Subscription(구독)
=== 구독 등록
==== HTTP Request
include::{snippets}/subscription/save/http-request.adoc[]
==== Path Parameters
include::{snippets}/subscription/save/path-parameters.adoc[]
==== HTTP Response
include::{snippets}/subscription/save/http-response.adoc[]
=== 구독 등록 (중복된 구독을 등록할 때)
==== HTTP Response
include::{snippets}/subscription/save/failByAlreadyExisting/http-response.adoc[]
=== 구독 등록 (3자의 개인 카테고리 구독 요청시)
==== HTTP Response
include::{snippets}/subscription/save/failBySubscribingPrivateCategoryOfOther/http-response.adoc[]
=== 자신의 구독 목록 조회
==== HTTP Request
include::{snippets}/subscription/findMine/http-request.adoc[]
==== HTTP Response
include::{snippets}/subscription/findMine/http-response.adoc[]
=== 내 구독 정보 수정
==== HTTP Request
include::{snippets}/subscription/update/http-request.adoc[]
==== Path Parameters
include::{snippets}/subscription/update/path-parameters.adoc[]
==== Request Headers
include::{snippets}/subscription/update/request-headers.adoc[]
==== Request Parameters
include::{snippets}/subscription/update/request-body.adoc[]
==== HTTP Response
include::{snippets}/subscription/update/http-response.adoc[]
=== 구독 삭제
==== HTTP Request
include::{snippets}/subscription/delete/http-request.adoc[]
==== Path Parameters
include::{snippets}/subscription/delete/path-parameters.adoc[]
==== HTTP Response
include::{snippets}/subscription/delete/http-response.adoc[]
=== 구독 삭제 (내 구독이 아닐 때)
==== HTTP Response
include::{snippets}/subscription/delete/failByNoPermission/http-response.adoc[]
================================================
FILE: backend/src/main/java/com/allog/dallog/DallogApplication.java
================================================
package com.allog.dallog;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DallogApplication {
public static void main(String[] args) {
SpringApplication.run(DallogApplication.class, args);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/application/AuthService.java
================================================
package com.allog.dallog.auth.application;
import com.allog.dallog.auth.domain.AuthToken;
import com.allog.dallog.auth.domain.OAuthToken;
import com.allog.dallog.auth.domain.OAuthTokenRepository;
import com.allog.dallog.auth.dto.OAuthMember;
import com.allog.dallog.auth.dto.request.TokenRenewalRequest;
import com.allog.dallog.auth.dto.response.AccessAndRefreshTokenResponse;
import com.allog.dallog.auth.dto.response.AccessTokenResponse;
import com.allog.dallog.auth.event.MemberSavedEvent;
import com.allog.dallog.member.domain.Member;
import com.allog.dallog.member.domain.MemberRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true)
@Service
public class AuthService {
private final MemberRepository memberRepository;
private final OAuthTokenRepository oAuthTokenRepository;
private final TokenCreator tokenCreator;
private final ApplicationEventPublisher eventPublisher;
public AuthService(final MemberRepository memberRepository, final OAuthTokenRepository oAuthTokenRepository,
final TokenCreator tokenCreator, final ApplicationEventPublisher eventPublisher) {
this.memberRepository = memberRepository;
this.oAuthTokenRepository = oAuthTokenRepository;
this.tokenCreator = tokenCreator;
this.eventPublisher = eventPublisher;
}
@Transactional
public AccessAndRefreshTokenResponse generateAccessAndRefreshToken(final OAuthMember oAuthMember) {
Member foundMember = findMember(oAuthMember);
OAuthToken oAuthToken = getOAuthToken(oAuthMember, foundMember);
oAuthToken.change(oAuthMember.getRefreshToken());
AuthToken authToken = tokenCreator.createAuthToken(foundMember.getId());
return new AccessAndRefreshTokenResponse(authToken.getAccessToken(), authToken.getRefreshToken());
}
private Member findMember(final OAuthMember oAuthMember) {
String email = oAuthMember.getEmail();
if (memberRepository.existsByEmail(email)) {
return memberRepository.getByEmail(email);
}
return saveMember(oAuthMember);
}
private Member saveMember(final OAuthMember oAuthMember) {
Member savedMember = memberRepository.save(oAuthMember.toMember());
eventPublisher.publishEvent(new MemberSavedEvent(savedMember.getId()));
return savedMember;
}
private OAuthToken getOAuthToken(final OAuthMember oAuthMember, final Member member) {
Long memberId = member.getId();
if (oAuthTokenRepository.existsByMemberId(memberId)) {
return oAuthTokenRepository.getByMemberId(memberId);
}
return oAuthTokenRepository.save(new OAuthToken(member, oAuthMember.getRefreshToken()));
}
public AccessTokenResponse generateAccessToken(final TokenRenewalRequest tokenRenewalRequest) {
String refreshToken = tokenRenewalRequest.getRefreshToken();
AuthToken authToken = tokenCreator.renewAuthToken(refreshToken);
return new AccessTokenResponse(authToken.getAccessToken());
}
public Long extractMemberId(final String accessToken) {
Long memberId = tokenCreator.extractPayload(accessToken);
memberRepository.validateExistsById(memberId);
return memberId;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/application/AuthTokenCreator.java
================================================
package com.allog.dallog.auth.application;
import com.allog.dallog.auth.domain.AuthToken;
import com.allog.dallog.auth.domain.TokenRepository;
import org.springframework.stereotype.Component;
@Component
public class AuthTokenCreator implements TokenCreator {
private final TokenProvider tokenProvider;
private final TokenRepository tokenRepository;
public AuthTokenCreator(final TokenProvider tokenProvider, final TokenRepository tokenRepository) {
this.tokenProvider = tokenProvider;
this.tokenRepository = tokenRepository;
}
public AuthToken createAuthToken(final Long memberId) {
String accessToken = tokenProvider.createAccessToken(String.valueOf(memberId));
String refreshToken = createRefreshToken(memberId);
return new AuthToken(accessToken, refreshToken);
}
private String createRefreshToken(final Long memberId) {
if (tokenRepository.exist(memberId)) {
return tokenRepository.getToken(memberId);
}
String refreshToken = tokenProvider.createRefreshToken(String.valueOf(memberId));
return tokenRepository.save(memberId, refreshToken);
}
public AuthToken renewAuthToken(final String refreshToken) {
tokenProvider.validateToken(refreshToken);
Long memberId = Long.valueOf(tokenProvider.getPayload(refreshToken));
String accessTokenForRenew = tokenProvider.createAccessToken(String.valueOf(memberId));
String refreshTokenForRenew = tokenRepository.getToken(memberId);
AuthToken renewalAuthToken = new AuthToken(accessTokenForRenew, refreshTokenForRenew);
renewalAuthToken.validateHasSameRefreshToken(refreshToken);
return renewalAuthToken;
}
public Long extractPayload(final String accessToken) {
tokenProvider.validateToken(accessToken);
return Long.valueOf(tokenProvider.getPayload(accessToken));
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/application/JwtTokenProvider.java
================================================
package com.allog.dallog.auth.application;
import com.allog.dallog.auth.exception.InvalidTokenException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import javax.crypto.SecretKey;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class JwtTokenProvider implements TokenProvider {
private final SecretKey key;
private final long accessTokenValidityInMilliseconds;
private final long refreshTokenValidityInMilliseconds;
public JwtTokenProvider(@Value("${security.jwt.token.secret-key}") final String secretKey,
@Value("${security.jwt.token.access.expire-length}") final long accessTokenValidityInMilliseconds,
@Value("${security.jwt.token.refresh.expire-length}") final long refreshTokenValidityInMilliseconds) {
this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
this.accessTokenValidityInMilliseconds = accessTokenValidityInMilliseconds;
this.refreshTokenValidityInMilliseconds = refreshTokenValidityInMilliseconds;
}
@Override
public String createAccessToken(final String payload) {
return createToken(payload, accessTokenValidityInMilliseconds);
}
@Override
public String createRefreshToken(final String payload) {
return createToken(payload, refreshTokenValidityInMilliseconds);
}
private String createToken(final String payload, final Long validityInMilliseconds) {
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()
.setSubject(payload)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
@Override
public String getPayload(final String token) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
@Override
public void validateToken(final String token) {
try {
Jws<Claims> claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token);
claims.getBody()
.getExpiration()
.before(new Date());
} catch (final JwtException | IllegalArgumentException e) {
throw new InvalidTokenException("권한이 없습니다.");
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/application/OAuthClient.java
================================================
package com.allog.dallog.auth.application;
import com.allog.dallog.auth.dto.OAuthMember;
import com.allog.dallog.auth.dto.response.OAuthAccessTokenResponse;
public interface OAuthClient {
OAuthMember getOAuthMember(final String code, final String redirectUri);
OAuthAccessTokenResponse getAccessToken(final String refreshToken);
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/application/OAuthUri.java
================================================
package com.allog.dallog.auth.application;
@FunctionalInterface
public interface OAuthUri {
String generate(final String redirectUri);
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/application/TokenCreator.java
================================================
package com.allog.dallog.auth.application;
import com.allog.dallog.auth.domain.AuthToken;
public interface TokenCreator {
AuthToken createAuthToken(final Long memberId);
AuthToken renewAuthToken(final String outRefreshToken);
Long extractPayload(final String accessToken);
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/application/TokenProvider.java
================================================
package com.allog.dallog.auth.application;
public interface TokenProvider {
String createAccessToken(final String payload);
String createRefreshToken(final String payload);
String getPayload(final String token);
void validateToken(final String token);
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/domain/AuthToken.java
================================================
package com.allog.dallog.auth.domain;
import com.allog.dallog.auth.exception.NoSuchTokenException;
public class AuthToken {
private String accessToken;
private String refreshToken;
public AuthToken(final String accessToken, final String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
public String getAccessToken() {
return accessToken;
}
public String getRefreshToken() {
return refreshToken;
}
public void validateHasSameRefreshToken(final String otherRefreshToken) {
if (!refreshToken.equals(otherRefreshToken)) {
throw new NoSuchTokenException("회원의 리프레시 토큰이 아닙니다.");
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/domain/InMemoryAuthTokenRepository.java
================================================
package com.allog.dallog.auth.domain;
import com.allog.dallog.auth.exception.NoSuchTokenException;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.stereotype.Component;
@Component
public class InMemoryAuthTokenRepository implements TokenRepository {
private static final Map<Long, String> TOKEN_REPOSITORY = new ConcurrentHashMap<>();
@Override
public String save(final Long memberId, final String refreshToken) {
TOKEN_REPOSITORY.put(memberId, refreshToken);
return TOKEN_REPOSITORY.get(memberId);
}
@Override
public void deleteAll() {
TOKEN_REPOSITORY.clear();
}
@Override
public void deleteByMemberId(final Long memberId) {
TOKEN_REPOSITORY.remove(memberId);
}
@Override
public boolean exist(final Long memberId) {
return TOKEN_REPOSITORY.containsKey(memberId);
}
@Override
public String getToken(final Long memberId) {
Optional<String> token = Optional.ofNullable(TOKEN_REPOSITORY.get(memberId));
return token.orElseThrow(() -> new NoSuchTokenException("일치하는 토큰이 존재하지 않습니다."));
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/domain/OAuthToken.java
================================================
package com.allog.dallog.auth.domain;
import com.allog.dallog.global.entity.BaseEntity;
import com.allog.dallog.member.domain.Member;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Table(name = "oauth_tokens")
@Entity
public class OAuthToken extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "members_id", nullable = false)
private Member member;
@Column(name = "refresh_token", nullable = false)
private String refreshToken;
protected OAuthToken() {
}
public OAuthToken(final Member member, final String refreshToken) {
this.member = member;
this.refreshToken = refreshToken;
}
public void change(final String refreshToken) {
if (!Objects.isNull(refreshToken)) {
this.refreshToken = refreshToken;
}
}
public Long getId() {
return id;
}
public Member getMember() {
return member;
}
public String getRefreshToken() {
return refreshToken;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/domain/OAuthTokenRepository.java
================================================
package com.allog.dallog.auth.domain;
import com.allog.dallog.auth.exception.NoSuchOAuthTokenException;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface OAuthTokenRepository extends JpaRepository<OAuthToken, Long> {
boolean existsByMemberId(final Long memberId);
@Query("SELECT o "
+ "FROM OAuthToken o "
+ "WHERE o.member.id = :memberId")
Optional<OAuthToken> findByMemberId(final Long memberId);
default OAuthToken getByMemberId(final Long memberId) {
return findByMemberId(memberId)
.orElseThrow(NoSuchOAuthTokenException::new);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/domain/TokenRepository.java
================================================
package com.allog.dallog.auth.domain;
public interface TokenRepository {
String save(final Long memberId, final String refreshToken);
void deleteAll();
void deleteByMemberId(final Long memberId);
boolean exist(final Long memberId);
String getToken(final Long memberId);
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/dto/LoginMember.java
================================================
package com.allog.dallog.auth.dto;
public class LoginMember {
private Long id;
private LoginMember() {
}
public LoginMember(final Long id) {
this.id = id;
}
public Long getId() {
return id;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/dto/OAuthMember.java
================================================
package com.allog.dallog.auth.dto;
import com.allog.dallog.member.domain.Member;
import com.allog.dallog.member.domain.SocialType;
public class OAuthMember {
private final String email;
private final String displayName;
private final String profileImageUrl;
private final String refreshToken;
public OAuthMember(final String email, final String displayName, final String profileImageUrl,
final String refreshToken) {
this.email = email;
this.displayName = displayName;
this.profileImageUrl = profileImageUrl;
this.refreshToken = refreshToken;
}
public String getEmail() {
return email;
}
public String getDisplayName() {
return displayName;
}
public String getProfileImageUrl() {
return profileImageUrl;
}
public String getRefreshToken() {
return refreshToken;
}
public Member toMember() {
return new Member(email, displayName, profileImageUrl, SocialType.GOOGLE);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/dto/request/TokenRenewalRequest.java
================================================
package com.allog.dallog.auth.dto.request;
import javax.validation.constraints.NotNull;
public class TokenRenewalRequest {
@NotNull(message = "리프레시 토큰은 공백일 수 없습니다.")
private String refreshToken;
private TokenRenewalRequest() {
}
public TokenRenewalRequest(final String refreshToken) {
this.refreshToken = refreshToken;
}
public String getRefreshToken() {
return refreshToken;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/dto/request/TokenRequest.java
================================================
package com.allog.dallog.auth.dto.request;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class TokenRequest {
@NotBlank(message = "인가 코드는 공백일 수 없습니다.")
private String code;
@NotNull(message = "Null일 수 없습니다.")
private String redirectUri;
private TokenRequest() {
}
public TokenRequest(final String code, final String redirectUri) {
this.code = code;
this.redirectUri = redirectUri;
}
public String getCode() {
return code;
}
public String getRedirectUri() {
return redirectUri;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/dto/response/AccessAndRefreshTokenResponse.java
================================================
package com.allog.dallog.auth.dto.response;
public class AccessAndRefreshTokenResponse {
private String accessToken;
private String refreshToken;
private AccessAndRefreshTokenResponse() {
}
public AccessAndRefreshTokenResponse(final String accessToken, final String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
public String getAccessToken() {
return accessToken;
}
public String getRefreshToken() {
return refreshToken;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/dto/response/AccessTokenResponse.java
================================================
package com.allog.dallog.auth.dto.response;
public class AccessTokenResponse {
private String accessToken;
private AccessTokenResponse() {
}
public AccessTokenResponse(final String accessToken) {
this.accessToken = accessToken;
}
public String getAccessToken() {
return accessToken;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/dto/response/OAuthAccessTokenResponse.java
================================================
package com.allog.dallog.auth.dto.response;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class OAuthAccessTokenResponse {
private String accessToken;
private OAuthAccessTokenResponse() {
}
public OAuthAccessTokenResponse(final String accessToken) {
this.accessToken = accessToken;
}
public String getAccessToken() {
return accessToken;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/dto/response/OAuthUriResponse.java
================================================
package com.allog.dallog.auth.dto.response;
// OAuth 인증 URI(소셜 로그인 링크)를 전달하는 DTO
public class OAuthUriResponse {
private String oAuthUri;
private OAuthUriResponse() {
}
public OAuthUriResponse(final String oAuthUri) {
this.oAuthUri = oAuthUri;
}
public String getoAuthUri() {
return oAuthUri;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/event/MemberSavedEvent.java
================================================
package com.allog.dallog.auth.event;
public class MemberSavedEvent {
private final Long memberId;
public MemberSavedEvent(final Long memberId) {
this.memberId = memberId;
}
public Long getMemberId() {
return memberId;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/exception/EmptyAuthorizationHeaderException.java
================================================
package com.allog.dallog.auth.exception;
public class EmptyAuthorizationHeaderException extends RuntimeException {
public EmptyAuthorizationHeaderException(final String message) {
super(message);
}
public EmptyAuthorizationHeaderException() {
this("header에 Authorization이 존재하지 않습니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/exception/InvalidTokenException.java
================================================
package com.allog.dallog.auth.exception;
public class InvalidTokenException extends RuntimeException {
public InvalidTokenException(final String message) {
super(message);
}
public InvalidTokenException() {
this("유효하지 않은 토큰입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/exception/NoPermissionException.java
================================================
package com.allog.dallog.auth.exception;
public class NoPermissionException extends RuntimeException {
public NoPermissionException(final String message) {
super(message);
}
public NoPermissionException() {
this("권한이 없는 요청 입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/exception/NoSuchOAuthTokenException.java
================================================
package com.allog.dallog.auth.exception;
public class NoSuchOAuthTokenException extends RuntimeException {
public NoSuchOAuthTokenException(final String message) {
super(message);
}
public NoSuchOAuthTokenException() {
this("존재하지 않는 OAuthToken 입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/exception/NoSuchTokenException.java
================================================
package com.allog.dallog.auth.exception;
public class NoSuchTokenException extends RuntimeException {
public NoSuchTokenException(final String message) {
super(message);
}
public NoSuchTokenException() {
this("존재하지 않는 Token 입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/presentation/AuthController.java
================================================
package com.allog.dallog.auth.presentation;
import com.allog.dallog.auth.application.OAuthClient;
import com.allog.dallog.auth.application.OAuthUri;
import com.allog.dallog.auth.application.AuthService;
import com.allog.dallog.auth.dto.LoginMember;
import com.allog.dallog.auth.dto.OAuthMember;
import com.allog.dallog.auth.dto.request.TokenRenewalRequest;
import com.allog.dallog.auth.dto.request.TokenRequest;
import com.allog.dallog.auth.dto.response.AccessAndRefreshTokenResponse;
import com.allog.dallog.auth.dto.response.AccessTokenResponse;
import com.allog.dallog.auth.dto.response.OAuthUriResponse;
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api/auth")
@RestController
public class AuthController {
private final OAuthUri oAuthUri;
private final OAuthClient oAuthClient;
private final AuthService authService;
public AuthController(final OAuthUri oAuthUri, final OAuthClient oAuthClient, final AuthService authService) {
this.oAuthUri = oAuthUri;
this.oAuthClient = oAuthClient;
this.authService = authService;
}
@GetMapping("/{oauthProvider}/oauth-uri")
public ResponseEntity<OAuthUriResponse> generateLink(@PathVariable final String oauthProvider,
@RequestParam final String redirectUri) {
OAuthUriResponse oAuthUriResponse = new OAuthUriResponse(oAuthUri.generate(redirectUri));
return ResponseEntity.ok(oAuthUriResponse);
}
@PostMapping("/{oauthProvider}/token")
public ResponseEntity<AccessAndRefreshTokenResponse> generateAccessAndRefreshToken(
@PathVariable final String oauthProvider, @Valid @RequestBody final TokenRequest tokenRequest) {
OAuthMember oAuthMember = oAuthClient.getOAuthMember(tokenRequest.getCode(), tokenRequest.getRedirectUri());
AccessAndRefreshTokenResponse response = authService.generateAccessAndRefreshToken(oAuthMember);
return ResponseEntity.ok(response);
}
@PostMapping("/token/access")
public ResponseEntity<AccessTokenResponse> generateAccessToken(
@Valid @RequestBody final TokenRenewalRequest tokenRenewalRequest) {
AccessTokenResponse response = authService.generateAccessToken(tokenRenewalRequest);
return ResponseEntity.ok(response);
}
@GetMapping("/validate/token")
public ResponseEntity<Void> validateToken(@AuthenticationPrincipal final LoginMember loginMember) {
return ResponseEntity.ok().build();
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/presentation/AuthenticationPrincipal.java
================================================
package com.allog.dallog.auth.presentation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticationPrincipal {
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/presentation/AuthenticationPrincipalArgumentResolver.java
================================================
package com.allog.dallog.auth.presentation;
import com.allog.dallog.auth.application.AuthService;
import com.allog.dallog.auth.dto.LoginMember;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
@Component
public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
private final AuthService authService;
public AuthenticationPrincipalArgumentResolver(final AuthService authService) {
this.authService = authService;
}
@Override
public boolean supportsParameter(final MethodParameter parameter) {
return parameter.hasParameterAnnotation(AuthenticationPrincipal.class);
}
@Override
public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer,
final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String accessToken = AuthorizationExtractor.extract(request);
Long id = authService.extractMemberId(accessToken);
return new LoginMember(id);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/auth/presentation/AuthorizationExtractor.java
================================================
package com.allog.dallog.auth.presentation;
import com.allog.dallog.auth.exception.InvalidTokenException;
import com.allog.dallog.auth.exception.EmptyAuthorizationHeaderException;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
public class AuthorizationExtractor {
private static final String BEARER_TYPE = "Bearer ";
public static String extract(final HttpServletRequest request) {
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if (Objects.isNull(authorizationHeader)) {
throw new EmptyAuthorizationHeaderException();
}
validateAuthorizationFormat(authorizationHeader);
return authorizationHeader.substring(BEARER_TYPE.length()).trim();
}
private static void validateAuthorizationFormat(final String authorizationHeader) {
if (!authorizationHeader.toLowerCase().startsWith(BEARER_TYPE.toLowerCase())) {
throw new InvalidTokenException("token 형식이 잘못 되었습니다.");
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/application/CategoryService.java
================================================
package com.allog.dallog.category.application;
import static com.allog.dallog.category.domain.CategoryType.NORMAL;
import static com.allog.dallog.category.domain.CategoryType.PERSONAL;
import com.allog.dallog.auth.event.MemberSavedEvent;
import com.allog.dallog.category.dto.request.CategoryCreateRequest;
import com.allog.dallog.category.dto.request.CategoryUpdateRequest;
import com.allog.dallog.category.dto.request.ExternalCategoryCreateRequest;
import com.allog.dallog.category.dto.response.CategoriesResponse;
import com.allog.dallog.category.dto.response.CategoryDetailResponse;
import com.allog.dallog.category.dto.response.CategoryResponse;
import com.allog.dallog.category.exception.InvalidCategoryException;
import com.allog.dallog.categoryrole.domain.CategoryAuthority;
import com.allog.dallog.categoryrole.domain.CategoryRoleType;
import com.allog.dallog.category.domain.Category;
import com.allog.dallog.category.domain.CategoryRepository;
import com.allog.dallog.category.domain.CategoryType;
import com.allog.dallog.category.domain.ExternalCategoryDetail;
import com.allog.dallog.category.domain.ExternalCategoryDetailRepository;
import com.allog.dallog.categoryrole.domain.CategoryRole;
import com.allog.dallog.categoryrole.domain.CategoryRoleRepository;
import com.allog.dallog.member.domain.Member;
import com.allog.dallog.member.domain.MemberRepository;
import com.allog.dallog.schedule.domain.ScheduleRepository;
import com.allog.dallog.subscription.application.ColorPicker;
import com.allog.dallog.subscription.domain.Color;
import com.allog.dallog.subscription.domain.Subscription;
import com.allog.dallog.subscription.domain.SubscriptionRepository;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true)
@Service
public class CategoryService {
private static final String PERSONAL_CATEGORY_NAME = "내 일정";
private final CategoryRepository categoryRepository;
private final ExternalCategoryDetailRepository externalCategoryDetailRepository;
private final MemberRepository memberRepository;
private final SubscriptionRepository subscriptionRepository;
private final ScheduleRepository scheduleRepository;
private final CategoryRoleRepository categoryRoleRepository;
private final ColorPicker colorPicker;
public CategoryService(final CategoryRepository categoryRepository,
final ExternalCategoryDetailRepository externalCategoryDetailRepository,
final MemberRepository memberRepository, final SubscriptionRepository subscriptionRepository,
final ScheduleRepository scheduleRepository,
final CategoryRoleRepository categoryRoleRepository, final ColorPicker colorPicker) {
this.categoryRepository = categoryRepository;
this.externalCategoryDetailRepository = externalCategoryDetailRepository;
this.memberRepository = memberRepository;
this.subscriptionRepository = subscriptionRepository;
this.scheduleRepository = scheduleRepository;
this.categoryRoleRepository = categoryRoleRepository;
this.colorPicker = colorPicker;
}
@Transactional
public CategoryResponse save(final Long memberId, final CategoryCreateRequest request) {
categoryRoleRepository.validateManagingCategoryLimit(memberId, CategoryRoleType.ADMIN);
Member member = memberRepository.getById(memberId);
Category category = request.toEntity(member);
Category savedCategory = categoryRepository.save(category);
subscribeCategory(member, category);
createCategoryRoleAsAdminToCreator(member, category);
return new CategoryResponse(savedCategory);
}
@Transactional
public CategoryResponse save(final Long memberId, final ExternalCategoryCreateRequest request) {
List<Category> externalCategories = categoryRepository
.findByMemberIdAndCategoryType(memberId, CategoryType.GOOGLE);
externalCategoryDetailRepository
.validateExistByExternalIdAndCategoryIn(request.getExternalId(), externalCategories);
CategoryResponse response = save(memberId, new CategoryCreateRequest(request.getName(), CategoryType.GOOGLE));
Category category = categoryRepository.getById(response.getId());
externalCategoryDetailRepository.save(new ExternalCategoryDetail(category, request.getExternalId()));
return response;
}
@Transactional
@EventListener
public void savePersonalCategory(final MemberSavedEvent event) {
Member member = memberRepository.getById(event.getMemberId());
Category category = categoryRepository.save(new Category(PERSONAL_CATEGORY_NAME, member, PERSONAL));
subscribeCategory(member, category);
createCategoryRoleAsAdminToCreator(member, category);
}
private void subscribeCategory(final Member member, final Category category) {
Color color = Color.pick(colorPicker.pickNumber());
subscriptionRepository.save(new Subscription(member, category, color));
}
private void createCategoryRoleAsAdminToCreator(final Member member, final Category category) {
CategoryRole categoryRole = new CategoryRole(category, member, CategoryRoleType.ADMIN);
categoryRoleRepository.save(categoryRole);
}
public CategoriesResponse findNormalByName(final String name) {
List<Category> categories = categoryRepository.findByCategoryTypeAndNameContaining(NORMAL, name);
return new CategoriesResponse(categories);
}
// 회원이 ADMIN이 아니어도 일정 추가/제거/수정이 가능하므로, findAdminCategories와 별도의 메소드로 분리해야함
public CategoriesResponse findScheduleEditableCategories(final Long memberId) {
List<CategoryRole> categoryRoles = categoryRoleRepository.findByMemberId(memberId);
Set<CategoryRoleType> roleTypes = CategoryRoleType.getHavingAuthorities(Set.of(CategoryAuthority.ADD_SCHEDULE, CategoryAuthority.UPDATE_SCHEDULE));
return new CategoriesResponse(toCategories(categoryRoles, roleTypes));
}
public CategoriesResponse findAdminCategories(final Long memberId) {
List<CategoryRole> categoryRoles = categoryRoleRepository.findByMemberId(memberId);
return new CategoriesResponse(toCategories(categoryRoles, Set.of(CategoryRoleType.ADMIN)));
}
private List<Category> toCategories(final List<CategoryRole> categoryRoles, final Set<CategoryRoleType> roleTypes) {
return categoryRoles.stream()
.filter(categoryRole -> roleTypes.contains(categoryRole.getCategoryRoleType()))
.map(CategoryRole::getCategory)
.collect(Collectors.toList());
}
public CategoryDetailResponse findDetailCategoryById(final Long id) {
Category category = categoryRepository.getById(id);
List<Subscription> subscriptions = subscriptionRepository.findByCategoryId(id);
return new CategoryDetailResponse(category, subscriptions.size());
}
@Transactional
public void update(final Long memberId, final Long id, final CategoryUpdateRequest request) {
Category category = categoryRepository.getById(id);
CategoryRole role = categoryRoleRepository.getByMemberIdAndCategoryId(memberId, category.getId());
role.validateAuthority(CategoryAuthority.UPDATE_CATEGORY);
category.changeName(request.getName());
}
@Transactional
public void delete(final Long memberId, final Long id) {
Category category = categoryRepository.getById(id);
validateNotPersonalCategory(category);
CategoryRole role = categoryRoleRepository.getByMemberIdAndCategoryId(memberId, category.getId());
role.validateAuthority(CategoryAuthority.DELETE_CATEGORY);
scheduleRepository.deleteByCategoryIdIn(List.of(id));
subscriptionRepository.deleteByCategoryIdIn(List.of(id));
externalCategoryDetailRepository.deleteByCategoryId(id);
categoryRoleRepository.deleteByCategoryId(id);
categoryRepository.deleteById(id);
}
private void validateNotPersonalCategory(final Category category) {
if (category.isPersonal()) {
throw new InvalidCategoryException("내 일정 카테고리는 삭제할 수 없습니다.");
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/application/ExternalCategoryDetailService.java
================================================
package com.allog.dallog.category.application;
import com.allog.dallog.category.domain.Category;
import com.allog.dallog.category.domain.ExternalCategoryDetail;
import com.allog.dallog.category.domain.ExternalCategoryDetailRepository;
import com.allog.dallog.subscription.domain.SubscriptionRepository;
import com.allog.dallog.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<ExternalCategoryDetail> findByMemberId(final Long memberId) {
Subscriptions subscriptions = new Subscriptions(subscriptionRepository.findByMemberId(memberId));
List<Category> categories = subscriptions.findExternalCategory();
return externalCategoryDetailRepository.findByCategoryIn(categories);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/domain/Category.java
================================================
package com.allog.dallog.category.domain;
import com.allog.dallog.auth.exception.NoPermissionException;
import com.allog.dallog.category.exception.InvalidCategoryException;
import com.allog.dallog.global.entity.BaseEntity;
import com.allog.dallog.member.domain.Member;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Table(name = "categories")
@Entity
public class Category extends BaseEntity {
public static final int MAX_NAME_LENGTH = 20;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name", nullable = false)
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "members_id")
private Member member;
@Enumerated(value = EnumType.STRING)
@Column(name = "category_type", nullable = false)
private CategoryType categoryType;
protected Category() {
}
public Category(final String name, final Member member) {
validateNameLength(name);
this.name = name;
this.member = member;
this.categoryType = CategoryType.NORMAL;
}
public Category(final String name, final Member member, final CategoryType categoryType) {
validateNameLength(name);
this.name = name;
this.member = member;
this.categoryType = categoryType;
}
public void changeName(final String name) {
validatePersonal();
validateNameLength(name);
this.name = name;
}
private void validatePersonal() {
if (isPersonal()) {
throw new InvalidCategoryException("'내 일정' 카테고리는 수정할 수 없습니다.");
}
}
private void validateNameLength(final String name) {
if (name.isBlank()) {
throw new InvalidCategoryException("카테고리 이름은 공백일 수 없습니다.");
}
if (name.length() > MAX_NAME_LENGTH) {
throw new InvalidCategoryException(String.format("카테고리 이름의 길이는 %d을 초과할 수 없습니다.", MAX_NAME_LENGTH));
}
}
public void validateSubscriptionPossible(final Member member) {
if (this.categoryType == CategoryType.PERSONAL && !isCreatorId(member.getId())) {
throw new NoPermissionException("구독 권한이 없는 카테고리입니다.");
}
}
public void validateNotExternalCategory() {
if (categoryType == CategoryType.GOOGLE) {
throw new NoPermissionException("외부 연동 카테고리에는 일정을 추가할 수 없습니다.");
}
}
public boolean isCreatorId(final Long creatorId) {
return member.hasSameId(creatorId);
}
public boolean isNormal() {
return categoryType == CategoryType.NORMAL;
}
public boolean isPersonal() {
return categoryType == CategoryType.PERSONAL;
}
public boolean isInternal() {
return categoryType != CategoryType.GOOGLE;
}
public boolean isExternal() {
return categoryType == CategoryType.GOOGLE;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public Member getMember() {
return member;
}
public void setMember(final Member member) {
this.member = member;
}
public CategoryType getCategoryType() {
return categoryType;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/domain/CategoryRepository.java
================================================
package com.allog.dallog.category.domain;
import com.allog.dallog.category.exception.NoSuchCategoryException;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface CategoryRepository extends JpaRepository<Category, Long> {
@Query("SELECT c "
+ "FROM Subscription s "
+ "JOIN s.category c "
+ "WHERE c.categoryType = :categoryType AND c.name LIKE %:name% "
+ "GROUP BY c.id "
+ "ORDER BY COUNT(c.id) DESC")
List<Category> findByCategoryTypeAndNameContaining(final CategoryType categoryType, final String name);
@Query("SELECT c "
+ "FROM Category c "
+ "WHERE c.member.id = :memberId AND c.categoryType = :categoryType")
List<Category> findByMemberIdAndCategoryType(final Long memberId, final CategoryType categoryType);
@Query("SELECT c "
+ "FROM Category c "
+ "WHERE c.member.id = :memberId")
List<Category> findByMemberId(final Long memberId);
default Category getById(final Long id) {
return this.findById(id)
.orElseThrow(NoSuchCategoryException::new);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/domain/CategoryType.java
================================================
package com.allog.dallog.category.domain;
import com.allog.dallog.category.exception.NoSuchCategoryException;
public enum CategoryType {
NORMAL, PERSONAL, GOOGLE;
public static CategoryType from(final String value) {
try {
return CategoryType.valueOf(value.toUpperCase());
} catch (final IllegalArgumentException e) {
throw new NoSuchCategoryException("(" + value + ")는 존재하지 않는 카테고리 타입입니다.");
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/domain/ExternalCategoryDetail.java
================================================
package com.allog.dallog.category.domain;
import com.allog.dallog.global.entity.BaseEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Table(name = "external_category_details")
@Entity
public class ExternalCategoryDetail extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "categories_id", nullable = false)
private Category category;
@Column(name = "external_id", nullable = false)
private String externalId;
protected ExternalCategoryDetail() {
}
public ExternalCategoryDetail(final Category category, final String externalId) {
this.category = category;
this.externalId = externalId;
}
public Long getId() {
return id;
}
public Category getCategory() {
return category;
}
public String getExternalId() {
return externalId;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/domain/ExternalCategoryDetailRepository.java
================================================
package com.allog.dallog.category.domain;
import com.allog.dallog.category.exception.ExistExternalCategoryException;
import com.allog.dallog.category.exception.NoSuchExternalCategoryDetailException;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ExternalCategoryDetailRepository extends JpaRepository<ExternalCategoryDetail, Long> {
Optional<ExternalCategoryDetail> findByCategory(final Category category);
List<ExternalCategoryDetail> findByCategoryIn(final List<Category> categories);
boolean existsByExternalIdAndCategoryIn(final String externalId, final List<Category> categories);
void deleteByCategoryId(final Long categoryId);
void deleteByCategoryIdIn(final List<Long> categoryIds);
default ExternalCategoryDetail getByCategory(final Category category) {
return this.findByCategory(category)
.orElseThrow(NoSuchExternalCategoryDetailException::new);
}
default void validateExistByExternalIdAndCategoryIn(final String externalId,
final List<Category> externalCategories) {
if (existsByExternalIdAndCategoryIn(externalId, externalCategories)) {
throw new ExistExternalCategoryException();
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/dto/request/CategoryCreateRequest.java
================================================
package com.allog.dallog.category.dto.request;
import com.allog.dallog.category.domain.Category;
import com.allog.dallog.category.domain.CategoryType;
import com.allog.dallog.member.domain.Member;
import javax.validation.constraints.NotBlank;
public class CategoryCreateRequest {
@NotBlank(message = "공백일 수 없습니다.")
private String name;
@NotBlank(message = "공백일 수 없습니다.")
private String categoryType;
private CategoryCreateRequest() {
}
public CategoryCreateRequest(final String name, final CategoryType categoryType) {
this.name = name;
this.categoryType = categoryType.name();
}
public Category toEntity(final Member member) {
return new Category(name, member, CategoryType.from(categoryType));
}
public String getName() {
return name;
}
public String getCategoryType() {
return categoryType;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/dto/request/CategoryUpdateRequest.java
================================================
package com.allog.dallog.category.dto.request;
import javax.validation.constraints.NotBlank;
public class CategoryUpdateRequest {
@NotBlank(message = "카테고리 이름이 공백일 수 없습니다.")
private String name;
private CategoryUpdateRequest() {
}
public CategoryUpdateRequest(final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/dto/request/ExternalCategoryCreateRequest.java
================================================
package com.allog.dallog.category.dto.request;
import javax.validation.constraints.NotBlank;
public class ExternalCategoryCreateRequest {
@NotBlank(message = "외부 캘린더 아이디가 공백일 수 없습니다.")
private String externalId;
@NotBlank(message = "외부 캘린더 이름이 공백일 수 없습니다.")
private String name;
private ExternalCategoryCreateRequest() {
}
public ExternalCategoryCreateRequest(final String externalId, final String name) {
this.externalId = externalId;
this.name = name;
}
public String getExternalId() {
return externalId;
}
public String getName() {
return name;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/dto/response/CategoriesResponse.java
================================================
package com.allog.dallog.category.dto.response;
import com.allog.dallog.category.domain.Category;
import java.util.List;
import java.util.stream.Collectors;
public class CategoriesResponse {
private List<CategoryResponse> categories;
private CategoriesResponse() {
}
public CategoriesResponse(final List<Category> categories) {
this.categories = toResponses(categories);
}
private List<CategoryResponse> toResponses(final List<Category> categories) {
return categories.stream()
.map(CategoryResponse::new)
.collect(Collectors.toList());
}
public List<CategoryResponse> getCategories() {
return categories;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/dto/response/CategoryDetailResponse.java
================================================
package com.allog.dallog.category.dto.response;
import com.allog.dallog.category.domain.Category;
import com.allog.dallog.member.dto.response.MemberResponse;
import java.time.LocalDateTime;
public class CategoryDetailResponse {
private Long id;
private String name;
private String categoryType;
private int subscriberCount;
private MemberResponse creator;
private LocalDateTime createdAt;
private CategoryDetailResponse() {
}
public CategoryDetailResponse(final Category category, final int subscriberCount) {
this(category.getId(), category.getName(), category.getCategoryType().name(), subscriberCount,
new MemberResponse(category.getMember()), category.getCreatedAt());
}
public CategoryDetailResponse(final Long id, final String name, final String categoryType,
final int subscriberCount, final MemberResponse creator,
final LocalDateTime createdAt) {
this.id = id;
this.name = name;
this.categoryType = categoryType;
this.subscriberCount = subscriberCount;
this.creator = creator;
this.createdAt = createdAt;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getCategoryType() {
return categoryType;
}
public int getSubscriberCount() {
return subscriberCount;
}
public MemberResponse getCreator() {
return creator;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/dto/response/CategoryResponse.java
================================================
package com.allog.dallog.category.dto.response;
import com.allog.dallog.category.domain.Category;
import com.allog.dallog.member.dto.response.MemberResponse;
import java.time.LocalDateTime;
public class CategoryResponse {
private Long id;
private String name;
private String categoryType;
private MemberResponse creator;
private LocalDateTime createdAt;
private CategoryResponse() {
}
public CategoryResponse(final Category category) {
this(category.getId(), category.getName(), category.getCategoryType().name(),
new MemberResponse(category.getMember()), category.getCreatedAt());
}
public CategoryResponse(final Long id, final String name, final String categoryType, final MemberResponse creator,
final LocalDateTime createdAt) {
this.id = id;
this.name = name;
this.categoryType = categoryType;
this.creator = creator;
this.createdAt = createdAt;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getCategoryType() {
return categoryType;
}
public MemberResponse getCreator() {
return creator;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/exception/ExistExternalCategoryException.java
================================================
package com.allog.dallog.category.exception;
public class ExistExternalCategoryException extends RuntimeException {
public ExistExternalCategoryException(final String message) {
super(message);
}
public ExistExternalCategoryException() {
this("이미 저장된 연동 카테고리입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/exception/InvalidCategoryException.java
================================================
package com.allog.dallog.category.exception;
public class InvalidCategoryException extends RuntimeException {
public InvalidCategoryException(final String message) {
super(message);
}
public InvalidCategoryException() {
this("잘못된 카테고리입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/exception/NoSuchCategoryException.java
================================================
package com.allog.dallog.category.exception;
public class NoSuchCategoryException extends RuntimeException {
public NoSuchCategoryException(final String message) {
super(message);
}
public NoSuchCategoryException() {
this("존재하지 않는 카테고리입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/exception/NoSuchExternalCategoryDetailException.java
================================================
package com.allog.dallog.category.exception;
public class NoSuchExternalCategoryDetailException extends RuntimeException {
public NoSuchExternalCategoryDetailException(final String message) {
super(message);
}
public NoSuchExternalCategoryDetailException() {
this("존재하지 않는 외부 카테고리 정보 입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/category/presentaion/CategoryController.java
================================================
package com.allog.dallog.category.presentaion;
import com.allog.dallog.auth.dto.LoginMember;
import com.allog.dallog.auth.presentation.AuthenticationPrincipal;
import com.allog.dallog.category.dto.request.CategoryCreateRequest;
import com.allog.dallog.category.dto.request.CategoryUpdateRequest;
import com.allog.dallog.category.dto.response.CategoriesResponse;
import com.allog.dallog.category.dto.response.CategoryDetailResponse;
import com.allog.dallog.category.dto.response.CategoryResponse;
import com.allog.dallog.categoryrole.dto.request.CategoryRoleUpdateRequest;
import com.allog.dallog.category.application.CategoryService;
import com.allog.dallog.categoryrole.application.CategoryRoleService;
import com.allog.dallog.categoryrole.dto.response.SubscribersResponse;
import java.net.URI;
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api/categories")
@RestController
public class CategoryController {
private final CategoryService categoryService;
private final CategoryRoleService categoryRoleService;
public CategoryController(final CategoryService categoryService, final CategoryRoleService categoryRoleService) {
this.categoryService = categoryService;
this.categoryRoleService = categoryRoleService;
}
@PostMapping
public ResponseEntity<CategoryResponse> save(@AuthenticationPrincipal final LoginMember loginMember,
@Valid @RequestBody final CategoryCreateRequest request) {
CategoryResponse categoryResponse = categoryService.save(loginMember.getId(), request);
return ResponseEntity.created(URI.create("/api/categories/" + categoryResponse.getId())).body(categoryResponse);
}
@GetMapping
public ResponseEntity<CategoriesResponse> findNormalByName(@RequestParam(defaultValue = "") final String name) {
return ResponseEntity.ok(categoryService.findNormalByName(name));
}
@GetMapping("/{categoryId}")
public ResponseEntity<CategoryDetailResponse> findDetailCategoryById(@PathVariable final Long categoryId) {
return ResponseEntity.ok(categoryService.findDetailCategoryById(categoryId));
}
@GetMapping("/me/schedule-editable") // 일정 추가, 수정 모달의 카테고리 목록에 사용됨
public ResponseEntity<CategoriesResponse> findScheduleEditableCategories(
@AuthenticationPrincipal final LoginMember loginMember) {
return ResponseEntity.ok(categoryService.findScheduleEditableCategories(loginMember.getId()));
}
@GetMapping("/me/admin") // 카테고리 관리 페이지에 접근할 수 있는지 판단하기 위해 사용됨
public ResponseEntity<CategoriesResponse> findAdminCategories(
@AuthenticationPrincipal final LoginMember loginMember) {
return ResponseEntity.ok(categoryService.findAdminCategories(loginMember.getId()));
}
@PatchMapping("/{categoryId}")
public ResponseEntity<Void> update(@AuthenticationPrincipal final LoginMember loginMember,
@PathVariable final Long categoryId,
@Valid @RequestBody final CategoryUpdateRequest request) {
categoryService.update(loginMember.getId(), categoryId, request);
return ResponseEntity.noContent().build();
}
@DeleteMapping("/{categoryId}")
public ResponseEntity<Void> delete(@AuthenticationPrincipal final LoginMember loginMember,
@PathVariable final Long categoryId) {
categoryService.delete(loginMember.getId(), categoryId);
return ResponseEntity.noContent().build();
}
@GetMapping("/{categoryId}/subscribers")
public ResponseEntity<SubscribersResponse> findSubscribers(@AuthenticationPrincipal final LoginMember loginMember,
@PathVariable final Long categoryId) {
SubscribersResponse subscribers = categoryRoleService.findSubscribers(loginMember.getId(), categoryId);
return ResponseEntity.ok(subscribers);
}
@PatchMapping("/{categoryId}/subscribers/{memberId}/role")
public ResponseEntity<Void> updateRole(@AuthenticationPrincipal final LoginMember loginMember,
@PathVariable final Long categoryId,
@PathVariable final Long memberId,
@RequestBody final CategoryRoleUpdateRequest request) {
categoryRoleService.updateRole(loginMember.getId(), memberId, categoryId, request);
return ResponseEntity.noContent().build();
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/application/CategoryRoleService.java
================================================
package com.allog.dallog.categoryrole.application;
import com.allog.dallog.categoryrole.domain.CategoryAuthority;
import com.allog.dallog.categoryrole.domain.CategoryRoleRepository;
import com.allog.dallog.categoryrole.dto.request.CategoryRoleUpdateRequest;
import com.allog.dallog.categoryrole.dto.response.SubscribersResponse;
import com.allog.dallog.category.domain.Category;
import com.allog.dallog.categoryrole.domain.CategoryRole;
import com.allog.dallog.categoryrole.exception.NotAbleToChangeRoleException;
import java.util.List;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true)
@Service
public class CategoryRoleService {
private final CategoryRoleRepository categoryRoleRepository;
public CategoryRoleService(final CategoryRoleRepository categoryRoleRepository) {
this.categoryRoleRepository = categoryRoleRepository;
}
public SubscribersResponse findSubscribers(final Long loginMemberId, final Long categoryId) {
CategoryRole categoryRole = categoryRoleRepository.getByMemberIdAndCategoryId(loginMemberId, categoryId);
categoryRole.validateAuthority(CategoryAuthority.FIND_SUBSCRIBERS);
List<CategoryRole> categoryRoles = categoryRoleRepository.findByCategoryId(categoryId);
return new SubscribersResponse(categoryRoles);
}
@Transactional
public void updateRole(final Long loginMemberId, final Long memberId, final Long categoryId,
final CategoryRoleUpdateRequest request) {
try {
List<CategoryRole> categoryRolesInCategory = categoryRoleRepository.findByCategoryId(categoryId);
CategoryRole roleOfTargetMember = getCategoryRole(memberId, categoryRolesInCategory);
validateLoginMemberAuthority(loginMemberId, categoryRolesInCategory); // 요청 유저 권한 검증
validateIsTargetMemberSoleAdmin(categoryRolesInCategory, roleOfTargetMember); // 대상 유저가 유일한 어드민이 아닌지 검증
validateCategoryType(roleOfTargetMember.getCategory()); // 카테고리가 개인, 외부 카테고리가 아닌지 검증
categoryRoleRepository.validateManagingCategoryLimit(memberId, request.getCategoryRoleType()); // 관리 개수 검증
roleOfTargetMember.changeRole(request.getCategoryRoleType());
} catch (final ObjectOptimisticLockingFailureException e) {
throw NotAbleToChangeRoleException.concurrentIssue();
}
}
private CategoryRole getCategoryRole(final Long memberId, final List<CategoryRole> categoryRoles) {
return categoryRoles.stream()
.filter(it -> it.getMember().getId().equals(memberId))
.findFirst()
.orElseThrow();
}
private void validateLoginMemberAuthority(final Long loginMemberId, final List<CategoryRole> categoryRoles) {
CategoryRole loginMemberCategoryRole = categoryRoles.stream()
.filter(categoryRole -> categoryRole.getMember().getId().equals(loginMemberId))
.findFirst()
.orElseThrow();
loginMemberCategoryRole.validateAuthority(CategoryAuthority.CHANGE_ROLE_OF_SUBSCRIBER);
}
private void validateIsTargetMemberSoleAdmin(final List<CategoryRole> categoryRoles,
final CategoryRole categoryRole) {
if (categoryRole.isAdmin() && categoryRoles.size() == 1) {
throw new NotAbleToChangeRoleException();
}
}
private void validateCategoryType(final Category category) {
if (!category.isNormal()) {
throw new NotAbleToChangeRoleException("개인 카테고리 또는 외부 카테고리에 대한 회원의 역할을 변경할 수 없습니다.");
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryAuthority.java
================================================
package com.allog.dallog.categoryrole.domain;
public enum CategoryAuthority {
UPDATE_CATEGORY("카테고리 수정"),
DELETE_CATEGORY("카테고리 제거"),
ADD_SCHEDULE("일정 추가"),
UPDATE_SCHEDULE("일정 수정"),
DELETE_SCHEDULE("일정 제거"),
CHANGE_ROLE_OF_SUBSCRIBER("역할 변경"),
FIND_SUBSCRIBERS("카테고리 구독자 조회");
private final String name;
CategoryAuthority(final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryRole.java
================================================
package com.allog.dallog.categoryrole.domain;
import com.allog.dallog.category.domain.Category;
import com.allog.dallog.global.entity.BaseEntity;
import com.allog.dallog.categoryrole.exception.NoCategoryAuthorityException;
import com.allog.dallog.member.domain.Member;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Version;
@Table(name = "category_roles")
@Entity
public class CategoryRole extends BaseEntity {
public static final int MAX_MANAGING_CATEGORY_COUNT = 50;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "categories_id", nullable = false)
private Category category;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "members_id", nullable = false)
private Member member;
@Enumerated(EnumType.STRING)
private CategoryRoleType categoryRoleType;
@Version
private Long version;
protected CategoryRole() {
}
public CategoryRole(final Category category, final Member member, final CategoryRoleType categoryRoleType) {
this.category = category;
this.member = member;
this.categoryRoleType = categoryRoleType;
}
public boolean isAdmin() {
return categoryRoleType.equals(CategoryRoleType.ADMIN);
}
public boolean isNone() {
return categoryRoleType.equals(CategoryRoleType.NONE);
}
public void validateAuthority(final CategoryAuthority authority) {
if (!ableTo(authority)) {
throw new NoCategoryAuthorityException(authority.getName());
}
}
public boolean ableTo(final CategoryAuthority authority) {
return categoryRoleType.ableTo(authority);
}
public void changeRole(final CategoryRoleType categoryRoleType) {
this.categoryRoleType = categoryRoleType;
}
public Long getId() {
return id;
}
public Category getCategory() {
return category;
}
public Member getMember() {
return member;
}
public CategoryRoleType getCategoryRoleType() {
return categoryRoleType;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryRoleRepository.java
================================================
package com.allog.dallog.categoryrole.domain;
import com.allog.dallog.categoryrole.exception.ManagingCategoryLimitExcessException;
import com.allog.dallog.categoryrole.exception.NoSuchCategoryRoleException;
import java.util.List;
import java.util.Optional;
import javax.persistence.LockModeType;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
public interface CategoryRoleRepository extends JpaRepository<CategoryRole, Long> {
@Lock(LockModeType.OPTIMISTIC)
@Query("SELECT cr "
+ "FROM CategoryRole cr "
+ "WHERE cr.member.id = :memberId AND cr.category.id = :categoryId")
Optional<CategoryRole> findByMemberIdAndCategoryId(final Long memberId, final Long categoryId);
@EntityGraph(attributePaths = {"member"})
List<CategoryRole> findByCategoryId(final Long categoryId);
@EntityGraph(attributePaths = {"category", "category.member"})
List<CategoryRole> findByMemberId(final Long memberId);
@Query("SELECT count(cr) "
+ "FROM CategoryRole cr "
+ "WHERE cr.categoryRoleType = :categoryRoleType "
+ "AND cr.member.id = :memberId")
int countByMemberIdAndCategoryRoleType(final Long memberId, final CategoryRoleType categoryRoleType);
int countByCategoryIdAndCategoryRoleType(final Long categoryId, final CategoryRoleType categoryRoleType);
void deleteByCategoryId(final Long categoryId);
default CategoryRole getByMemberIdAndCategoryId(final Long memberId, final Long categoryId) {
return findByMemberIdAndCategoryId(memberId, categoryId)
.orElseThrow(NoSuchCategoryRoleException::new);
}
default boolean isMemberSoleAdminInCategory(final Long memberId, final Long categoryId) {
CategoryRole categoryRole = getByMemberIdAndCategoryId(memberId, categoryId);
int adminCount = countByCategoryIdAndCategoryRoleType(categoryId, CategoryRoleType.ADMIN);
return categoryRole.isAdmin() && adminCount == 1;
}
default void validateManagingCategoryLimit(final Long memberId, final CategoryRoleType categoryRoleType) {
int memberAdminCount = countByMemberIdAndCategoryRoleType(memberId, CategoryRoleType.ADMIN);
if (!categoryRoleType.equals(CategoryRoleType.NONE)
&& memberAdminCount >= CategoryRole.MAX_MANAGING_CATEGORY_COUNT) {
throw new ManagingCategoryLimitExcessException();
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryRoleType.java
================================================
package com.allog.dallog.categoryrole.domain;
import static com.allog.dallog.categoryrole.domain.CategoryAuthority.ADD_SCHEDULE;
import static com.allog.dallog.categoryrole.domain.CategoryAuthority.CHANGE_ROLE_OF_SUBSCRIBER;
import static com.allog.dallog.categoryrole.domain.CategoryAuthority.DELETE_CATEGORY;
import static com.allog.dallog.categoryrole.domain.CategoryAuthority.DELETE_SCHEDULE;
import static com.allog.dallog.categoryrole.domain.CategoryAuthority.FIND_SUBSCRIBERS;
import static com.allog.dallog.categoryrole.domain.CategoryAuthority.UPDATE_CATEGORY;
import static com.allog.dallog.categoryrole.domain.CategoryAuthority.UPDATE_SCHEDULE;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import java.util.stream.Collectors;
public enum CategoryRoleType {
ADMIN(EnumSet.of(UPDATE_CATEGORY, DELETE_CATEGORY, ADD_SCHEDULE, UPDATE_SCHEDULE, DELETE_SCHEDULE,
CHANGE_ROLE_OF_SUBSCRIBER, FIND_SUBSCRIBERS)),
NONE(Set.of());
private final Set<CategoryAuthority> authorities;
CategoryRoleType(final Set<CategoryAuthority> authorities) {
this.authorities = authorities;
}
public static Set<CategoryRoleType> getHavingAuthorities(final Set<CategoryAuthority> authorities) {
return Arrays.stream(values())
.filter(categoryRoleType -> isCategoryRoleTypeContainsAuthorities(authorities, categoryRoleType))
.collect(Collectors.toSet());
}
private static boolean isCategoryRoleTypeContainsAuthorities(final Set<CategoryAuthority> authorities,
final CategoryRoleType categoryRoleType) {
return categoryRoleType.authorities.containsAll(authorities);
}
public boolean ableTo(final CategoryAuthority authority) {
return authorities.contains(authority);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/dto/request/CategoryRoleUpdateRequest.java
================================================
package com.allog.dallog.categoryrole.dto.request;
import com.allog.dallog.categoryrole.domain.CategoryRoleType;
import javax.validation.constraints.NotBlank;
public class CategoryRoleUpdateRequest {
@NotBlank(message = "공백일 수 없습니다.")
private CategoryRoleType categoryRoleType;
private CategoryRoleUpdateRequest() {
}
public CategoryRoleUpdateRequest(final CategoryRoleType categoryRoleType) {
this.categoryRoleType = categoryRoleType;
}
public CategoryRoleType getCategoryRoleType() {
return categoryRoleType;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/dto/response/MemberWithRoleTypeResponse.java
================================================
package com.allog.dallog.categoryrole.dto.response;
import com.allog.dallog.categoryrole.domain.CategoryRoleType;
import com.allog.dallog.categoryrole.domain.CategoryRole;
import com.allog.dallog.member.dto.response.MemberResponse;
public class MemberWithRoleTypeResponse {
private MemberResponse member;
private CategoryRoleType categoryRoleType;
private MemberWithRoleTypeResponse() {
}
public MemberWithRoleTypeResponse(final CategoryRole categoryRole) {
this.member = new MemberResponse(categoryRole.getMember());
this.categoryRoleType = categoryRole.getCategoryRoleType();
}
public MemberResponse getMember() {
return member;
}
public CategoryRoleType getCategoryRoleType() {
return categoryRoleType;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/dto/response/SubscribersResponse.java
================================================
package com.allog.dallog.categoryrole.dto.response;
import com.allog.dallog.categoryrole.domain.CategoryRole;
import java.util.List;
import java.util.stream.Collectors;
public class SubscribersResponse {
private List<MemberWithRoleTypeResponse> subscribers;
private SubscribersResponse() {
}
public SubscribersResponse(final List<CategoryRole> categoryRoles) {
this.subscribers = toResponses(categoryRoles);
}
private List<MemberWithRoleTypeResponse> toResponses(final List<CategoryRole> categoryRoles) {
return categoryRoles.stream()
.map(MemberWithRoleTypeResponse::new)
.collect(Collectors.toList());
}
public List<MemberWithRoleTypeResponse> getSubscribers() {
return subscribers;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/exception/ManagingCategoryLimitExcessException.java
================================================
package com.allog.dallog.categoryrole.exception;
public class ManagingCategoryLimitExcessException extends RuntimeException {
private static final int MAX_MANAGING_CATEGORY_COUNT = 50;
public ManagingCategoryLimitExcessException() {
super("한 사람이 관리할 수 있는 카테고리는 최대 " + MAX_MANAGING_CATEGORY_COUNT + "개 입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/exception/NoCategoryAuthorityException.java
================================================
package com.allog.dallog.categoryrole.exception;
public class NoCategoryAuthorityException extends RuntimeException {
public NoCategoryAuthorityException(final String authorityName) {
super(authorityName + " 권한이 없습니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/exception/NoSuchCategoryRoleException.java
================================================
package com.allog.dallog.categoryrole.exception;
public class NoSuchCategoryRoleException extends RuntimeException {
public NoSuchCategoryRoleException(final String message) {
super(message);
}
public NoSuchCategoryRoleException() {
this("존재하지 않는 역할입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/categoryrole/exception/NotAbleToChangeRoleException.java
================================================
package com.allog.dallog.categoryrole.exception;
public class NotAbleToChangeRoleException extends RuntimeException {
public NotAbleToChangeRoleException(final String message) {
super(message);
}
public NotAbleToChangeRoleException() {
super("역할을 변경할 수 없습니다.");
}
public static NotAbleToChangeRoleException concurrentIssue() {
return new NotAbleToChangeRoleException("회원님의 권한이 변경되어 카테고리 역할을 수정할 수 없습니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/application/ExternalCalendarClient.java
================================================
package com.allog.dallog.externalcalendar.application;
import com.allog.dallog.externalcalendar.dto.ExternalCalendar;
import com.allog.dallog.schedule.domain.IntegrationSchedule;
import java.util.List;
public interface ExternalCalendarClient {
List<ExternalCalendar> getExternalCalendars(final String accessToken);
List<IntegrationSchedule> getExternalCalendarSchedules(final String accessToken,
final Long internalCategoryId,
final String externalCalendarId,
final String startDateTime, final String endDateTime);
}
================================================
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/application/ExternalCalendarService.java
================================================
package com.allog.dallog.externalcalendar.application;
import com.allog.dallog.auth.application.OAuthClient;
import com.allog.dallog.auth.domain.OAuthToken;
import com.allog.dallog.auth.domain.OAuthTokenRepository;
import com.allog.dallog.externalcalendar.dto.ExternalCalendarsResponse;
import org.springframework.stereotype.Service;
@Service
public class ExternalCalendarService {
private final OAuthClient oAuthClient;
private final ExternalCalendarClient externalCalendarClient;
private final OAuthTokenRepository oAuthTokenRepository;
public ExternalCalendarService(final OAuthClient oAuthClient, final ExternalCalendarClient externalCalendarClient,
final OAuthTokenRepository oAuthTokenRepository) {
this.oAuthClient = oAuthClient;
this.externalCalendarClient = externalCalendarClient;
this.oAuthTokenRepository = oAuthTokenRepository;
}
public ExternalCalendarsResponse findByMemberId(final Long memberId) {
OAuthToken oAuthToken = oAuthTokenRepository.getByMemberId(memberId);
String oAuthAccessToken = oAuthClient.getAccessToken(oAuthToken.getRefreshToken()).getAccessToken();
return new ExternalCalendarsResponse(externalCalendarClient.getExternalCalendars(oAuthAccessToken));
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/dto/ExternalCalendar.java
================================================
package com.allog.dallog.externalcalendar.dto;
public class ExternalCalendar {
private String calendarId;
private String summary;
private ExternalCalendar() {
}
public ExternalCalendar(final String calendarId, final String summary) {
this.calendarId = calendarId;
this.summary = summary;
}
public String getCalendarId() {
return calendarId;
}
public String getSummary() {
return summary;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/dto/ExternalCalendarsResponse.java
================================================
package com.allog.dallog.externalcalendar.dto;
import java.util.List;
public class ExternalCalendarsResponse {
private List<ExternalCalendar> externalCalendars;
private ExternalCalendarsResponse() {
}
public ExternalCalendarsResponse(final List<ExternalCalendar> externalCalendars) {
this.externalCalendars = externalCalendars;
}
public List<ExternalCalendar> getExternalCalendars() {
return externalCalendars;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/presentation/ExternalCalendarController.java
================================================
package com.allog.dallog.externalcalendar.presentation;
import com.allog.dallog.auth.dto.LoginMember;
import com.allog.dallog.auth.presentation.AuthenticationPrincipal;
import com.allog.dallog.category.dto.request.ExternalCategoryCreateRequest;
import com.allog.dallog.category.dto.response.CategoryResponse;
import com.allog.dallog.category.application.CategoryService;
import com.allog.dallog.externalcalendar.application.ExternalCalendarService;
import com.allog.dallog.externalcalendar.dto.ExternalCalendarsResponse;
import java.net.URI;
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api/external-calendars/me")
@RestController
public class ExternalCalendarController {
private final ExternalCalendarService externalCalendarService;
private final CategoryService categoryService;
public ExternalCalendarController(final ExternalCalendarService externalCalendarService,
final CategoryService categoryService) {
this.externalCalendarService = externalCalendarService;
this.categoryService = categoryService;
}
@GetMapping
public ResponseEntity<ExternalCalendarsResponse> getExternalCalendar(
@AuthenticationPrincipal final LoginMember loginMember) {
return ResponseEntity.ok(externalCalendarService.findByMemberId(loginMember.getId()));
}
@PostMapping
public ResponseEntity<CategoryResponse> save(@AuthenticationPrincipal final LoginMember loginMember,
@Valid @RequestBody final ExternalCategoryCreateRequest request) {
CategoryResponse response = categoryService.save(loginMember.getId(), request);
return ResponseEntity.created(URI.create("/api/categories/" + response.getId())).body(response);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/JpaConfig.java
================================================
package com.allog.dallog.global.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@Configuration
@EnableJpaAuditing
public class JpaConfig {
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/PropertiesConfig.java
================================================
package com.allog.dallog.global.config;
import com.allog.dallog.global.config.properties.GoogleProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(GoogleProperties.class)
public class PropertiesConfig {
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/WebConfig.java
================================================
package com.allog.dallog.global.config;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final List<String> allowOriginUrlPatterns;
private final HandlerMethodArgumentResolver authenticationPrincipalArgumentResolver;
public WebConfig(@Value("${cors.allow-origin.urls}") final List<String> allowOriginUrlPatterns,
final HandlerMethodArgumentResolver authenticationPrincipalArgumentResolver) {
this.allowOriginUrlPatterns = allowOriginUrlPatterns;
this.authenticationPrincipalArgumentResolver = authenticationPrincipalArgumentResolver;
}
@Override
public void addCorsMappings(CorsRegistry registry) {
String[] patterns = allowOriginUrlPatterns.stream()
.toArray(String[]::new);
registry.addMapping("/**")
.allowedMethods("*")
.allowedOriginPatterns(patterns);
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(authenticationPrincipalArgumentResolver);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/cache/CacheConfig.java
================================================
package com.allog.dallog.global.config.cache;
import java.util.List;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
@Configuration
@EnableCaching
@EnableScheduling
public class CacheConfig {
public static final String GOOGLE_CALENDAR = "googleCalendar";
private static final long EXPIRE_AFTER = 60 * 60 * 3;
@Bean
public CacheManager cacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
simpleCacheManager.setCaches(List.of(new ExpiringConcurrentMapCache(GOOGLE_CALENDAR, EXPIRE_AFTER)));
return simpleCacheManager;
}
@Scheduled(cron = "0 0 0 * * *")
private void evict() {
ExpiringConcurrentMapCache cache = (ExpiringConcurrentMapCache) cacheManager().getCache(GOOGLE_CALENDAR);
cache.evictAllExpired();
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/cache/ExpiringConcurrentMapCache.java
================================================
package com.allog.dallog.global.config.cache;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.springframework.cache.concurrent.ConcurrentMapCache;
public class ExpiringConcurrentMapCache extends ConcurrentMapCache {
private final Map<Object, LocalDateTime> expires = new ConcurrentHashMap<>();
private final long expireAfter;
public ExpiringConcurrentMapCache(final String name, final long expireAfter) {
super(name);
this.expireAfter = expireAfter;
}
@Override
protected Object lookup(final Object key) {
LocalDateTime expiredDate = expires.get(key);
if (Objects.isNull(expiredDate) || isCacheValid(expiredDate)) {
return super.lookup(key);
}
expires.remove(key);
super.evict(key);
return null;
}
@Override
public void put(final Object key, final Object value) {
LocalDateTime expiredAt = LocalDateTime.now().plusSeconds(expireAfter);
expires.put(key, expiredAt);
super.put(key, value);
}
public void evictAllExpired() {
ConcurrentMap<Object, Object> nativeCache = getNativeCache();
nativeCache.keySet()
.stream()
.filter(cacheKey -> !isCacheValid(expires.get(cacheKey)))
.forEach(super::evict);
}
private boolean isCacheValid(final LocalDateTime expiredDate) {
return LocalDateTime.now().isBefore(expiredDate);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/properties/GoogleProperties.java
================================================
package com.allog.dallog.global.config.properties;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
@ConfigurationProperties("oauth.google")
@ConstructorBinding
public class GoogleProperties {
private final String clientId;
private final String clientSecret;
private final String oAuthEndPoint;
private final String responseType;
private final List<String> scopes;
private final String tokenUri;
private final String accessType;
public GoogleProperties(final String clientId, final String clientSecret, final String oAuthEndPoint,
final String responseType, final List<String> scopes, final String tokenUri,
final String accessType) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.oAuthEndPoint = oAuthEndPoint;
this.responseType = responseType;
this.scopes = scopes;
this.tokenUri = tokenUri;
this.accessType = accessType;
}
public String getClientId() {
return clientId;
}
public String getClientSecret() {
return clientSecret;
}
public String getOAuthEndPoint() {
return oAuthEndPoint;
}
public String getResponseType() {
return responseType;
}
public List<String> getScopes() {
return scopes;
}
public String getTokenUri() {
return tokenUri;
}
public String getAccessType() {
return accessType;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/replication/DataSourceConfiguration.java
================================================
package com.allog.dallog.global.config.replication;
import static com.allog.dallog.global.config.replication.DataSourceKey.KeyName.REPLICA_1_NAME;
import static com.allog.dallog.global.config.replication.DataSourceKey.KeyName.REPLICA_2_NAME;
import static com.allog.dallog.global.config.replication.DataSourceKey.KeyName.SOURCE_NAME;
import static com.allog.dallog.global.config.replication.DataSourceKey.REPLICA_1;
import static com.allog.dallog.global.config.replication.DataSourceKey.REPLICA_2;
import static com.allog.dallog.global.config.replication.DataSourceKey.SOURCE;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
@Configuration
@Profile({"prod", "dev"})
public class DataSourceConfiguration {
@Bean
@Primary
public DataSource dataSource() {
DataSource determinedDataSource = routingDataSource(sourceDataSource(), replica1DataSource(),
replica2DataSource());
return new LazyConnectionDataSourceProxy(determinedDataSource);
}
@Bean
@Qualifier(SOURCE_NAME)
@ConfigurationProperties(prefix = "spring.datasource.source")
public DataSource sourceDataSource() {
return DataSourceBuilder.create()
.build();
}
@Bean
@Qualifier(REPLICA_1_NAME)
@ConfigurationProperties(prefix = "spring.datasource.replica1")
public DataSource replica1DataSource() {
return DataSourceBuilder.create()
.build();
}
@Bean
@Qualifier(REPLICA_2_NAME)
@ConfigurationProperties(prefix = "spring.datasource.replica2")
public DataSource replica2DataSource() {
return DataSourceBuilder.create()
.build();
}
@Bean
public DataSource routingDataSource(
@Qualifier(SOURCE_NAME) DataSource sourceDataSource,
@Qualifier(REPLICA_1_NAME) DataSource replica1DataSource,
@Qualifier(REPLICA_2_NAME) DataSource replica2DataSource
) {
Map<Object, Object> dataSources = Map.of(
SOURCE, sourceDataSource, REPLICA_1, replica1DataSource, REPLICA_2, replica2DataSource
);
RoutingDataSource routingDataSource = new RoutingDataSource();
routingDataSource.setTargetDataSources(dataSources);
routingDataSource.setDefaultTargetDataSource(sourceDataSource);
return routingDataSource;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/replication/DataSourceKey.java
================================================
package com.allog.dallog.global.config.replication;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public enum DataSourceKey {
SOURCE(KeyName.SOURCE_NAME, false),
REPLICA_1(KeyName.REPLICA_1_NAME, true),
REPLICA_2(KeyName.REPLICA_2_NAME, true);
private final String key;
private final boolean isReplica;
DataSourceKey(final String key, final boolean isReplica) {
this.key = key;
this.isReplica = isReplica;
}
public static List<DataSourceKey> getReplicas() {
return Arrays.stream(values())
.filter(key -> key.isReplica)
.collect(Collectors.toList());
}
// 어노테이션에서도 참조할 수 있도록 중첩 클래스에 상수 선언
public static class KeyName {
public static final String SOURCE_NAME = "SOURCE";
public static final String REPLICA_1_NAME = "REPLICA_1";
public static final String REPLICA_2_NAME = "REPLICA_2";
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/replication/RandomReplicaKeys.java
================================================
package com.allog.dallog.global.config.replication;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class RandomReplicaKeys {
private static final ThreadLocalRandom random = ThreadLocalRandom.current();
private final List<DataSourceKey> dataSourceKeys;
private final int size;
public RandomReplicaKeys() {
this.dataSourceKeys = List.copyOf(DataSourceKey.getReplicas());
this.size = dataSourceKeys.size();
}
public DataSourceKey next() {
int currentDataSourceIndex = random.nextInt(size);
return dataSourceKeys.get(currentDataSourceIndex);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/config/replication/RoutingDataSource.java
================================================
package com.allog.dallog.global.config.replication;
import static com.allog.dallog.global.config.replication.DataSourceKey.SOURCE;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class RoutingDataSource extends AbstractRoutingDataSource {
private final RandomReplicaKeys randomReplicaKeys = new RandomReplicaKeys();
@Override
protected Object determineCurrentLookupKey() {
boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
if (isReadOnly) {
return randomReplicaKeys.next();
}
return SOURCE;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/entity/BaseEntity.java
================================================
package com.allog.dallog.global.entity;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
public LocalDateTime getCreatedAt() {
return createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java
================================================
package com.allog.dallog.global.error;
import com.allog.dallog.auth.exception.EmptyAuthorizationHeaderException;
import com.allog.dallog.auth.exception.InvalidTokenException;
import com.allog.dallog.auth.exception.NoPermissionException;
import com.allog.dallog.auth.exception.NoSuchOAuthTokenException;
import com.allog.dallog.auth.exception.NoSuchTokenException;
import com.allog.dallog.category.exception.ExistExternalCategoryException;
import com.allog.dallog.category.exception.InvalidCategoryException;
import com.allog.dallog.category.exception.NoSuchCategoryException;
import com.allog.dallog.categoryrole.exception.ManagingCategoryLimitExcessException;
import com.allog.dallog.categoryrole.exception.NoCategoryAuthorityException;
import com.allog.dallog.categoryrole.exception.NoSuchCategoryRoleException;
import com.allog.dallog.categoryrole.exception.NotAbleToChangeRoleException;
import com.allog.dallog.member.exception.InvalidMemberException;
import com.allog.dallog.member.exception.NoSuchMemberException;
import com.allog.dallog.schedule.exception.InvalidScheduleException;
import com.allog.dallog.schedule.exception.NoSuchScheduleException;
import com.allog.dallog.subscription.exception.ExistSubscriptionException;
import com.allog.dallog.subscription.exception.InvalidSubscriptionException;
import com.allog.dallog.subscription.exception.NoSuchSubscriptionException;
import com.allog.dallog.subscription.exception.NotAbleToUnsubscribeException;
import com.allog.dallog.global.error.dto.ErrorReportRequest;
import com.allog.dallog.global.error.dto.ErrorResponse;
import com.allog.dallog.infrastructure.oauth.exception.OAuthException;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
@RestControllerAdvice
public class ControllerAdvice {
private static final Logger log = LoggerFactory.getLogger(ControllerAdvice.class);
private static final String INVALID_DTO_FIELD_ERROR_MESSAGE_FORMAT = "%s 필드는 %s (전달된 값: %s)";
@ExceptionHandler({
InvalidCategoryException.class,
InvalidMemberException.class,
InvalidScheduleException.class,
InvalidSubscriptionException.class,
ExistSubscriptionException.class,
ExistExternalCategoryException.class,
NotAbleToChangeRoleException.class,
ManagingCategoryLimitExcessException.class,
NotAbleToUnsubscribeException.class
})
public ResponseEntity<ErrorResponse> handleInvalidData(final RuntimeException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());
return ResponseEntity.badRequest().body(errorResponse);
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ErrorResponse> handleInvalidRequestBody() {
ErrorResponse errorResponse = new ErrorResponse("잘못된 형식의 Request Body 입니다.");
return ResponseEntity.badRequest().body(errorResponse);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleInvalidDtoField(final MethodArgumentNotValidException e) {
FieldError firstFieldError = e.getFieldErrors().get(0);
String errorMessage = String.format(INVALID_DTO_FIELD_ERROR_MESSAGE_FORMAT, firstFieldError.getField(),
firstFieldError.getDefaultMessage(), firstFieldError.getRejectedValue());
ErrorResponse errorResponse = new ErrorResponse(errorMessage);
return ResponseEntity.badRequest().body(errorResponse);
}
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<ErrorResponse> handleTypeMismatch() {
ErrorResponse errorResponse = new ErrorResponse("잘못된 데이터 타입입니다.");
return ResponseEntity.badRequest().body(errorResponse);
}
@ExceptionHandler({
EmptyAuthorizationHeaderException.class,
InvalidTokenException.class
})
public ResponseEntity<ErrorResponse> handleInvalidAuthorization(final RuntimeException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(errorResponse);
}
@ExceptionHandler({
NoPermissionException.class,
NoCategoryAuthorityException.class,
})
public ResponseEntity<ErrorResponse> handleNoPermission(final RuntimeException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(errorResponse);
}
@ExceptionHandler({
NoSuchCategoryException.class,
NoSuchMemberException.class,
NoSuchSubscriptionException.class,
NoSuchScheduleException.class,
NoSuchTokenException.class,
NoSuchOAuthTokenException.class,
NoSuchCategoryRoleException.class
})
public ResponseEntity<ErrorResponse> handleNoSuchData(final RuntimeException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<ErrorResponse> handleNotSupportedMethod() {
ErrorResponse errorResponse = new ErrorResponse("지원하지 않는 HTTP 메소드 요청입니다.");
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(errorResponse);
}
@ExceptionHandler(OAuthException.class)
public ResponseEntity<ErrorResponse> handleOAuthException(final RuntimeException e) {
log.error(e.getMessage(), e);
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());
return ResponseEntity.internalServerError().body(errorResponse);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleUnexpectedException(final Exception e,
final HttpServletRequest request) {
ErrorReportRequest errorReport = new ErrorReportRequest(request, e);
log.error(errorReport.getLogMessage(), e);
ErrorResponse errorResponse = new ErrorResponse("예상하지 못한 서버 에러가 발생했습니다.");
return ResponseEntity.internalServerError().body(errorResponse);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/error/dto/ErrorReportRequest.java
================================================
package com.allog.dallog.global.error.dto;
import javax.servlet.http.HttpServletRequest;
public class ErrorReportRequest {
private static final String ERROR_REPORT_FORMAT = "[%s] %s";
private final HttpServletRequest request;
private final Exception exception;
public ErrorReportRequest(final HttpServletRequest request, final Exception exception) {
this.request = request;
this.exception = exception;
}
public String getLogMessage() {
String requestUri = request.getRequestURI();
String requestMethod = request.getMethod();
return String.format(ERROR_REPORT_FORMAT, requestMethod, requestUri);
}
public HttpServletRequest getRequest() {
return request;
}
public Exception getException() {
return exception;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/global/error/dto/ErrorResponse.java
================================================
package com.allog.dallog.global.error.dto;
public class ErrorResponse {
private final String message;
public ErrorResponse(final String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/log/DiscordAppender.java
================================================
package com.allog.dallog.infrastructure.log;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import com.allog.dallog.infrastructure.log.dto.DiscordWebhookRequest;
import com.allog.dallog.infrastructure.log.dto.Embed;
import com.allog.dallog.infrastructure.log.dto.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
public class DiscordAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
private static final String TITLE_FORMAT = "[%s] %s";
private static final String DESCRIPTION_FORMAT = "%s: %s";
private static final RestTemplate CLIENT;
static {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(3000);
CLIENT = new RestTemplate(factory);
}
private String username;
private String embedsColor;
private int stackTraceMaxSize;
private String webhookUri;
@Override
protected void append(final ILoggingEvent eventObject) {
if (!Objects.isNull(webhookUri) && !webhookUri.isEmpty()) {
String title = getTitle(eventObject);
List<Embed> embeds = getEmbeds(title, embedsColor, eventObject);
DiscordWebhookRequest request = new DiscordWebhookRequest(username, embeds);
CLIENT.postForEntity(webhookUri, request, Void.class);
}
}
private String getTitle(final ILoggingEvent eventObject) {
return String.format(TITLE_FORMAT, eventObject.getLevel(), eventObject.getMessage());
}
private List<Embed> getEmbeds(final String title, final String embedsColor, final ILoggingEvent eventObject) {
if (Objects.isNull(eventObject.getThrowableProxy())) {
return List.of(new Embed(title, embedsColor));
}
IThrowableProxy throwableProxy = eventObject.getThrowableProxy();
String description = getDescription(throwableProxy);
List<Field> fields = getFields(throwableProxy);
return List.of(new Embed(title, description, embedsColor, fields));
}
private String getDescription(final IThrowableProxy throwableProxy) {
return String.format(DESCRIPTION_FORMAT, throwableProxy.getClassName(), throwableProxy.getMessage());
}
private List<Field> getFields(final IThrowableProxy throwableProxy) {
List<String> stackTraces = Arrays.stream(throwableProxy.getStackTraceElementProxyArray())
.map(StackTraceElementProxy::getSTEAsString)
.limit(stackTraceMaxSize)
.collect(Collectors.toList());
return stackTraces.stream()
.map(Field::from)
.collect(Collectors.toList());
}
public String getUsername() {
return username;
}
public void setUsername(final String username) {
this.username = username;
}
public String getEmbedsColor() {
return embedsColor;
}
public void setEmbedsColor(final String embedsColor) {
this.embedsColor = embedsColor;
}
public int getStackTraceMaxSize() {
return stackTraceMaxSize;
}
public void setStackTraceMaxSize(final int stackTraceMaxSize) {
this.stackTraceMaxSize = stackTraceMaxSize;
}
public String getWebhookUri() {
return webhookUri;
}
public void setWebhookUri(final String webhookUri) {
this.webhookUri = webhookUri;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/log/dto/DiscordWebhookRequest.java
================================================
package com.allog.dallog.infrastructure.log.dto;
import java.util.List;
public class DiscordWebhookRequest {
private String username;
private List<Embed> embeds;
private DiscordWebhookRequest() {
}
public DiscordWebhookRequest(final String username, final List<Embed> embeds) {
this.username = username;
this.embeds = embeds;
}
public String getUsername() {
return username;
}
public List<Embed> getEmbeds() {
return embeds;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Embed.java
================================================
package com.allog.dallog.infrastructure.log.dto;
import java.util.List;
public class Embed {
private String title;
private String description;
private String color;
private List<Field> fields;
private Embed() {
}
public Embed(final String title, final String color) {
this(title, null, color, null);
}
public Embed(final String title, final String description, final String color, final List<Field> fields) {
this.title = title;
this.description = description;
this.color = color;
this.fields = fields;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getColor() {
return color;
}
public List<Field> getFields() {
return fields;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Field.java
================================================
package com.allog.dallog.infrastructure.log.dto;
public class Field {
private String name;
private String value;
private Field() {
}
private Field(final String name, final String value) {
this.name = name;
this.value = value;
}
public static Field from(final String steAsString) {
String name = steAsString.substring(steAsString.indexOf("(") + 1, steAsString.indexOf(")"));
String value = steAsString.substring(0, steAsString.indexOf("("));
return new Field(name, value);
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java
================================================
package com.allog.dallog.infrastructure.oauth.client;
import com.allog.dallog.externalcalendar.application.ExternalCalendarClient;
import com.allog.dallog.externalcalendar.dto.ExternalCalendar;
import com.allog.dallog.schedule.domain.IntegrationSchedule;
import com.allog.dallog.global.config.cache.CacheConfig;
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.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
@Component
public class GoogleExternalCalendarClient implements ExternalCalendarClient {
private static final String CALENDAR_LIST_REQUEST_URI = "https://www.googleapis.com/calendar/v3/users/me/calendarList";
private static final String CALENDAR_EVENTS_REQUEST_URI = "https://www.googleapis.com/calendar/v3/calendars/{calendarId}/events?singleEvents=true&timeMax={timeMax}&timeMin={timeMin}";
private static final String ACCEPT_HEADER_NAME = "Accept";
private final RestTemplate restTemplate;
public GoogleExternalCalendarClient(final RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
@Override
public List<ExternalCalendar> getExternalCalendars(final String accessToken) {
HttpEntity<Void> request = new HttpEntity<>(generateCalendarRequestHeaders(accessToken));
GoogleCalendarListResponse response = fetchGoogleCalendarList(request).getBody();
return response.getItems()
.stream()
.map(item -> new ExternalCalendar(item.getId(), item.getSummary()))
.collect(Collectors.toList());
}
private ResponseEntity<GoogleCalendarListResponse> fetchGoogleCalendarList(final HttpEntity<Void> request) {
try {
return restTemplate.exchange(CALENDAR_LIST_REQUEST_URI, HttpMethod.GET, request,
GoogleCalendarListResponse.class);
} catch (final HttpClientErrorException e) {
throw new OAuthException("외부 캘린더에 대한 권한이 없습니다.", e);
} catch (final RestClientException e) {
throw new OAuthException("외부 캘린더를 가져올 수 없습니다.", e);
}
}
@Override
@Cacheable(value = CacheConfig.GOOGLE_CALENDAR, key = "#internalCategoryId+#externalCalendarId+#startDateTime+#endDateTime")
public List<IntegrationSchedule> getExternalCalendarSchedules(final String accessToken,
final Long internalCategoryId,
final String externalCalendarId,
final String startDateTime,
final String endDateTime) {
HttpEntity<Void> request = new HttpEntity<>(generateCalendarRequestHeaders(accessToken));
Map<String, String> uriVariables = generateEventsVariables(externalCalendarId, startDateTime, endDateTime);
GoogleCalendarEventsResponse response = fetchGoogleCalendarEvents(request, uriVariables).getBody();
return response.getItems()
.stream()
.map(event -> event.toIntegrationSchedule(internalCategoryId))
.collect(Collectors.toList());
}
private HttpHeaders generateCalendarRequestHeaders(final String accessToken) {
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
headers.set(ACCEPT_HEADER_NAME, MediaType.APPLICATION_JSON_VALUE);
return headers;
}
private Map<String, String> generateEventsVariables(final String externalCalendarId, final String startDateTime,
final String endDateTime) {
return Map.of(
"calendarId", externalCalendarId,
"timeMax", endDateTime + "Z",
"timeMin", startDateTime + "Z"
);
}
private ResponseEntity<GoogleCalendarEventsResponse> fetchGoogleCalendarEvents(
final HttpEntity<Void> request, final Map<String, String> uriVariables) {
try {
return restTemplate.exchange(CALENDAR_EVENTS_REQUEST_URI, HttpMethod.GET, request,
GoogleCalendarEventsResponse.class, uriVariables);
} catch (final HttpClientErrorException e) {
throw new OAuthException("외부 일정에 대한 권한이 없습니다.", e);
} catch (final RestClientException e) {
throw new OAuthException("외부 일정을 가져올 수 없습니다.", e);
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleOAuthClient.java
================================================
package com.allog.dallog.infrastructure.oauth.client;
import com.allog.dallog.auth.application.OAuthClient;
import com.allog.dallog.auth.dto.OAuthMember;
import com.allog.dallog.auth.dto.response.OAuthAccessTokenResponse;
import com.allog.dallog.global.config.properties.GoogleProperties;
import com.allog.dallog.infrastructure.oauth.dto.GoogleTokenResponse;
import com.allog.dallog.infrastructure.oauth.dto.UserInfo;
import com.allog.dallog.infrastructure.oauth.exception.OAuthException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
@Component
public class GoogleOAuthClient implements OAuthClient {
private static final String JWT_DELIMITER = "\\.";
private final GoogleProperties properties;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public GoogleOAuthClient(final GoogleProperties properties, final RestTemplateBuilder restTemplateBuilder,
final ObjectMapper objectMapper) {
this.properties = properties;
this.restTemplate = restTemplateBuilder.build();
this.objectMapper = objectMapper;
}
@Override
public OAuthMember getOAuthMember(final String code, final String redirectUri) {
GoogleTokenResponse googleTokenResponse = requestGoogleToken(code, redirectUri);
String payload = getPayload(googleTokenResponse.getIdToken());
UserInfo userInfo = parseUserInfo(payload);
String refreshToken = googleTokenResponse.getRefreshToken();
return new OAuthMember(userInfo.getEmail(), userInfo.getName(), userInfo.getPicture(), refreshToken);
}
private GoogleTokenResponse requestGoogleToken(final String code, final String redirectUri) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> params = generateTokenParams(code, redirectUri);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
return fetchGoogleToken(request).getBody();
}
private MultiValueMap<String, String> generateTokenParams(final String code, final String redirectUri) {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("client_id", properties.getClientId());
params.add("client_secret", properties.getClientSecret());
params.add("code", code);
params.add("grant_type", "authorization_code");
params.add("redirect_uri", redirectUri);
return params;
}
private ResponseEntity<GoogleTokenResponse> fetchGoogleToken(
final HttpEntity<MultiValueMap<String, String>> request) {
try {
return restTemplate.postForEntity(properties.getTokenUri(), request, GoogleTokenResponse.class);
} catch (final RestClientException e) {
throw new OAuthException(e);
}
}
private String getPayload(final String jwt) {
return jwt.split(JWT_DELIMITER)[1];
}
private UserInfo parseUserInfo(final String payload) {
String decodedPayload = decodeJwtPayload(payload);
try {
return objectMapper.readValue(decodedPayload, UserInfo.class);
} catch (final JsonProcessingException e) {
throw new OAuthException("id 토큰을 읽을 수 없습니다.", e);
}
}
private String decodeJwtPayload(final String payload) {
return new String(Base64.getUrlDecoder().decode(payload), StandardCharsets.UTF_8);
}
@Override
public OAuthAccessTokenResponse getAccessToken(final String refreshToken) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> params = generateAccessTokenParams(refreshToken);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
return fetchGoogleAccessToken(request).getBody();
}
private MultiValueMap<String, String> generateAccessTokenParams(final String refreshToken) {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("client_id", properties.getClientId());
params.add("client_secret", properties.getClientSecret());
params.add("refresh_token", refreshToken);
params.add("grant_type", "refresh_token");
return params;
}
private ResponseEntity<OAuthAccessTokenResponse> fetchGoogleAccessToken(
final HttpEntity<MultiValueMap<String, String>> request) {
try {
return restTemplate.postForEntity(properties.getTokenUri(), request, OAuthAccessTokenResponse.class);
} catch (final RestClientException e) {
throw new OAuthException(e);
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java
================================================
package com.allog.dallog.infrastructure.oauth.dto;
import com.allog.dallog.category.domain.CategoryType;
import com.allog.dallog.schedule.domain.IntegrationSchedule;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Objects;
public class GoogleCalendarEventResponse {
private String id;
private String summary = "";
private String description = "";
private GoogleDateFormat start;
private GoogleDateFormat end;
private GoogleCalendarEventResponse() {
}
public GoogleCalendarEventResponse(final String id, final String summary, final String description,
final GoogleDateFormat start,
final GoogleDateFormat end) {
this.id = id;
this.summary = summary;
this.description = description;
this.start = start;
this.end = end;
}
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));
}
return LocalDateTime.of(LocalDate.parse(start.getDate()), LocalTime.MIN);
}
private LocalDateTime getEndDateTime() {
if (Objects.isNull(end.getDate())) {
return LocalDateTime.parse(end.getDateTime().substring(0, 19));
}
return LocalDateTime.of(LocalDate.parse(end.getDate()), LocalTime.MIN);
}
public String getId() {
return id;
}
public String getSummary() {
return summary;
}
public String getDescription() {
return description;
}
public GoogleDateFormat getStart() {
return start;
}
public GoogleDateFormat getEnd() {
return end;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java
================================================
package com.allog.dallog.infrastructure.oauth.dto;
import java.util.List;
public class GoogleCalendarEventsResponse {
private List<GoogleCalendarEventResponse> items;
private GoogleCalendarEventsResponse() {
}
public GoogleCalendarEventsResponse(final List<GoogleCalendarEventResponse> items) {
this.items = items;
}
public List<GoogleCalendarEventResponse> getItems() {
return items;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java
================================================
package com.allog.dallog.infrastructure.oauth.dto;
import java.util.List;
public class GoogleCalendarListResponse {
private List<GoogleCalendarResponse> items;
private GoogleCalendarListResponse() {
}
public GoogleCalendarListResponse(final List<GoogleCalendarResponse> items) {
this.items = items;
}
public List<GoogleCalendarResponse> getItems() {
return items;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java
================================================
package com.allog.dallog.infrastructure.oauth.dto;
public class GoogleCalendarResponse {
private String id;
private String summary;
private String description;
private GoogleCalendarResponse() {
}
public GoogleCalendarResponse(final String id, final String summary, final String description) {
this.id = id;
this.summary = summary;
this.description = description;
}
public String getId() {
return id;
}
public String getSummary() {
return summary;
}
public String getDescription() {
return description;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java
================================================
package com.allog.dallog.infrastructure.oauth.dto;
public class GoogleDateFormat {
private String date;
private String dateTime;
private GoogleDateFormat() {
}
public GoogleDateFormat(final String date, final String dateTime) {
this.date = date;
this.dateTime = dateTime;
}
public String getDate() {
return date;
}
public String getDateTime() {
return dateTime;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java
================================================
package com.allog.dallog.infrastructure.oauth.dto;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class GoogleTokenResponse {
private String refreshToken;
private String idToken;
private GoogleTokenResponse() {
}
public GoogleTokenResponse(final String refreshToken, final String idToken) {
this.refreshToken = refreshToken;
this.idToken = idToken;
}
public String getRefreshToken() {
return refreshToken;
}
public String getIdToken() {
return idToken;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/UserInfo.java
================================================
package com.allog.dallog.infrastructure.oauth.dto;
public class UserInfo {
private String email;
private String name;
private String picture;
private UserInfo() {
}
public UserInfo(final String email, final String name, final String picture) {
this.email = email;
this.name = name;
this.picture = picture;
}
public String getEmail() {
return email;
}
public String getName() {
return name;
}
public String getPicture() {
return picture;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/exception/OAuthException.java
================================================
package com.allog.dallog.infrastructure.oauth.exception;
public class OAuthException extends RuntimeException {
public OAuthException() {
super("Oauth 서버와의 통신 과정에서 문제가 발생했습니다.");
}
public OAuthException(final Exception e) {
this("Oauth 서버와의 통신 과정에서 문제가 발생했습니다.", e);
}
public OAuthException(final String message, final Exception e) {
super(message, e);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/DevGoogleOAuthUri.java
================================================
package com.allog.dallog.infrastructure.oauth.uri;
import com.allog.dallog.auth.application.OAuthUri;
import com.allog.dallog.global.config.properties.GoogleProperties;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Component
@Profile({"local", "dev"})
public class DevGoogleOAuthUri implements OAuthUri {
private final GoogleProperties properties;
public DevGoogleOAuthUri(final GoogleProperties properties) {
this.properties = properties;
}
@Override
public String generate(final String redirectUri) {
return properties.getOAuthEndPoint() + "?"
+ "client_id=" + properties.getClientId() + "&"
+ "redirect_uri=" + redirectUri + "&"
+ "response_type=code&"
+ "scope=" + String.join(" ", properties.getScopes()) + "&"
+ "access_type=" + properties.getAccessType() + "&"
+ "prompt=consent";
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/GoogleOAuthUri.java
================================================
package com.allog.dallog.infrastructure.oauth.uri;
import com.allog.dallog.auth.application.OAuthUri;
import com.allog.dallog.global.config.properties.GoogleProperties;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Component
@Profile("prod")
public class GoogleOAuthUri implements OAuthUri {
private final GoogleProperties properties;
public GoogleOAuthUri(final GoogleProperties properties) {
this.properties = properties;
}
@Override
public String generate(final String redirectUri) {
return properties.getOAuthEndPoint() + "?"
+ "client_id=" + properties.getClientId() + "&"
+ "redirect_uri=" + redirectUri + "&"
+ "response_type=code&"
+ "scope=" + String.join(" ", properties.getScopes()) + "&"
+ "access_type=" + properties.getAccessType();
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/member/application/MemberService.java
================================================
package com.allog.dallog.member.application;
import com.allog.dallog.member.domain.Member;
import com.allog.dallog.member.domain.MemberRepository;
import com.allog.dallog.member.dto.request.MemberUpdateRequest;
import com.allog.dallog.member.dto.response.MemberResponse;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true)
@Service
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(final MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public MemberResponse findById(final Long id) {
return new MemberResponse(memberRepository.getById(id));
}
@Transactional
public void update(final Long id, final MemberUpdateRequest request) {
Member member = memberRepository.getById(id);
member.change(request.getDisplayName());
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/member/domain/Member.java
================================================
package com.allog.dallog.member.domain;
import com.allog.dallog.global.entity.BaseEntity;
import com.allog.dallog.member.exception.InvalidMemberException;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "members")
@Entity
public class Member extends BaseEntity {
private static final Pattern EMAIL_PATTERN = Pattern.compile("^[a-z0-9._-]+@[a-z]+[.]+[a-z]{2,3}$");
private static final int MAX_DISPLAY_NAME_LENGTH = 100;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "email", nullable = false)
private String email;
@Column(name = "display_name", nullable = false)
private String displayName;
@Column(name = "profile_image_url", nullable = false)
private String profileImageUrl;
@Enumerated(value = EnumType.STRING)
@Column(name = "social_type", nullable = false)
private SocialType socialType;
protected Member() {
}
public Member(final String email, final String displayName, final String profileImageUrl,
final SocialType socialType) {
validateEmail(email);
validateDisplayName(displayName);
this.email = email;
this.displayName = displayName;
this.profileImageUrl = profileImageUrl;
this.socialType = socialType;
}
private void validateEmail(final String email) {
Matcher matcher = EMAIL_PATTERN.matcher(email);
if (!matcher.matches()) {
throw new InvalidMemberException("이메일 형식이 올바르지 않습니다.");
}
}
private void validateDisplayName(final String displayName) {
if (displayName.isEmpty() || displayName.length() > MAX_DISPLAY_NAME_LENGTH) {
throw new InvalidMemberException(String.format("이름은 1자 이상 1자 %d이하여야 합니다.", MAX_DISPLAY_NAME_LENGTH));
}
}
public void change(final String displayName) {
validateDisplayName(displayName);
this.displayName = displayName;
}
public boolean hasSameId(final Long memberId) {
return Objects.equals(this.id, memberId);
}
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
public String getDisplayName() {
return displayName;
}
public String getProfileImageUrl() {
return profileImageUrl;
}
public SocialType getSocialType() {
return socialType;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/member/domain/MemberRepository.java
================================================
package com.allog.dallog.member.domain;
import com.allog.dallog.member.exception.NoSuchMemberException;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> 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();
}
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/member/domain/SocialType.java
================================================
package com.allog.dallog.member.domain;
public enum SocialType {
GOOGLE, GITHUB;
}
================================================
FILE: backend/src/main/java/com/allog/dallog/member/dto/request/MemberUpdateRequest.java
================================================
package com.allog.dallog.member.dto.request;
import javax.validation.constraints.NotBlank;
public class MemberUpdateRequest {
@NotBlank(message = "회원 이름이 공백일 수 없습니다.")
private String displayName;
private MemberUpdateRequest() {
}
public MemberUpdateRequest(final String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/member/dto/response/MemberResponse.java
================================================
package com.allog.dallog.member.dto.response;
import com.allog.dallog.member.domain.Member;
import com.allog.dallog.member.domain.SocialType;
public class MemberResponse {
private Long id;
private String email;
private String displayName;
private String profileImageUrl;
private SocialType socialType;
private MemberResponse() {
}
public MemberResponse(final Long id, final String email, final String displayName, final String profileImageUrl,
final SocialType socialType) {
this.id = id;
this.email = email;
this.displayName = displayName;
this.profileImageUrl = profileImageUrl;
this.socialType = socialType;
}
public MemberResponse(final Member member) {
this(member.getId(), member.getEmail(), member.getDisplayName(), member.getProfileImageUrl(),
member.getSocialType());
}
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
public String getDisplayName() {
return displayName;
}
public String getProfileImageUrl() {
return profileImageUrl;
}
public SocialType getSocialType() {
return socialType;
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/member/exception/InvalidMemberException.java
================================================
package com.allog.dallog.member.exception;
public class InvalidMemberException extends RuntimeException {
public InvalidMemberException(final String message) {
super(message);
}
public InvalidMemberException() {
this("잘못된 회원의 정보입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/member/exception/NoSuchMemberException.java
================================================
package com.allog.dallog.member.exception;
public class NoSuchMemberException extends RuntimeException {
public NoSuchMemberException(final String message) {
super(message);
}
public NoSuchMemberException() {
this("존재하지 않는 회원입니다.");
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/member/presentation/MemberController.java
================================================
package com.allog.dallog.member.presentation;
import com.allog.dallog.auth.dto.LoginMember;
import com.allog.dallog.auth.presentation.AuthenticationPrincipal;
import com.allog.dallog.member.application.MemberService;
import com.allog.dallog.member.dto.request.MemberUpdateRequest;
import com.allog.dallog.member.dto.response.MemberResponse;
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api/members")
@RestController
public class MemberController {
private final MemberService memberService;
public MemberController(final MemberService memberService) {
this.memberService = memberService;
}
@GetMapping("/me")
public ResponseEntity<MemberResponse> findMe(@AuthenticationPrincipal final LoginMember loginMember) {
MemberResponse response = memberService.findById(loginMember.getId());
return ResponseEntity.ok(response);
}
@PatchMapping("/me")
public ResponseEntity<Void> update(@AuthenticationPrincipal LoginMember loginMember,
@Valid @RequestBody final MemberUpdateRequest request) {
memberService.update(loginMember.getId(), request);
return ResponseEntity.noContent().build();
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/schedule/application/CheckedSchedulesFinder.java
================================================
package com.allog.dallog.schedule.application;
import com.allog.dallog.auth.application.OAuthClient;
import com.allog.dallog.auth.dto.response.OAuthAccessTokenResponse;
import com.allog.dallog.category.domain.ExternalCategoryDetail;
import com.allog.dallog.category.domain.Category;
import com.allog.dallog.externalcalendar.application.ExternalCalendarClient;
import com.allog.dallog.schedule.domain.IntegrationSchedule;
import com.allog.dallog.schedule.domain.TypedSchedules;
import com.allog.dallog.schedule.dto.MaterialToFindSchedules;
import com.allog.dallog.schedule.dto.request.DateRangeRequest;
import com.allog.dallog.schedule.dto.response.IntegrationScheduleResponses;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;
@Component
public class CheckedSchedulesFinder {
private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
private final ScheduleService scheduleService;
private final OAuthClient oAuthClient;
private final ExternalCalendarClient externalCalendarClient;
public CheckedSchedulesFinder(final ScheduleService scheduleService, final OAuthClient oAuthClient,
final ExternalCalendarClient externalCalendarClient) {
this.scheduleService = scheduleService;
this.oAuthClient = oAuthClient;
this.externalCalendarClient = externalCalendarClient;
}
public IntegrationScheduleResponses findMyCheckedSchedules(final Long memberId, final DateRangeRequest request) {
MaterialToFindSchedules material = scheduleService.findInternalByMemberIdAndDateRange(memberId, request);
List<IntegrationSchedule> schedules = material.getSchedules();
String refreshToken = material.getRefreshToken();
String accessToken = toAccessToken(refreshToken);
List<IntegrationSchedule> externalSchedules = toExternalSchedules(request, material, accessToken);
schedules.addAll(externalSchedules);
return new IntegrationScheduleResponses(material.getSubscriptions(), new TypedSchedules(schedules));
}
private String toAccessToken(final String refreshToken) {
OAuthAccessTokenResponse oAuthToken = oAuthClient.getAccessToken(refreshToken);
return oAuthToken.getAccessToken();
}
private List<IntegrationSchedule> toExternalSchedules(final DateRangeRequest request,
final MaterialToFindSchedules material,
final String accessToken) {
List<ExternalCategoryDetail> externalCategoryDetails = material.getExternalCategoryDetails();
if (externalCategoryDetails.isEmpty()) {
return new ArrayList<>();
}
return externalCategoryDetails.stream()
.map(externalCategoryDetail -> findExternalSchedules(request, accessToken, externalCategoryDetail))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
private List<IntegrationSchedule> findExternalSchedules(final DateRangeRequest request, final String accessToken,
final ExternalCategoryDetail externalCategoryDetail) {
String startDateTime = request.getStartDateTime().format(DateTimeFormatter.ofPattern(DATE_FORMAT));
String endDateTime = request.getEndDateTime().format(DateTimeFormatter.ofPattern(DATE_FORMAT));
Category externalCategory = externalCategoryDetail.getCategory();
String externalId = externalCategoryDetail.getExternalId();
return externalCalendarClient.getExternalCalendarSchedules(
accessToken, externalCategory.getId(), externalId, startDateTime, endDateTime);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/schedule/application/ScheduleService.java
================================================
package com.allog.dallog.schedule.application;
import com.allog.dallog.auth.domain.OAuthToken;
import com.allog.dallog.auth.domain.OAuthTokenRepository;
import com.allog.dallog.categoryrole.domain.CategoryAuthority;
import com.allog.dallog.category.domain.Category;
import com.allog.dallog.category.domain.CategoryRepository;
import com.allog.dallog.category.domain.ExternalCategoryDetail;
import com.allog.dallog.category.domain.ExternalCategoryDetailRepository;
import com.allog.dallog.categoryrole.domain.CategoryRole;
import com.allog.dallog.categoryrole.domain.CategoryRoleRepository;
import com.allog.dallog.schedule.domain.IntegrationSchedule;
import com.allog.dallog.schedule.domain.Schedule;
import com.allog.dallog.schedule.domain.ScheduleRepository;
import com.allog.dallog.schedule.domain.TypedSchedules;
import com.allog.dallog.schedule.dto.MaterialToFindSchedules;
import com.allog.dallog.schedule.dto.request.DateRangeRequest;
import com.allog.dallog.schedule.dto.request.ScheduleCreateRequest;
import com.allog.dallog.schedule.dto.request.ScheduleUpdateRequest;
import com.allog.dallog.schedule.dto.response.IntegrationScheduleResponses;
import com.allog.dallog.schedule.dto.response.ScheduleResponse;
import com.allog.dallog.subscription.application.ColorPicker;
import com.allog.dallog.subscription.domain.Color;
import com.allog.dallog.subscription.domain.SubscriptionRepository;
import com.allog.dallog.subscription.domain.Subscriptions;
import java.time.LocalDateTime;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true)
@Service
public class ScheduleService {
private final ScheduleRepository scheduleRepository;
private final CategoryRepository categoryRepository;
private final CategoryRoleRepository categoryRoleRepository;
private final SubscriptionRepository subscriptionRepository;
private final OAuthTokenRepository oAuthTokenRepository;
private final ExternalCategoryDetailRepository externalCategoryDetailRepository;
private final ColorPicker colorPicker;
public ScheduleService(final ScheduleRepository scheduleRepository, final CategoryRepository categoryRepository,
final CategoryRoleRepository categoryRoleRepository,
final SubscriptionRepository subscriptionRepository,
final OAuthTokenRepository oAuthTokenRepository,
final ExternalCategoryDetailRepository externalCategoryDetailRepository,
final ColorPicker colorPicker) {
this.scheduleRepository = scheduleRepository;
this.categoryRepository = categoryRepository;
this.categoryRoleRepository = categoryRoleRepository;
this.subscriptionRepository = subscriptionRepository;
this.oAuthTokenRepository = oAuthTokenRepository;
this.externalCategoryDetailRepository = externalCategoryDetailRepository;
this.colorPicker = colorPicker;
}
@Transactional
public ScheduleResponse save(final Long memberId, final Long categoryId, final ScheduleCreateRequest request) {
Category category = categoryRepository.getById(categoryId);
category.validateNotExternalCategory();
CategoryRole categoryRole = categoryRoleRepository.getByMemberIdAndCategoryId(memberId, categoryId);
categoryRole.validateAuthority(CategoryAuthority.ADD_SCHEDULE);
Schedule schedule = scheduleRepository.save(request.toEntity(category));
return new ScheduleResponse(schedule);
}
public ScheduleResponse findById(final Long id) {
Schedule schedule = scheduleRepository.getById(id);
return new ScheduleResponse(schedule);
}
public MaterialToFindSchedules findInternalByMemberIdAndDateRange(final Long memberId,
final DateRangeRequest request) {
Subscriptions subscriptions = new Subscriptions(subscriptionRepository.findByMemberId(memberId));
List<Category> categories = subscriptions.findInternalCategory();
LocalDateTime startDateTime = request.getStartDateTime();
LocalDateTime endDateTime = request.getEndDateTime();
List<IntegrationSchedule> schedules = toIntegrationSchedules(categories, startDateTime, endDateTime);
String refreshToken = toRefreshToken(memberId);
List<ExternalCategoryDetail> externalCategoryDetails = toCategoryDetails(subscriptions);
return new MaterialToFindSchedules(subscriptions, schedules, refreshToken, externalCategoryDetails);
}
private String toRefreshToken(final Long memberId) {
OAuthToken oAuthToken = oAuthTokenRepository.getByMemberId(memberId);
return oAuthToken.getRefreshToken();
}
private List<ExternalCategoryDetail> toCategoryDetails(final Subscriptions subscriptions) {
return externalCategoryDetailRepository.findByCategoryIn(subscriptions.findExternalCategory());
}
public IntegrationScheduleResponses findByCategoryIdAndDateRange(final Long categoryId,
final DateRangeRequest request) {
Category category = categoryRepository.getById(categoryId);
LocalDateTime startDateTime = request.getStartDateTime();
LocalDateTime endDateTime = request.getEndDateTime();
List<IntegrationSchedule> schedules = toIntegrationSchedules(List.of(category), startDateTime, endDateTime);
Color color = Color.pick(colorPicker.pickNumber());
return new IntegrationScheduleResponses(color, new TypedSchedules(schedules));
}
private List<IntegrationSchedule> toIntegrationSchedules(final List<Category> categories,
final LocalDateTime startDateTime,
final LocalDateTime endDateTime) {
return scheduleRepository.getByCategoriesAndBetween(categories, startDateTime, endDateTime);
}
@Transactional
public void update(final Long id, final Long memberId, final ScheduleUpdateRequest request) {
Long categoryId = request.getCategoryId();
Category categoryForUpdate = categoryRepository.getById(categoryId);
Schedule schedule = scheduleRepository.getById(id);
CategoryRole categoryRole = categoryRoleRepository.getByMemberIdAndCategoryId(memberId, categoryId);
categoryRole.validateAuthority(CategoryAuthority.UPDATE_SCHEDULE);
schedule.change(categoryForUpdate, request.getTitle(), request.getStartDateTime(), request.getEndDateTime(),
request.getMemo());
}
@Transactional
public void delete(final Long id, final Long memberId) {
Schedule schedule = scheduleRepository.getById(id);
Long categoryId = schedule.getCategory().getId();
CategoryRole categoryRole = categoryRoleRepository.getByMemberIdAndCategoryId(memberId, categoryId);
categoryRole.validateAuthority(CategoryAuthority.DELETE_SCHEDULE);
scheduleRepository.deleteById(id);
}
}
================================================
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/IntegrationSchedule.java
================================================
package com.allog.dallog.schedule.domain;
import com.allog.dallog.category.domain.CategoryType;
import com.allog.dallog.category.domain.Category;
import java.time.LocalDateTime;
import java.util.Objects;
public class IntegrationSchedule {
private static final int ONE_DAY = 1;
private final String id;
private final Long categoryId;
private final String title;
private final Period period;
private final String memo;
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 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 CategoryType categoryType) {
this.id = id;
this.categoryId = categoryId;
this.title = title;
this.period = period;
this.memo = memo;
this.categoryType = categoryType;
}
public boolean isLongTerms() {
return !isAllDays()
&& period.calculateDayDifference() >= ONE_DAY;
}
public boolean isAllDays() {
return period.calculateDayDifference() == ONE_DAY
&& period.isMidnightToMidnight();
}
public boolean isFewHours() {
return period.calculateDayDifference() < ONE_DAY;
}
public boolean isSameCategory(final Category category) {
Long categoryId = category.getId();
return this.categoryId.equals(categoryId);
}
public String getId() {
return id;
}
public Long getCategoryId() {
return categoryId;
}
public String getTitle() {
return title;
}
public LocalDateTime getStartDateTime() {
return period.getStartDateTime();
}
public LocalDateTime getEndDateTime() {
return period.getEndDateTime();
}
public Period getPeriod() {
return period;
}
public String getMemo() {
return memo;
}
public CategoryType getCategoryType() {
return categoryType;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == nu
gitextract_eag9zh7a/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-template.md
│ │ └── feature-template.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── backend-cd.yml
│ ├── backend-ci.yml
│ └── frontend-ci.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── backend/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── src/
│ ├── docs/
│ │ └── asciidoc/
│ │ ├── auth.adoc
│ │ ├── category.adoc
│ │ ├── external-calendar.adoc
│ │ ├── index.adoc
│ │ ├── member.adoc
│ │ ├── schedule.adoc
│ │ └── subscription.adoc
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── allog/
│ │ │ └── dallog/
│ │ │ ├── DallogApplication.java
│ │ │ ├── auth/
│ │ │ │ ├── application/
│ │ │ │ │ ├── AuthService.java
│ │ │ │ │ ├── AuthTokenCreator.java
│ │ │ │ │ ├── JwtTokenProvider.java
│ │ │ │ │ ├── OAuthClient.java
│ │ │ │ │ ├── OAuthUri.java
│ │ │ │ │ ├── TokenCreator.java
│ │ │ │ │ └── TokenProvider.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── AuthToken.java
│ │ │ │ │ ├── InMemoryAuthTokenRepository.java
│ │ │ │ │ ├── OAuthToken.java
│ │ │ │ │ ├── OAuthTokenRepository.java
│ │ │ │ │ └── TokenRepository.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── LoginMember.java
│ │ │ │ │ ├── OAuthMember.java
│ │ │ │ │ ├── request/
│ │ │ │ │ │ ├── TokenRenewalRequest.java
│ │ │ │ │ │ └── TokenRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ ├── AccessAndRefreshTokenResponse.java
│ │ │ │ │ ├── AccessTokenResponse.java
│ │ │ │ │ ├── OAuthAccessTokenResponse.java
│ │ │ │ │ └── OAuthUriResponse.java
│ │ │ │ ├── event/
│ │ │ │ │ └── MemberSavedEvent.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── EmptyAuthorizationHeaderException.java
│ │ │ │ │ ├── InvalidTokenException.java
│ │ │ │ │ ├── NoPermissionException.java
│ │ │ │ │ ├── NoSuchOAuthTokenException.java
│ │ │ │ │ └── NoSuchTokenException.java
│ │ │ │ └── presentation/
│ │ │ │ ├── AuthController.java
│ │ │ │ ├── AuthenticationPrincipal.java
│ │ │ │ ├── AuthenticationPrincipalArgumentResolver.java
│ │ │ │ └── AuthorizationExtractor.java
│ │ │ ├── category/
│ │ │ │ ├── application/
│ │ │ │ │ ├── CategoryService.java
│ │ │ │ │ └── ExternalCategoryDetailService.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Category.java
│ │ │ │ │ ├── CategoryRepository.java
│ │ │ │ │ ├── CategoryType.java
│ │ │ │ │ ├── ExternalCategoryDetail.java
│ │ │ │ │ └── ExternalCategoryDetailRepository.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/
│ │ │ │ │ │ ├── CategoryCreateRequest.java
│ │ │ │ │ │ ├── CategoryUpdateRequest.java
│ │ │ │ │ │ └── ExternalCategoryCreateRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ ├── CategoriesResponse.java
│ │ │ │ │ ├── CategoryDetailResponse.java
│ │ │ │ │ └── CategoryResponse.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── ExistExternalCategoryException.java
│ │ │ │ │ ├── InvalidCategoryException.java
│ │ │ │ │ ├── NoSuchCategoryException.java
│ │ │ │ │ └── NoSuchExternalCategoryDetailException.java
│ │ │ │ └── presentaion/
│ │ │ │ └── CategoryController.java
│ │ │ ├── categoryrole/
│ │ │ │ ├── application/
│ │ │ │ │ └── CategoryRoleService.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── CategoryAuthority.java
│ │ │ │ │ ├── CategoryRole.java
│ │ │ │ │ ├── CategoryRoleRepository.java
│ │ │ │ │ └── CategoryRoleType.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/
│ │ │ │ │ │ └── CategoryRoleUpdateRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ ├── MemberWithRoleTypeResponse.java
│ │ │ │ │ └── SubscribersResponse.java
│ │ │ │ └── exception/
│ │ │ │ ├── ManagingCategoryLimitExcessException.java
│ │ │ │ ├── NoCategoryAuthorityException.java
│ │ │ │ ├── NoSuchCategoryRoleException.java
│ │ │ │ └── NotAbleToChangeRoleException.java
│ │ │ ├── externalcalendar/
│ │ │ │ ├── application/
│ │ │ │ │ ├── ExternalCalendarClient.java
│ │ │ │ │ └── ExternalCalendarService.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── ExternalCalendar.java
│ │ │ │ │ └── ExternalCalendarsResponse.java
│ │ │ │ └── presentation/
│ │ │ │ └── ExternalCalendarController.java
│ │ │ ├── global/
│ │ │ │ ├── config/
│ │ │ │ │ ├── JpaConfig.java
│ │ │ │ │ ├── PropertiesConfig.java
│ │ │ │ │ ├── WebConfig.java
│ │ │ │ │ ├── cache/
│ │ │ │ │ │ ├── CacheConfig.java
│ │ │ │ │ │ └── ExpiringConcurrentMapCache.java
│ │ │ │ │ ├── properties/
│ │ │ │ │ │ └── GoogleProperties.java
│ │ │ │ │ └── replication/
│ │ │ │ │ ├── DataSourceConfiguration.java
│ │ │ │ │ ├── DataSourceKey.java
│ │ │ │ │ ├── RandomReplicaKeys.java
│ │ │ │ │ └── RoutingDataSource.java
│ │ │ │ ├── entity/
│ │ │ │ │ └── BaseEntity.java
│ │ │ │ └── error/
│ │ │ │ ├── ControllerAdvice.java
│ │ │ │ └── dto/
│ │ │ │ ├── ErrorReportRequest.java
│ │ │ │ └── ErrorResponse.java
│ │ │ ├── infrastructure/
│ │ │ │ ├── log/
│ │ │ │ │ ├── DiscordAppender.java
│ │ │ │ │ └── dto/
│ │ │ │ │ ├── DiscordWebhookRequest.java
│ │ │ │ │ ├── Embed.java
│ │ │ │ │ └── Field.java
│ │ │ │ └── oauth/
│ │ │ │ ├── client/
│ │ │ │ │ ├── GoogleExternalCalendarClient.java
│ │ │ │ │ └── GoogleOAuthClient.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── GoogleCalendarEventResponse.java
│ │ │ │ │ ├── GoogleCalendarEventsResponse.java
│ │ │ │ │ ├── GoogleCalendarListResponse.java
│ │ │ │ │ ├── GoogleCalendarResponse.java
│ │ │ │ │ ├── GoogleDateFormat.java
│ │ │ │ │ ├── GoogleTokenResponse.java
│ │ │ │ │ └── UserInfo.java
│ │ │ │ ├── exception/
│ │ │ │ │ └── OAuthException.java
│ │ │ │ └── uri/
│ │ │ │ ├── DevGoogleOAuthUri.java
│ │ │ │ └── GoogleOAuthUri.java
│ │ │ ├── member/
│ │ │ │ ├── application/
│ │ │ │ │ └── MemberService.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Member.java
│ │ │ │ │ ├── MemberRepository.java
│ │ │ │ │ └── SocialType.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/
│ │ │ │ │ │ └── MemberUpdateRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ └── MemberResponse.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── InvalidMemberException.java
│ │ │ │ │ └── NoSuchMemberException.java
│ │ │ │ └── presentation/
│ │ │ │ └── MemberController.java
│ │ │ ├── schedule/
│ │ │ │ ├── application/
│ │ │ │ │ ├── CheckedSchedulesFinder.java
│ │ │ │ │ └── ScheduleService.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── IntegrationSchedule.java
│ │ │ │ │ ├── IntegrationScheduleComparator.java
│ │ │ │ │ ├── IntegrationSchedules.java
│ │ │ │ │ ├── Period.java
│ │ │ │ │ ├── Schedule.java
│ │ │ │ │ ├── ScheduleRepository.java
│ │ │ │ │ ├── ScheduleType.java
│ │ │ │ │ ├── TypedSchedules.java
│ │ │ │ │ └── scheduler/
│ │ │ │ │ └── Scheduler.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── MaterialToFindSchedules.java
│ │ │ │ │ ├── request/
│ │ │ │ │ │ ├── DateRangeRequest.java
│ │ │ │ │ │ ├── ScheduleCreateRequest.java
│ │ │ │ │ │ └── ScheduleUpdateRequest.java
│ │ │ │ │ └── response/
│ │ │ │ │ ├── IntegrationScheduleResponse.java
│ │ │ │ │ ├── IntegrationScheduleResponses.java
│ │ │ │ │ └── ScheduleResponse.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── InvalidScheduleException.java
│ │ │ │ │ └── NoSuchScheduleException.java
│ │ │ │ └── presentation/
│ │ │ │ └── ScheduleController.java
│ │ │ └── subscription/
│ │ │ ├── application/
│ │ │ │ ├── ColorPicker.java
│ │ │ │ ├── RandomColorPicker.java
│ │ │ │ └── SubscriptionService.java
│ │ │ ├── domain/
│ │ │ │ ├── Color.java
│ │ │ │ ├── Subscription.java
│ │ │ │ ├── SubscriptionRepository.java
│ │ │ │ └── Subscriptions.java
│ │ │ ├── dto/
│ │ │ │ ├── request/
│ │ │ │ │ └── SubscriptionUpdateRequest.java
│ │ │ │ └── response/
│ │ │ │ ├── SubscriptionResponse.java
│ │ │ │ └── SubscriptionsResponse.java
│ │ │ ├── exception/
│ │ │ │ ├── ExistSubscriptionException.java
│ │ │ │ ├── InvalidSubscriptionException.java
│ │ │ │ ├── NoSuchSubscriptionException.java
│ │ │ │ └── NotAbleToUnsubscribeException.java
│ │ │ └── presentation/
│ │ │ └── SubscriptionController.java
│ │ └── resources/
│ │ ├── application-test.yml
│ │ ├── db/
│ │ │ └── prod/
│ │ │ └── schema.sql
│ │ └── logback-spring.xml
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── allog/
│ │ └── dallog/
│ │ ├── acceptance/
│ │ │ ├── AcceptanceTest.java
│ │ │ ├── AuthAcceptanceTest.java
│ │ │ ├── CategoryAcceptanceTest.java
│ │ │ ├── ExternalCalendarAcceptanceTest.java
│ │ │ ├── MemberAcceptanceTest.java
│ │ │ ├── ScheduleAcceptanceTest.java
│ │ │ ├── SubscriptionAcceptanceTest.java
│ │ │ └── fixtures/
│ │ │ ├── AuthAcceptanceFixtures.java
│ │ │ ├── CategoryAcceptanceFixtures.java
│ │ │ ├── CommonAcceptanceFixtures.java
│ │ │ ├── MemberAcceptanceFixtures.java
│ │ │ ├── ScheduleAcceptanceFixtures.java
│ │ │ └── SubscriptionAcceptanceFixtures.java
│ │ ├── auth/
│ │ │ ├── application/
│ │ │ │ ├── AuthServiceTest.java
│ │ │ │ ├── AuthTokenCreatorTest.java
│ │ │ │ ├── JwtTokenProviderTest.java
│ │ │ │ └── StubTokenProvider.java
│ │ │ ├── domain/
│ │ │ │ ├── AuthTokenTest.java
│ │ │ │ ├── InMemoryAuthTokenRepositoryTest.java
│ │ │ │ ├── OAuthTokenRepositoryTest.java
│ │ │ │ └── OAuthTokenTest.java
│ │ │ └── presentation/
│ │ │ └── AuthControllerTest.java
│ │ ├── category/
│ │ │ ├── application/
│ │ │ │ ├── CategoryServiceTest.java
│ │ │ │ └── ExternalCategoryDetailServiceTest.java
│ │ │ ├── domain/
│ │ │ │ ├── CategoryRepositoryTest.java
│ │ │ │ ├── CategoryTest.java
│ │ │ │ ├── CategoryTypeTest.java
│ │ │ │ └── ExternalCategoryDetailRepositoryTest.java
│ │ │ └── presentation/
│ │ │ └── CategoryControllerTest.java
│ │ ├── categoryrole/
│ │ │ ├── application/
│ │ │ │ └── CategoryRoleServiceTest.java
│ │ │ └── domain/
│ │ │ ├── CategoryRoleRepositoryTest.java
│ │ │ ├── CategoryRoleTest.java
│ │ │ └── CategoryRoleTypeTest.java
│ │ ├── common/
│ │ │ ├── Constants.java
│ │ │ ├── DatabaseCleaner.java
│ │ │ ├── annotation/
│ │ │ │ ├── ControllerTest.java
│ │ │ │ ├── RepositoryTest.java
│ │ │ │ └── ServiceTest.java
│ │ │ ├── builder/
│ │ │ │ ├── BuilderSupporter.java
│ │ │ │ └── GivenBuilder.java
│ │ │ ├── config/
│ │ │ │ ├── ExternalApiConfig.java
│ │ │ │ └── TokenConfig.java
│ │ │ └── fixtures/
│ │ │ ├── AuthFixtures.java
│ │ │ ├── CategoryFixtures.java
│ │ │ ├── ExternalCalendarFixtures.java
│ │ │ ├── ExternalCategoryFixtures.java
│ │ │ ├── IntegrationScheduleFixtures.java
│ │ │ ├── MemberFixtures.java
│ │ │ ├── OAuthFixtures.java
│ │ │ ├── OAuthTokenFixtures.java
│ │ │ ├── ScheduleFixtures.java
│ │ │ └── SubscriptionFixtures.java
│ │ ├── externalcalendar/
│ │ │ ├── application/
│ │ │ │ └── ExternalCalendarServiceTest.java
│ │ │ └── presentation/
│ │ │ └── ExternalCalendarControllerTest.java
│ │ ├── infrastructure/
│ │ │ └── oauth/
│ │ │ ├── client/
│ │ │ │ ├── StubExternalCalendarClient.java
│ │ │ │ └── StubOAuthClient.java
│ │ │ └── uri/
│ │ │ └── StubOAuthUri.java
│ │ ├── member/
│ │ │ ├── application/
│ │ │ │ └── MemberServiceTest.java
│ │ │ ├── domain/
│ │ │ │ ├── MemberRepositoryTest.java
│ │ │ │ └── MemberTest.java
│ │ │ └── presentation/
│ │ │ └── MemberControllerTest.java
│ │ ├── schedule/
│ │ │ ├── application/
│ │ │ │ ├── CheckedSchedulesFinderTest.java
│ │ │ │ └── ScheduleServiceTest.java
│ │ │ ├── domain/
│ │ │ │ ├── IntegrationScheduleTest.java
│ │ │ │ ├── IntegrationSchedulesTest.java
│ │ │ │ ├── PeriodTest.java
│ │ │ │ ├── ScheduleRepositoryTest.java
│ │ │ │ ├── ScheduleTest.java
│ │ │ │ └── scheduler/
│ │ │ │ └── SchedulerTest.java
│ │ │ └── presentation/
│ │ │ └── ScheduleControllerTest.java
│ │ └── subscription/
│ │ ├── application/
│ │ │ └── SubscriptionServiceTest.java
│ │ ├── domain/
│ │ │ ├── ColorTest.java
│ │ │ ├── SubscriptionRepositoryTest.java
│ │ │ ├── SubscriptionTest.java
│ │ │ └── SubscriptionsTest.java
│ │ └── presentation/
│ │ └── SubscriptionControllerTest.java
│ └── resources/
│ └── application.yml
├── frontend/
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc.json
│ ├── .storybook/
│ │ ├── main.js
│ │ ├── preview-body.html
│ │ └── preview.js
│ ├── babel.config.js
│ ├── jest.config.js
│ ├── package.json
│ ├── setupFile.js
│ ├── src/
│ │ ├── @types/
│ │ │ ├── calendar.ts
│ │ │ ├── category.ts
│ │ │ ├── custom.d.ts
│ │ │ ├── emotion.d.ts
│ │ │ ├── googleCalendar.ts
│ │ │ ├── index.ts
│ │ │ ├── profile.ts
│ │ │ ├── schedule.ts
│ │ │ ├── subscription.ts
│ │ │ └── util.ts
│ │ ├── App.tsx
│ │ ├── api/
│ │ │ ├── category.ts
│ │ │ ├── googleCalendar.ts
│ │ │ ├── index.ts
│ │ │ ├── login.ts
│ │ │ ├── profile.ts
│ │ │ ├── schedule.ts
│ │ │ └── subscription.ts
│ │ ├── components/
│ │ │ ├── @common/
│ │ │ │ ├── Button/
│ │ │ │ │ ├── Button.stories.tsx
│ │ │ │ │ ├── Button.styles.ts
│ │ │ │ │ ├── Button.test.tsx
│ │ │ │ │ └── Button.tsx
│ │ │ │ ├── ErrorBoundary/
│ │ │ │ │ └── ErrorBoundary.tsx
│ │ │ │ ├── Fieldset/
│ │ │ │ │ ├── Fieldset.stories.tsx
│ │ │ │ │ ├── Fieldset.styles.ts
│ │ │ │ │ ├── Fieldset.test.tsx
│ │ │ │ │ └── Fieldset.tsx
│ │ │ │ ├── ModalPortal/
│ │ │ │ │ ├── ModalPortal.styles.ts
│ │ │ │ │ └── ModalPortal.tsx
│ │ │ │ ├── PageLayout/
│ │ │ │ │ ├── PageLayout.styles.ts
│ │ │ │ │ └── PageLayout.tsx
│ │ │ │ ├── Responsive/
│ │ │ │ │ ├── Responsive.styles.ts
│ │ │ │ │ └── Responsive.tsx
│ │ │ │ ├── Select/
│ │ │ │ │ ├── Select.styles.ts
│ │ │ │ │ └── Select.tsx
│ │ │ │ ├── Skeleton/
│ │ │ │ │ ├── Skeleton.stories.tsx
│ │ │ │ │ ├── Skeleton.styles.ts
│ │ │ │ │ └── Skeleton.tsx
│ │ │ │ └── Spinner/
│ │ │ │ ├── Spinner.stories.tsx
│ │ │ │ ├── Spinner.styles.ts
│ │ │ │ └── Spinner.tsx
│ │ │ ├── AdminCategoryManageModal/
│ │ │ │ ├── AdminCategoryManageModal.styles.ts
│ │ │ │ └── AdminCategoryManageModal.tsx
│ │ │ ├── AdminItem/
│ │ │ │ ├── AdminItem.styles.ts
│ │ │ │ └── AdminItem.tsx
│ │ │ ├── Calendar/
│ │ │ │ ├── Calendar.fallback.tsx
│ │ │ │ ├── Calendar.styles.ts
│ │ │ │ └── Calendar.tsx
│ │ │ ├── CategoryAddModal/
│ │ │ │ ├── CategoryAddModal.stories.tsx
│ │ │ │ ├── CategoryAddModal.styles.ts
│ │ │ │ └── CategoryAddModal.tsx
│ │ │ ├── CategoryControl/
│ │ │ │ ├── CategoryControl.tsx
│ │ │ │ └── CategoryCotrol.styles.ts
│ │ │ ├── CategoryList/
│ │ │ │ ├── CategoryList.fallback.stories.tsx
│ │ │ │ ├── CategoryList.fallback.tsx
│ │ │ │ ├── CategoryList.stories.tsx
│ │ │ │ ├── CategoryList.styles.ts
│ │ │ │ └── CategoryList.tsx
│ │ │ ├── CategoryModifyModal/
│ │ │ │ ├── CategoryModifyModal.styles.ts
│ │ │ │ └── CategoryModifyModal.tsx
│ │ │ ├── DateCell/
│ │ │ │ ├── DateCell.styles.ts
│ │ │ │ └── DateCell.tsx
│ │ │ ├── Footer/
│ │ │ │ ├── Footer.styles.ts
│ │ │ │ └── Footer.tsx
│ │ │ ├── GoogleCategoryManageModal/
│ │ │ │ ├── GoogleCategoryManageModal.styles.ts
│ │ │ │ └── GoogleCategoryManageModal.tsx
│ │ │ ├── GoogleImportModal/
│ │ │ │ ├── GoogleImportModal.styles.ts
│ │ │ │ └── GoogleImportModal.tsx
│ │ │ ├── MoreScheduleModal/
│ │ │ │ ├── MoreScheduleModal.styles.ts
│ │ │ │ └── MoreScheduleModal.tsx
│ │ │ ├── NavBar/
│ │ │ │ ├── NavBar.stories.tsx
│ │ │ │ ├── NavBar.styles.ts
│ │ │ │ └── NavBar.tsx
│ │ │ ├── Profile/
│ │ │ │ ├── Profile.fallback.stories.tsx
│ │ │ │ ├── Profile.fallback.tsx
│ │ │ │ ├── Profile.styles.ts
│ │ │ │ └── Profile.tsx
│ │ │ ├── ProtectRoute/
│ │ │ │ └── ProtectRoute.tsx
│ │ │ ├── Schedule/
│ │ │ │ ├── Schedule.styles.ts
│ │ │ │ └── Schedule.tsx
│ │ │ ├── ScheduleAddButton/
│ │ │ │ ├── ScheduleAddButton.stories.tsx
│ │ │ │ ├── ScheduleAddButton.styles.ts
│ │ │ │ └── ScheduleAddButton.tsx
│ │ │ ├── ScheduleAddModal/
│ │ │ │ ├── ScheduleAddModal.stories.tsx
│ │ │ │ ├── ScheduleAddModal.styles.ts
│ │ │ │ └── ScheduleAddModal.tsx
│ │ │ ├── ScheduleModal/
│ │ │ │ ├── ScheduleModal.styles.ts
│ │ │ │ └── ScheduleModal.tsx
│ │ │ ├── ScheduleModifyModal/
│ │ │ │ ├── ScheduleModifyModal.styles.ts
│ │ │ │ └── ScheduleModifyModal.tsx
│ │ │ ├── SideAdminList/
│ │ │ │ ├── SideAdminList.styles.ts
│ │ │ │ └── SideAdminList.tsx
│ │ │ ├── SideBar/
│ │ │ │ ├── SideBar.fallback.stories.tsx
│ │ │ │ ├── SideBar.fallback.tsx
│ │ │ │ ├── SideBar.styles.ts
│ │ │ │ └── SideBar.tsx
│ │ │ ├── SideBarButton/
│ │ │ │ ├── SideBarButton.styles.ts
│ │ │ │ └── SideBarButton.tsx
│ │ │ ├── SideGoogleList/
│ │ │ │ ├── SideGoogleList.styles.ts
│ │ │ │ └── SideGoogleList.tsx
│ │ │ ├── SideItem/
│ │ │ │ ├── SideItem.styles.ts
│ │ │ │ └── SideItem.tsx
│ │ │ ├── SideSubscribedList/
│ │ │ │ ├── SideSubscribedList.styles.ts
│ │ │ │ └── SideSubscribedList.tsx
│ │ │ ├── SnackBar/
│ │ │ │ ├── SnackBar.styles.ts
│ │ │ │ └── SnackBar.tsx
│ │ │ ├── SubscribedCategoryItem/
│ │ │ │ ├── SubscribedCategoryItem.styles.ts
│ │ │ │ └── SubscribedCategoryItem.tsx
│ │ │ ├── SubscriberItem/
│ │ │ │ ├── SubscriberItem.styles.ts
│ │ │ │ └── SubscriberItem.tsx
│ │ │ ├── SubscriptionModifyModal/
│ │ │ │ ├── SubscriptionModifyModal.styles.ts
│ │ │ │ └── SubscriptionModifyModal.tsx
│ │ │ ├── UnsubscribedCategoryItem/
│ │ │ │ ├── UnsubscribedCategoryItem.styles.ts
│ │ │ │ └── UnsubscribedCategoryItem.tsx
│ │ │ └── WithdrawalModal/
│ │ │ ├── WithdrawalModal.stories.tsx
│ │ │ ├── WithdrawalModal.styles.ts
│ │ │ └── WithdrawalModal.tsx
│ │ ├── constants/
│ │ │ ├── api.ts
│ │ │ ├── category.ts
│ │ │ ├── date.ts
│ │ │ ├── index.ts
│ │ │ ├── message.ts
│ │ │ ├── schedule.ts
│ │ │ ├── style.ts
│ │ │ └── validate.ts
│ │ ├── domains/
│ │ │ └── schedule.ts
│ │ ├── hooks/
│ │ │ ├── @queries/
│ │ │ │ ├── category.ts
│ │ │ │ ├── googleCalendar.ts
│ │ │ │ ├── login.ts
│ │ │ │ ├── profile.ts
│ │ │ │ ├── schedule.ts
│ │ │ │ └── subscription.ts
│ │ │ ├── useCalendar.ts
│ │ │ ├── useControlledInput.ts
│ │ │ ├── useHoverCategoryItem.ts
│ │ │ ├── useIntersect.ts
│ │ │ ├── useModalPosition.ts
│ │ │ ├── useRootFontSize.ts
│ │ │ ├── useSnackBar.ts
│ │ │ ├── useToggle.ts
│ │ │ ├── useUserValue.ts
│ │ │ ├── useValidateCategory.ts
│ │ │ └── useValidateSchedule.ts
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── pages/
│ │ │ ├── AuthPage/
│ │ │ │ └── AuthPage.tsx
│ │ │ ├── CalendarPage/
│ │ │ │ ├── CalendarPage.styles.ts
│ │ │ │ └── CalendarPage.tsx
│ │ │ ├── CategoryPage/
│ │ │ │ ├── CategoryPage.styles.ts
│ │ │ │ └── CategoryPage.tsx
│ │ │ ├── ErrorPage/
│ │ │ │ ├── ErrorPage.styles.ts
│ │ │ │ └── ErrorPage.tsx
│ │ │ ├── NotFoundPage/
│ │ │ │ ├── NotFoundPage.styles.ts
│ │ │ │ └── NotFoundPage.tsx
│ │ │ ├── PrivacyPolicyPage/
│ │ │ │ ├── PrivacyPolicyPage.styles.ts
│ │ │ │ └── PrivacyPolicyPage.tsx
│ │ │ └── StartPage/
│ │ │ ├── StartPage.styles.ts
│ │ │ └── StartPage.tsx
│ │ ├── recoil/
│ │ │ ├── atoms/
│ │ │ │ └── index.ts
│ │ │ └── selectors/
│ │ │ └── index.ts
│ │ ├── styles/
│ │ │ ├── GlobalStyle.tsx
│ │ │ └── theme.ts
│ │ ├── utils/
│ │ │ ├── date.ts
│ │ │ ├── index.ts
│ │ │ └── storage.ts
│ │ └── validation/
│ │ └── index.ts
│ ├── tsconfig.json
│ └── webpack.config.js
└── jenkins/
├── backend-dev.jenkinsfile
├── backend-prod.jenkinsfile
├── frontend-dev.jenkinsfile
└── frontend-prod.jenkinsfile
SYMBOL INDEX (1462 symbols across 307 files)
FILE: backend/src/main/java/com/allog/dallog/DallogApplication.java
class DallogApplication (line 6) | @SpringBootApplication
method main (line 9) | public static void main(String[] args) {
FILE: backend/src/main/java/com/allog/dallog/auth/application/AuthService.java
class AuthService (line 17) | @Transactional(readOnly = true)
method AuthService (line 26) | public AuthService(final MemberRepository memberRepository, final OAut...
method generateAccessAndRefreshToken (line 34) | @Transactional
method findMember (line 45) | private Member findMember(final OAuthMember oAuthMember) {
method saveMember (line 53) | private Member saveMember(final OAuthMember oAuthMember) {
method getOAuthToken (line 59) | private OAuthToken getOAuthToken(final OAuthMember oAuthMember, final ...
method generateAccessToken (line 67) | public AccessTokenResponse generateAccessToken(final TokenRenewalReque...
method extractMemberId (line 73) | public Long extractMemberId(final String accessToken) {
FILE: backend/src/main/java/com/allog/dallog/auth/application/AuthTokenCreator.java
class AuthTokenCreator (line 7) | @Component
method AuthTokenCreator (line 13) | public AuthTokenCreator(final TokenProvider tokenProvider, final Token...
method createAuthToken (line 18) | public AuthToken createAuthToken(final Long memberId) {
method createRefreshToken (line 24) | private String createRefreshToken(final Long memberId) {
method renewAuthToken (line 32) | public AuthToken renewAuthToken(final String refreshToken) {
method extractPayload (line 44) | public Long extractPayload(final String accessToken) {
FILE: backend/src/main/java/com/allog/dallog/auth/application/JwtTokenProvider.java
class JwtTokenProvider (line 16) | @Component
method JwtTokenProvider (line 23) | public JwtTokenProvider(@Value("${security.jwt.token.secret-key}") fin...
method createAccessToken (line 31) | @Override
method createRefreshToken (line 36) | @Override
method createToken (line 41) | private String createToken(final String payload, final Long validityIn...
method getPayload (line 53) | @Override
method validateToken (line 63) | @Override
FILE: backend/src/main/java/com/allog/dallog/auth/application/OAuthClient.java
type OAuthClient (line 6) | public interface OAuthClient {
method getOAuthMember (line 8) | OAuthMember getOAuthMember(final String code, final String redirectUri);
method getAccessToken (line 10) | OAuthAccessTokenResponse getAccessToken(final String refreshToken);
FILE: backend/src/main/java/com/allog/dallog/auth/application/OAuthUri.java
type OAuthUri (line 3) | @FunctionalInterface
method generate (line 6) | String generate(final String redirectUri);
FILE: backend/src/main/java/com/allog/dallog/auth/application/TokenCreator.java
type TokenCreator (line 5) | public interface TokenCreator {
method createAuthToken (line 7) | AuthToken createAuthToken(final Long memberId);
method renewAuthToken (line 9) | AuthToken renewAuthToken(final String outRefreshToken);
method extractPayload (line 11) | Long extractPayload(final String accessToken);
FILE: backend/src/main/java/com/allog/dallog/auth/application/TokenProvider.java
type TokenProvider (line 3) | public interface TokenProvider {
method createAccessToken (line 5) | String createAccessToken(final String payload);
method createRefreshToken (line 7) | String createRefreshToken(final String payload);
method getPayload (line 9) | String getPayload(final String token);
method validateToken (line 11) | void validateToken(final String token);
FILE: backend/src/main/java/com/allog/dallog/auth/domain/AuthToken.java
class AuthToken (line 5) | public class AuthToken {
method AuthToken (line 10) | public AuthToken(final String accessToken, final String refreshToken) {
method getAccessToken (line 15) | public String getAccessToken() {
method getRefreshToken (line 19) | public String getRefreshToken() {
method validateHasSameRefreshToken (line 23) | public void validateHasSameRefreshToken(final String otherRefreshToken) {
FILE: backend/src/main/java/com/allog/dallog/auth/domain/InMemoryAuthTokenRepository.java
class InMemoryAuthTokenRepository (line 9) | @Component
method save (line 14) | @Override
method deleteAll (line 20) | @Override
method deleteByMemberId (line 25) | @Override
method exist (line 30) | @Override
method getToken (line 35) | @Override
FILE: backend/src/main/java/com/allog/dallog/auth/domain/OAuthToken.java
class OAuthToken (line 16) | @Table(name = "oauth_tokens")
method OAuthToken (line 31) | protected OAuthToken() {
method OAuthToken (line 34) | public OAuthToken(final Member member, final String refreshToken) {
method change (line 39) | public void change(final String refreshToken) {
method getId (line 45) | public Long getId() {
method getMember (line 49) | public Member getMember() {
method getRefreshToken (line 53) | public String getRefreshToken() {
FILE: backend/src/main/java/com/allog/dallog/auth/domain/OAuthTokenRepository.java
type OAuthTokenRepository (line 8) | public interface OAuthTokenRepository extends JpaRepository<OAuthToken, ...
method existsByMemberId (line 10) | boolean existsByMemberId(final Long memberId);
method findByMemberId (line 12) | @Query("SELECT o "
method getByMemberId (line 17) | default OAuthToken getByMemberId(final Long memberId) {
FILE: backend/src/main/java/com/allog/dallog/auth/domain/TokenRepository.java
type TokenRepository (line 3) | public interface TokenRepository {
method save (line 5) | String save(final Long memberId, final String refreshToken);
method deleteAll (line 7) | void deleteAll();
method deleteByMemberId (line 9) | void deleteByMemberId(final Long memberId);
method exist (line 11) | boolean exist(final Long memberId);
method getToken (line 13) | String getToken(final Long memberId);
FILE: backend/src/main/java/com/allog/dallog/auth/dto/LoginMember.java
class LoginMember (line 3) | public class LoginMember {
method LoginMember (line 7) | private LoginMember() {
method LoginMember (line 10) | public LoginMember(final Long id) {
method getId (line 14) | public Long getId() {
FILE: backend/src/main/java/com/allog/dallog/auth/dto/OAuthMember.java
class OAuthMember (line 6) | public class OAuthMember {
method OAuthMember (line 13) | public OAuthMember(final String email, final String displayName, final...
method getEmail (line 21) | public String getEmail() {
method getDisplayName (line 25) | public String getDisplayName() {
method getProfileImageUrl (line 29) | public String getProfileImageUrl() {
method getRefreshToken (line 33) | public String getRefreshToken() {
method toMember (line 37) | public Member toMember() {
FILE: backend/src/main/java/com/allog/dallog/auth/dto/request/TokenRenewalRequest.java
class TokenRenewalRequest (line 5) | public class TokenRenewalRequest {
method TokenRenewalRequest (line 10) | private TokenRenewalRequest() {
method TokenRenewalRequest (line 13) | public TokenRenewalRequest(final String refreshToken) {
method getRefreshToken (line 17) | public String getRefreshToken() {
FILE: backend/src/main/java/com/allog/dallog/auth/dto/request/TokenRequest.java
class TokenRequest (line 6) | public class TokenRequest {
method TokenRequest (line 14) | private TokenRequest() {
method TokenRequest (line 17) | public TokenRequest(final String code, final String redirectUri) {
method getCode (line 22) | public String getCode() {
method getRedirectUri (line 26) | public String getRedirectUri() {
FILE: backend/src/main/java/com/allog/dallog/auth/dto/response/AccessAndRefreshTokenResponse.java
class AccessAndRefreshTokenResponse (line 3) | public class AccessAndRefreshTokenResponse {
method AccessAndRefreshTokenResponse (line 8) | private AccessAndRefreshTokenResponse() {
method AccessAndRefreshTokenResponse (line 11) | public AccessAndRefreshTokenResponse(final String accessToken, final S...
method getAccessToken (line 16) | public String getAccessToken() {
method getRefreshToken (line 20) | public String getRefreshToken() {
FILE: backend/src/main/java/com/allog/dallog/auth/dto/response/AccessTokenResponse.java
class AccessTokenResponse (line 3) | public class AccessTokenResponse {
method AccessTokenResponse (line 7) | private AccessTokenResponse() {
method AccessTokenResponse (line 10) | public AccessTokenResponse(final String accessToken) {
method getAccessToken (line 14) | public String getAccessToken() {
FILE: backend/src/main/java/com/allog/dallog/auth/dto/response/OAuthAccessTokenResponse.java
class OAuthAccessTokenResponse (line 6) | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
method OAuthAccessTokenResponse (line 11) | private OAuthAccessTokenResponse() {
method OAuthAccessTokenResponse (line 14) | public OAuthAccessTokenResponse(final String accessToken) {
method getAccessToken (line 18) | public String getAccessToken() {
FILE: backend/src/main/java/com/allog/dallog/auth/dto/response/OAuthUriResponse.java
class OAuthUriResponse (line 4) | public class OAuthUriResponse {
method OAuthUriResponse (line 8) | private OAuthUriResponse() {
method OAuthUriResponse (line 11) | public OAuthUriResponse(final String oAuthUri) {
method getoAuthUri (line 15) | public String getoAuthUri() {
FILE: backend/src/main/java/com/allog/dallog/auth/event/MemberSavedEvent.java
class MemberSavedEvent (line 3) | public class MemberSavedEvent {
method MemberSavedEvent (line 7) | public MemberSavedEvent(final Long memberId) {
method getMemberId (line 11) | public Long getMemberId() {
FILE: backend/src/main/java/com/allog/dallog/auth/exception/EmptyAuthorizationHeaderException.java
class EmptyAuthorizationHeaderException (line 3) | public class EmptyAuthorizationHeaderException extends RuntimeException {
method EmptyAuthorizationHeaderException (line 5) | public EmptyAuthorizationHeaderException(final String message) {
method EmptyAuthorizationHeaderException (line 9) | public EmptyAuthorizationHeaderException() {
FILE: backend/src/main/java/com/allog/dallog/auth/exception/InvalidTokenException.java
class InvalidTokenException (line 3) | public class InvalidTokenException extends RuntimeException {
method InvalidTokenException (line 5) | public InvalidTokenException(final String message) {
method InvalidTokenException (line 9) | public InvalidTokenException() {
FILE: backend/src/main/java/com/allog/dallog/auth/exception/NoPermissionException.java
class NoPermissionException (line 3) | public class NoPermissionException extends RuntimeException {
method NoPermissionException (line 5) | public NoPermissionException(final String message) {
method NoPermissionException (line 9) | public NoPermissionException() {
FILE: backend/src/main/java/com/allog/dallog/auth/exception/NoSuchOAuthTokenException.java
class NoSuchOAuthTokenException (line 3) | public class NoSuchOAuthTokenException extends RuntimeException {
method NoSuchOAuthTokenException (line 5) | public NoSuchOAuthTokenException(final String message) {
method NoSuchOAuthTokenException (line 9) | public NoSuchOAuthTokenException() {
FILE: backend/src/main/java/com/allog/dallog/auth/exception/NoSuchTokenException.java
class NoSuchTokenException (line 3) | public class NoSuchTokenException extends RuntimeException {
method NoSuchTokenException (line 5) | public NoSuchTokenException(final String message) {
method NoSuchTokenException (line 9) | public NoSuchTokenException() {
FILE: backend/src/main/java/com/allog/dallog/auth/presentation/AuthController.java
class AuthController (line 23) | @RequestMapping("/api/auth")
method AuthController (line 31) | public AuthController(final OAuthUri oAuthUri, final OAuthClient oAuth...
method generateLink (line 37) | @GetMapping("/{oauthProvider}/oauth-uri")
method generateAccessAndRefreshToken (line 44) | @PostMapping("/{oauthProvider}/token")
method generateAccessToken (line 52) | @PostMapping("/token/access")
method validateToken (line 59) | @GetMapping("/validate/token")
FILE: backend/src/main/java/com/allog/dallog/auth/presentation/AuthenticationPrincipalArgumentResolver.java
class AuthenticationPrincipalArgumentResolver (line 13) | @Component
method AuthenticationPrincipalArgumentResolver (line 18) | public AuthenticationPrincipalArgumentResolver(final AuthService authS...
method supportsParameter (line 22) | @Override
method resolveArgument (line 27) | @Override
FILE: backend/src/main/java/com/allog/dallog/auth/presentation/AuthorizationExtractor.java
class AuthorizationExtractor (line 9) | public class AuthorizationExtractor {
method extract (line 13) | public static String extract(final HttpServletRequest request) {
method validateAuthorizationFormat (line 23) | private static void validateAuthorizationFormat(final String authoriza...
FILE: backend/src/main/java/com/allog/dallog/category/application/CategoryService.java
class CategoryService (line 37) | @Transactional(readOnly = true)
method CategoryService (line 51) | public CategoryService(final CategoryRepository categoryRepository,
method save (line 65) | @Transactional
method save (line 78) | @Transactional
method savePersonalCategory (line 92) | @Transactional
method subscribeCategory (line 102) | private void subscribeCategory(final Member member, final Category cat...
method createCategoryRoleAsAdminToCreator (line 107) | private void createCategoryRoleAsAdminToCreator(final Member member, f...
method findNormalByName (line 112) | public CategoriesResponse findNormalByName(final String name) {
method findScheduleEditableCategories (line 118) | public CategoriesResponse findScheduleEditableCategories(final Long me...
method findAdminCategories (line 124) | public CategoriesResponse findAdminCategories(final Long memberId) {
method toCategories (line 129) | private List<Category> toCategories(final List<CategoryRole> categoryR...
method findDetailCategoryById (line 136) | public CategoryDetailResponse findDetailCategoryById(final Long id) {
method update (line 142) | @Transactional
method delete (line 152) | @Transactional
method validateNotPersonalCategory (line 168) | private void validateNotPersonalCategory(final Category category) {
FILE: backend/src/main/java/com/allog/dallog/category/application/ExternalCategoryDetailService.java
class ExternalCategoryDetailService (line 12) | @Transactional(readOnly = true)
method ExternalCategoryDetailService (line 19) | public ExternalCategoryDetailService(final ExternalCategoryDetailRepos...
method findByMemberId (line 25) | public List<ExternalCategoryDetail> findByMemberId(final Long memberId) {
FILE: backend/src/main/java/com/allog/dallog/category/domain/Category.java
class Category (line 19) | @Table(name = "categories")
method Category (line 41) | protected Category() {
method Category (line 44) | public Category(final String name, final Member member) {
method Category (line 51) | public Category(final String name, final Member member, final Category...
method changeName (line 58) | public void changeName(final String name) {
method validatePersonal (line 64) | private void validatePersonal() {
method validateNameLength (line 70) | private void validateNameLength(final String name) {
method validateSubscriptionPossible (line 79) | public void validateSubscriptionPossible(final Member member) {
method validateNotExternalCategory (line 85) | public void validateNotExternalCategory() {
method isCreatorId (line 91) | public boolean isCreatorId(final Long creatorId) {
method isNormal (line 95) | public boolean isNormal() {
method isPersonal (line 99) | public boolean isPersonal() {
method isInternal (line 103) | public boolean isInternal() {
method isExternal (line 107) | public boolean isExternal() {
method getId (line 111) | public Long getId() {
method getName (line 115) | public String getName() {
method getMember (line 119) | public Member getMember() {
method setMember (line 123) | public void setMember(final Member member) {
method getCategoryType (line 127) | public CategoryType getCategoryType() {
FILE: backend/src/main/java/com/allog/dallog/category/domain/CategoryRepository.java
type CategoryRepository (line 8) | public interface CategoryRepository extends JpaRepository<Category, Long> {
method findByCategoryTypeAndNameContaining (line 10) | @Query("SELECT c "
method findByMemberIdAndCategoryType (line 18) | @Query("SELECT c "
method findByMemberId (line 23) | @Query("SELECT c "
method getById (line 28) | default Category getById(final Long id) {
FILE: backend/src/main/java/com/allog/dallog/category/domain/CategoryType.java
type CategoryType (line 5) | public enum CategoryType {
method from (line 9) | public static CategoryType from(final String value) {
FILE: backend/src/main/java/com/allog/dallog/category/domain/ExternalCategoryDetail.java
class ExternalCategoryDetail (line 14) | @Table(name = "external_category_details")
method ExternalCategoryDetail (line 30) | protected ExternalCategoryDetail() {
method ExternalCategoryDetail (line 33) | public ExternalCategoryDetail(final Category category, final String ex...
method getId (line 38) | public Long getId() {
method getCategory (line 42) | public Category getCategory() {
method getExternalId (line 46) | public String getExternalId() {
FILE: backend/src/main/java/com/allog/dallog/category/domain/ExternalCategoryDetailRepository.java
type ExternalCategoryDetailRepository (line 9) | public interface ExternalCategoryDetailRepository extends JpaRepository<...
method findByCategory (line 11) | Optional<ExternalCategoryDetail> findByCategory(final Category category);
method findByCategoryIn (line 13) | List<ExternalCategoryDetail> findByCategoryIn(final List<Category> cat...
method existsByExternalIdAndCategoryIn (line 15) | boolean existsByExternalIdAndCategoryIn(final String externalId, final...
method deleteByCategoryId (line 17) | void deleteByCategoryId(final Long categoryId);
method deleteByCategoryIdIn (line 19) | void deleteByCategoryIdIn(final List<Long> categoryIds);
method getByCategory (line 21) | default ExternalCategoryDetail getByCategory(final Category category) {
method validateExistByExternalIdAndCategoryIn (line 26) | default void validateExistByExternalIdAndCategoryIn(final String exter...
FILE: backend/src/main/java/com/allog/dallog/category/dto/request/CategoryCreateRequest.java
class CategoryCreateRequest (line 8) | public class CategoryCreateRequest {
method CategoryCreateRequest (line 16) | private CategoryCreateRequest() {
method CategoryCreateRequest (line 19) | public CategoryCreateRequest(final String name, final CategoryType cat...
method toEntity (line 24) | public Category toEntity(final Member member) {
method getName (line 28) | public String getName() {
method getCategoryType (line 32) | public String getCategoryType() {
FILE: backend/src/main/java/com/allog/dallog/category/dto/request/CategoryUpdateRequest.java
class CategoryUpdateRequest (line 5) | public class CategoryUpdateRequest {
method CategoryUpdateRequest (line 10) | private CategoryUpdateRequest() {
method CategoryUpdateRequest (line 13) | public CategoryUpdateRequest(final String name) {
method getName (line 17) | public String getName() {
FILE: backend/src/main/java/com/allog/dallog/category/dto/request/ExternalCategoryCreateRequest.java
class ExternalCategoryCreateRequest (line 5) | public class ExternalCategoryCreateRequest {
method ExternalCategoryCreateRequest (line 13) | private ExternalCategoryCreateRequest() {
method ExternalCategoryCreateRequest (line 16) | public ExternalCategoryCreateRequest(final String externalId, final St...
method getExternalId (line 21) | public String getExternalId() {
method getName (line 25) | public String getName() {
FILE: backend/src/main/java/com/allog/dallog/category/dto/response/CategoriesResponse.java
class CategoriesResponse (line 7) | public class CategoriesResponse {
method CategoriesResponse (line 11) | private CategoriesResponse() {
method CategoriesResponse (line 14) | public CategoriesResponse(final List<Category> categories) {
method toResponses (line 18) | private List<CategoryResponse> toResponses(final List<Category> catego...
method getCategories (line 24) | public List<CategoryResponse> getCategories() {
FILE: backend/src/main/java/com/allog/dallog/category/dto/response/CategoryDetailResponse.java
class CategoryDetailResponse (line 7) | public class CategoryDetailResponse {
method CategoryDetailResponse (line 16) | private CategoryDetailResponse() {
method CategoryDetailResponse (line 19) | public CategoryDetailResponse(final Category category, final int subsc...
method CategoryDetailResponse (line 24) | public CategoryDetailResponse(final Long id, final String name, final ...
method getId (line 35) | public Long getId() {
method getName (line 39) | public String getName() {
method getCategoryType (line 43) | public String getCategoryType() {
method getSubscriberCount (line 47) | public int getSubscriberCount() {
method getCreator (line 51) | public MemberResponse getCreator() {
method getCreatedAt (line 55) | public LocalDateTime getCreatedAt() {
FILE: backend/src/main/java/com/allog/dallog/category/dto/response/CategoryResponse.java
class CategoryResponse (line 7) | public class CategoryResponse {
method CategoryResponse (line 15) | private CategoryResponse() {
method CategoryResponse (line 18) | public CategoryResponse(final Category category) {
method CategoryResponse (line 23) | public CategoryResponse(final Long id, final String name, final String...
method getId (line 32) | public Long getId() {
method getName (line 36) | public String getName() {
method getCategoryType (line 40) | public String getCategoryType() {
method getCreator (line 44) | public MemberResponse getCreator() {
method getCreatedAt (line 48) | public LocalDateTime getCreatedAt() {
FILE: backend/src/main/java/com/allog/dallog/category/exception/ExistExternalCategoryException.java
class ExistExternalCategoryException (line 3) | public class ExistExternalCategoryException extends RuntimeException {
method ExistExternalCategoryException (line 5) | public ExistExternalCategoryException(final String message) {
method ExistExternalCategoryException (line 9) | public ExistExternalCategoryException() {
FILE: backend/src/main/java/com/allog/dallog/category/exception/InvalidCategoryException.java
class InvalidCategoryException (line 3) | public class InvalidCategoryException extends RuntimeException {
method InvalidCategoryException (line 5) | public InvalidCategoryException(final String message) {
method InvalidCategoryException (line 9) | public InvalidCategoryException() {
FILE: backend/src/main/java/com/allog/dallog/category/exception/NoSuchCategoryException.java
class NoSuchCategoryException (line 3) | public class NoSuchCategoryException extends RuntimeException {
method NoSuchCategoryException (line 5) | public NoSuchCategoryException(final String message) {
method NoSuchCategoryException (line 9) | public NoSuchCategoryException() {
FILE: backend/src/main/java/com/allog/dallog/category/exception/NoSuchExternalCategoryDetailException.java
class NoSuchExternalCategoryDetailException (line 3) | public class NoSuchExternalCategoryDetailException extends RuntimeExcept...
method NoSuchExternalCategoryDetailException (line 5) | public NoSuchExternalCategoryDetailException(final String message) {
method NoSuchExternalCategoryDetailException (line 9) | public NoSuchExternalCategoryDetailException() {
FILE: backend/src/main/java/com/allog/dallog/category/presentaion/CategoryController.java
class CategoryController (line 27) | @RequestMapping("/api/categories")
method CategoryController (line 34) | public CategoryController(final CategoryService categoryService, final...
method save (line 39) | @PostMapping
method findNormalByName (line 46) | @GetMapping
method findDetailCategoryById (line 51) | @GetMapping("/{categoryId}")
method findScheduleEditableCategories (line 56) | @GetMapping("/me/schedule-editable") // 일정 추가, 수정 모달의 카테고리 목록에 사용됨
method findAdminCategories (line 62) | @GetMapping("/me/admin") // 카테고리 관리 페이지에 접근할 수 있는지 판단하기 위해 사용됨
method update (line 68) | @PatchMapping("/{categoryId}")
method delete (line 76) | @DeleteMapping("/{categoryId}")
method findSubscribers (line 83) | @GetMapping("/{categoryId}/subscribers")
method updateRole (line 90) | @PatchMapping("/{categoryId}/subscribers/{memberId}/role")
FILE: backend/src/main/java/com/allog/dallog/categoryrole/application/CategoryRoleService.java
class CategoryRoleService (line 15) | @Transactional(readOnly = true)
method CategoryRoleService (line 21) | public CategoryRoleService(final CategoryRoleRepository categoryRoleRe...
method findSubscribers (line 25) | public SubscribersResponse findSubscribers(final Long loginMemberId, f...
method updateRole (line 33) | @Transactional
method getCategoryRole (line 51) | private CategoryRole getCategoryRole(final Long memberId, final List<C...
method validateLoginMemberAuthority (line 58) | private void validateLoginMemberAuthority(final Long loginMemberId, fi...
method validateIsTargetMemberSoleAdmin (line 67) | private void validateIsTargetMemberSoleAdmin(final List<CategoryRole> ...
method validateCategoryType (line 74) | private void validateCategoryType(final Category category) {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryAuthority.java
type CategoryAuthority (line 3) | public enum CategoryAuthority {
method CategoryAuthority (line 14) | CategoryAuthority(final String name) {
method getName (line 18) | public String getName() {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryRole.java
class CategoryRole (line 19) | @Table(name = "category_roles")
method CategoryRole (line 43) | protected CategoryRole() {
method CategoryRole (line 46) | public CategoryRole(final Category category, final Member member, fina...
method isAdmin (line 52) | public boolean isAdmin() {
method isNone (line 56) | public boolean isNone() {
method validateAuthority (line 60) | public void validateAuthority(final CategoryAuthority authority) {
method ableTo (line 66) | public boolean ableTo(final CategoryAuthority authority) {
method changeRole (line 70) | public void changeRole(final CategoryRoleType categoryRoleType) {
method getId (line 74) | public Long getId() {
method getCategory (line 78) | public Category getCategory() {
method getMember (line 82) | public Member getMember() {
method getCategoryRoleType (line 86) | public CategoryRoleType getCategoryRoleType() {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryRoleRepository.java
type CategoryRoleRepository (line 13) | public interface CategoryRoleRepository extends JpaRepository<CategoryRo...
method findByMemberIdAndCategoryId (line 15) | @Lock(LockModeType.OPTIMISTIC)
method findByCategoryId (line 21) | @EntityGraph(attributePaths = {"member"})
method findByMemberId (line 24) | @EntityGraph(attributePaths = {"category", "category.member"})
method countByMemberIdAndCategoryRoleType (line 27) | @Query("SELECT count(cr) "
method countByCategoryIdAndCategoryRoleType (line 33) | int countByCategoryIdAndCategoryRoleType(final Long categoryId, final ...
method deleteByCategoryId (line 35) | void deleteByCategoryId(final Long categoryId);
method getByMemberIdAndCategoryId (line 37) | default CategoryRole getByMemberIdAndCategoryId(final Long memberId, f...
method isMemberSoleAdminInCategory (line 42) | default boolean isMemberSoleAdminInCategory(final Long memberId, final...
method validateManagingCategoryLimit (line 49) | default void validateManagingCategoryLimit(final Long memberId, final ...
FILE: backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryRoleType.java
type CategoryRoleType (line 16) | public enum CategoryRoleType {
method CategoryRoleType (line 23) | CategoryRoleType(final Set<CategoryAuthority> authorities) {
method getHavingAuthorities (line 27) | public static Set<CategoryRoleType> getHavingAuthorities(final Set<Cat...
method isCategoryRoleTypeContainsAuthorities (line 33) | private static boolean isCategoryRoleTypeContainsAuthorities(final Set...
method ableTo (line 38) | public boolean ableTo(final CategoryAuthority authority) {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/dto/request/CategoryRoleUpdateRequest.java
class CategoryRoleUpdateRequest (line 6) | public class CategoryRoleUpdateRequest {
method CategoryRoleUpdateRequest (line 11) | private CategoryRoleUpdateRequest() {
method CategoryRoleUpdateRequest (line 14) | public CategoryRoleUpdateRequest(final CategoryRoleType categoryRoleTy...
method getCategoryRoleType (line 18) | public CategoryRoleType getCategoryRoleType() {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/dto/response/MemberWithRoleTypeResponse.java
class MemberWithRoleTypeResponse (line 7) | public class MemberWithRoleTypeResponse {
method MemberWithRoleTypeResponse (line 12) | private MemberWithRoleTypeResponse() {
method MemberWithRoleTypeResponse (line 15) | public MemberWithRoleTypeResponse(final CategoryRole categoryRole) {
method getMember (line 20) | public MemberResponse getMember() {
method getCategoryRoleType (line 24) | public CategoryRoleType getCategoryRoleType() {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/dto/response/SubscribersResponse.java
class SubscribersResponse (line 7) | public class SubscribersResponse {
method SubscribersResponse (line 11) | private SubscribersResponse() {
method SubscribersResponse (line 14) | public SubscribersResponse(final List<CategoryRole> categoryRoles) {
method toResponses (line 18) | private List<MemberWithRoleTypeResponse> toResponses(final List<Catego...
method getSubscribers (line 24) | public List<MemberWithRoleTypeResponse> getSubscribers() {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/exception/ManagingCategoryLimitExcessException.java
class ManagingCategoryLimitExcessException (line 3) | public class ManagingCategoryLimitExcessException extends RuntimeExcepti...
method ManagingCategoryLimitExcessException (line 7) | public ManagingCategoryLimitExcessException() {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/exception/NoCategoryAuthorityException.java
class NoCategoryAuthorityException (line 3) | public class NoCategoryAuthorityException extends RuntimeException {
method NoCategoryAuthorityException (line 5) | public NoCategoryAuthorityException(final String authorityName) {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/exception/NoSuchCategoryRoleException.java
class NoSuchCategoryRoleException (line 3) | public class NoSuchCategoryRoleException extends RuntimeException {
method NoSuchCategoryRoleException (line 5) | public NoSuchCategoryRoleException(final String message) {
method NoSuchCategoryRoleException (line 9) | public NoSuchCategoryRoleException() {
FILE: backend/src/main/java/com/allog/dallog/categoryrole/exception/NotAbleToChangeRoleException.java
class NotAbleToChangeRoleException (line 3) | public class NotAbleToChangeRoleException extends RuntimeException {
method NotAbleToChangeRoleException (line 5) | public NotAbleToChangeRoleException(final String message) {
method NotAbleToChangeRoleException (line 9) | public NotAbleToChangeRoleException() {
method concurrentIssue (line 13) | public static NotAbleToChangeRoleException concurrentIssue() {
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/application/ExternalCalendarClient.java
type ExternalCalendarClient (line 7) | public interface ExternalCalendarClient {
method getExternalCalendars (line 9) | List<ExternalCalendar> getExternalCalendars(final String accessToken);
method getExternalCalendarSchedules (line 11) | List<IntegrationSchedule> getExternalCalendarSchedules(final String ac...
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/application/ExternalCalendarService.java
class ExternalCalendarService (line 9) | @Service
method ExternalCalendarService (line 16) | public ExternalCalendarService(final OAuthClient oAuthClient, final Ex...
method findByMemberId (line 23) | public ExternalCalendarsResponse findByMemberId(final Long memberId) {
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/dto/ExternalCalendar.java
class ExternalCalendar (line 3) | public class ExternalCalendar {
method ExternalCalendar (line 8) | private ExternalCalendar() {
method ExternalCalendar (line 11) | public ExternalCalendar(final String calendarId, final String summary) {
method getCalendarId (line 16) | public String getCalendarId() {
method getSummary (line 20) | public String getSummary() {
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/dto/ExternalCalendarsResponse.java
class ExternalCalendarsResponse (line 5) | public class ExternalCalendarsResponse {
method ExternalCalendarsResponse (line 9) | private ExternalCalendarsResponse() {
method ExternalCalendarsResponse (line 12) | public ExternalCalendarsResponse(final List<ExternalCalendar> external...
method getExternalCalendars (line 16) | public List<ExternalCalendar> getExternalCalendars() {
FILE: backend/src/main/java/com/allog/dallog/externalcalendar/presentation/ExternalCalendarController.java
class ExternalCalendarController (line 19) | @RequestMapping("/api/external-calendars/me")
method ExternalCalendarController (line 26) | public ExternalCalendarController(final ExternalCalendarService extern...
method getExternalCalendar (line 32) | @GetMapping
method save (line 38) | @PostMapping
FILE: backend/src/main/java/com/allog/dallog/global/config/JpaConfig.java
class JpaConfig (line 6) | @Configuration
FILE: backend/src/main/java/com/allog/dallog/global/config/PropertiesConfig.java
class PropertiesConfig (line 7) | @Configuration
FILE: backend/src/main/java/com/allog/dallog/global/config/WebConfig.java
class WebConfig (line 10) | @Configuration
method WebConfig (line 16) | public WebConfig(@Value("${cors.allow-origin.urls}") final List<String...
method addCorsMappings (line 22) | @Override
method addArgumentResolvers (line 32) | @Override
FILE: backend/src/main/java/com/allog/dallog/global/config/cache/CacheConfig.java
class CacheConfig (line 12) | @Configuration
method cacheManager (line 20) | @Bean
method evict (line 28) | @Scheduled(cron = "0 0 0 * * *")
FILE: backend/src/main/java/com/allog/dallog/global/config/cache/ExpiringConcurrentMapCache.java
class ExpiringConcurrentMapCache (line 10) | public class ExpiringConcurrentMapCache extends ConcurrentMapCache {
method ExpiringConcurrentMapCache (line 15) | public ExpiringConcurrentMapCache(final String name, final long expire...
method lookup (line 21) | @Override
method put (line 33) | @Override
method evictAllExpired (line 41) | public void evictAllExpired() {
method isCacheValid (line 50) | private boolean isCacheValid(final LocalDateTime expiredDate) {
FILE: backend/src/main/java/com/allog/dallog/global/config/properties/GoogleProperties.java
class GoogleProperties (line 7) | @ConfigurationProperties("oauth.google")
method GoogleProperties (line 19) | public GoogleProperties(final String clientId, final String clientSecr...
method getClientId (line 31) | public String getClientId() {
method getClientSecret (line 35) | public String getClientSecret() {
method getOAuthEndPoint (line 39) | public String getOAuthEndPoint() {
method getResponseType (line 43) | public String getResponseType() {
method getScopes (line 47) | public List<String> getScopes() {
method getTokenUri (line 51) | public String getTokenUri() {
method getAccessType (line 55) | public String getAccessType() {
FILE: backend/src/main/java/com/allog/dallog/global/config/replication/DataSourceConfiguration.java
class DataSourceConfiguration (line 21) | @Configuration
method dataSource (line 25) | @Bean
method sourceDataSource (line 33) | @Bean
method replica1DataSource (line 41) | @Bean
method replica2DataSource (line 49) | @Bean
method routingDataSource (line 57) | @Bean
FILE: backend/src/main/java/com/allog/dallog/global/config/replication/DataSourceKey.java
type DataSourceKey (line 7) | public enum DataSourceKey {
method DataSourceKey (line 15) | DataSourceKey(final String key, final boolean isReplica) {
method getReplicas (line 20) | public static List<DataSourceKey> getReplicas() {
class KeyName (line 27) | public static class KeyName {
FILE: backend/src/main/java/com/allog/dallog/global/config/replication/RandomReplicaKeys.java
class RandomReplicaKeys (line 6) | public class RandomReplicaKeys {
method RandomReplicaKeys (line 13) | public RandomReplicaKeys() {
method next (line 18) | public DataSourceKey next() {
FILE: backend/src/main/java/com/allog/dallog/global/config/replication/RoutingDataSource.java
class RoutingDataSource (line 8) | public class RoutingDataSource extends AbstractRoutingDataSource {
method determineCurrentLookupKey (line 12) | @Override
FILE: backend/src/main/java/com/allog/dallog/global/entity/BaseEntity.java
class BaseEntity (line 11) | @MappedSuperclass
method getCreatedAt (line 23) | public LocalDateTime getCreatedAt() {
method getUpdatedAt (line 27) | public LocalDateTime getUpdatedAt() {
FILE: backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java
class ControllerAdvice (line 39) | @RestControllerAdvice
method handleInvalidData (line 45) | @ExceptionHandler({
method handleInvalidRequestBody (line 61) | @ExceptionHandler(HttpMessageNotReadableException.class)
method handleInvalidDtoField (line 67) | @ExceptionHandler(MethodArgumentNotValidException.class)
method handleTypeMismatch (line 77) | @ExceptionHandler(MethodArgumentTypeMismatchException.class)
method handleInvalidAuthorization (line 83) | @ExceptionHandler({
method handleNoPermission (line 92) | @ExceptionHandler({
method handleNoSuchData (line 101) | @ExceptionHandler({
method handleNotSupportedMethod (line 115) | @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
method handleOAuthException (line 121) | @ExceptionHandler(OAuthException.class)
method handleUnexpectedException (line 128) | @ExceptionHandler(Exception.class)
FILE: backend/src/main/java/com/allog/dallog/global/error/dto/ErrorReportRequest.java
class ErrorReportRequest (line 5) | public class ErrorReportRequest {
method ErrorReportRequest (line 12) | public ErrorReportRequest(final HttpServletRequest request, final Exce...
method getLogMessage (line 17) | public String getLogMessage() {
method getRequest (line 24) | public HttpServletRequest getRequest() {
method getException (line 28) | public Exception getException() {
FILE: backend/src/main/java/com/allog/dallog/global/error/dto/ErrorResponse.java
class ErrorResponse (line 3) | public class ErrorResponse {
method ErrorResponse (line 7) | public ErrorResponse(final String message) {
method getMessage (line 11) | public String getMessage() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/log/DiscordAppender.java
class DiscordAppender (line 17) | public class DiscordAppender extends UnsynchronizedAppenderBase<ILogging...
method append (line 34) | @Override
method getTitle (line 45) | private String getTitle(final ILoggingEvent eventObject) {
method getEmbeds (line 49) | private List<Embed> getEmbeds(final String title, final String embedsC...
method getDescription (line 61) | private String getDescription(final IThrowableProxy throwableProxy) {
method getFields (line 65) | private List<Field> getFields(final IThrowableProxy throwableProxy) {
method getUsername (line 76) | public String getUsername() {
method setUsername (line 80) | public void setUsername(final String username) {
method getEmbedsColor (line 84) | public String getEmbedsColor() {
method setEmbedsColor (line 88) | public void setEmbedsColor(final String embedsColor) {
method getStackTraceMaxSize (line 92) | public int getStackTraceMaxSize() {
method setStackTraceMaxSize (line 96) | public void setStackTraceMaxSize(final int stackTraceMaxSize) {
method getWebhookUri (line 100) | public String getWebhookUri() {
method setWebhookUri (line 104) | public void setWebhookUri(final String webhookUri) {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/log/dto/DiscordWebhookRequest.java
class DiscordWebhookRequest (line 5) | public class DiscordWebhookRequest {
method DiscordWebhookRequest (line 10) | private DiscordWebhookRequest() {
method DiscordWebhookRequest (line 13) | public DiscordWebhookRequest(final String username, final List<Embed> ...
method getUsername (line 18) | public String getUsername() {
method getEmbeds (line 22) | public List<Embed> getEmbeds() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Embed.java
class Embed (line 5) | public class Embed {
method Embed (line 12) | private Embed() {
method Embed (line 15) | public Embed(final String title, final String color) {
method Embed (line 19) | public Embed(final String title, final String description, final Strin...
method getTitle (line 26) | public String getTitle() {
method getDescription (line 30) | public String getDescription() {
method getColor (line 34) | public String getColor() {
method getFields (line 38) | public List<Field> getFields() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Field.java
class Field (line 3) | public class Field {
method Field (line 8) | private Field() {
method Field (line 11) | private Field(final String name, final String value) {
method from (line 16) | public static Field from(final String steAsString) {
method getName (line 22) | public String getName() {
method getValue (line 26) | public String getValue() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java
class GoogleExternalCalendarClient (line 25) | @Component
method GoogleExternalCalendarClient (line 34) | public GoogleExternalCalendarClient(final RestTemplateBuilder restTemp...
method getExternalCalendars (line 38) | @Override
method fetchGoogleCalendarList (line 49) | private ResponseEntity<GoogleCalendarListResponse> fetchGoogleCalendar...
method getExternalCalendarSchedules (line 60) | @Override
method generateCalendarRequestHeaders (line 78) | private HttpHeaders generateCalendarRequestHeaders(final String access...
method generateEventsVariables (line 85) | private Map<String, String> generateEventsVariables(final String exter...
method fetchGoogleCalendarEvents (line 94) | private ResponseEntity<GoogleCalendarEventsResponse> fetchGoogleCalend...
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleOAuthClient.java
class GoogleOAuthClient (line 25) | @Component
method GoogleOAuthClient (line 34) | public GoogleOAuthClient(final GoogleProperties properties, final Rest...
method getOAuthMember (line 41) | @Override
method requestGoogleToken (line 51) | private GoogleTokenResponse requestGoogleToken(final String code, fina...
method generateTokenParams (line 60) | private MultiValueMap<String, String> generateTokenParams(final String...
method fetchGoogleToken (line 70) | private ResponseEntity<GoogleTokenResponse> fetchGoogleToken(
method getPayload (line 79) | private String getPayload(final String jwt) {
method parseUserInfo (line 83) | private UserInfo parseUserInfo(final String payload) {
method decodeJwtPayload (line 92) | private String decodeJwtPayload(final String payload) {
method getAccessToken (line 96) | @Override
method generateAccessTokenParams (line 106) | private MultiValueMap<String, String> generateAccessTokenParams(final ...
method fetchGoogleAccessToken (line 115) | private ResponseEntity<OAuthAccessTokenResponse> fetchGoogleAccessToken(
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java
class GoogleCalendarEventResponse (line 10) | public class GoogleCalendarEventResponse {
method GoogleCalendarEventResponse (line 18) | private GoogleCalendarEventResponse() {
method GoogleCalendarEventResponse (line 21) | public GoogleCalendarEventResponse(final String id, final String summa...
method toIntegrationSchedule (line 31) | public IntegrationSchedule toIntegrationSchedule(final Long internalCa...
method getStartDateTime (line 36) | private LocalDateTime getStartDateTime() {
method getEndDateTime (line 44) | private LocalDateTime getEndDateTime() {
method getId (line 52) | public String getId() {
method getSummary (line 56) | public String getSummary() {
method getDescription (line 60) | public String getDescription() {
method getStart (line 64) | public GoogleDateFormat getStart() {
method getEnd (line 68) | public GoogleDateFormat getEnd() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java
class GoogleCalendarEventsResponse (line 5) | public class GoogleCalendarEventsResponse {
method GoogleCalendarEventsResponse (line 9) | private GoogleCalendarEventsResponse() {
method GoogleCalendarEventsResponse (line 12) | public GoogleCalendarEventsResponse(final List<GoogleCalendarEventResp...
method getItems (line 16) | public List<GoogleCalendarEventResponse> getItems() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java
class GoogleCalendarListResponse (line 5) | public class GoogleCalendarListResponse {
method GoogleCalendarListResponse (line 9) | private GoogleCalendarListResponse() {
method GoogleCalendarListResponse (line 12) | public GoogleCalendarListResponse(final List<GoogleCalendarResponse> i...
method getItems (line 16) | public List<GoogleCalendarResponse> getItems() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java
class GoogleCalendarResponse (line 3) | public class GoogleCalendarResponse {
method GoogleCalendarResponse (line 9) | private GoogleCalendarResponse() {
method GoogleCalendarResponse (line 12) | public GoogleCalendarResponse(final String id, final String summary, f...
method getId (line 18) | public String getId() {
method getSummary (line 22) | public String getSummary() {
method getDescription (line 26) | public String getDescription() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java
class GoogleDateFormat (line 3) | public class GoogleDateFormat {
method GoogleDateFormat (line 8) | private GoogleDateFormat() {
method GoogleDateFormat (line 11) | public GoogleDateFormat(final String date, final String dateTime) {
method getDate (line 16) | public String getDate() {
method getDateTime (line 20) | public String getDateTime() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java
class GoogleTokenResponse (line 6) | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
method GoogleTokenResponse (line 12) | private GoogleTokenResponse() {
method GoogleTokenResponse (line 15) | public GoogleTokenResponse(final String refreshToken, final String idT...
method getRefreshToken (line 20) | public String getRefreshToken() {
method getIdToken (line 24) | public String getIdToken() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/UserInfo.java
class UserInfo (line 3) | public class UserInfo {
method UserInfo (line 9) | private UserInfo() {
method UserInfo (line 12) | public UserInfo(final String email, final String name, final String pi...
method getEmail (line 18) | public String getEmail() {
method getName (line 22) | public String getName() {
method getPicture (line 26) | public String getPicture() {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/exception/OAuthException.java
class OAuthException (line 3) | public class OAuthException extends RuntimeException {
method OAuthException (line 5) | public OAuthException() {
method OAuthException (line 9) | public OAuthException(final Exception e) {
method OAuthException (line 13) | public OAuthException(final String message, final Exception e) {
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/DevGoogleOAuthUri.java
class DevGoogleOAuthUri (line 8) | @Component
method DevGoogleOAuthUri (line 14) | public DevGoogleOAuthUri(final GoogleProperties properties) {
method generate (line 18) | @Override
FILE: backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/GoogleOAuthUri.java
class GoogleOAuthUri (line 8) | @Component
method GoogleOAuthUri (line 14) | public GoogleOAuthUri(final GoogleProperties properties) {
method generate (line 18) | @Override
FILE: backend/src/main/java/com/allog/dallog/member/application/MemberService.java
class MemberService (line 10) | @Transactional(readOnly = true)
method MemberService (line 16) | public MemberService(final MemberRepository memberRepository) {
method findById (line 20) | public MemberResponse findById(final Long id) {
method update (line 24) | @Transactional
FILE: backend/src/main/java/com/allog/dallog/member/domain/Member.java
class Member (line 17) | @Table(name = "members")
method Member (line 41) | protected Member() {
method Member (line 44) | public Member(final String email, final String displayName, final Stri...
method validateEmail (line 55) | private void validateEmail(final String email) {
method validateDisplayName (line 62) | private void validateDisplayName(final String displayName) {
method change (line 68) | public void change(final String displayName) {
method hasSameId (line 73) | public boolean hasSameId(final Long memberId) {
method getId (line 77) | public Long getId() {
method getEmail (line 81) | public String getEmail() {
method getDisplayName (line 85) | public String getDisplayName() {
method getProfileImageUrl (line 89) | public String getProfileImageUrl() {
method getSocialType (line 93) | public SocialType getSocialType() {
FILE: backend/src/main/java/com/allog/dallog/member/domain/MemberRepository.java
type MemberRepository (line 7) | public interface MemberRepository extends JpaRepository<Member, Long> {
method findByEmail (line 9) | Optional<Member> findByEmail(final String email);
method existsByEmail (line 11) | boolean existsByEmail(final String email);
method getById (line 13) | default Member getById(final Long id) {
method getByEmail (line 18) | default Member getByEmail(final String email) {
method validateExistsById (line 23) | default void validateExistsById(final Long id) {
FILE: backend/src/main/java/com/allog/dallog/member/domain/SocialType.java
type SocialType (line 3) | public enum SocialType {
FILE: backend/src/main/java/com/allog/dallog/member/dto/request/MemberUpdateRequest.java
class MemberUpdateRequest (line 5) | public class MemberUpdateRequest {
method MemberUpdateRequest (line 10) | private MemberUpdateRequest() {
method MemberUpdateRequest (line 13) | public MemberUpdateRequest(final String displayName) {
method getDisplayName (line 17) | public String getDisplayName() {
FILE: backend/src/main/java/com/allog/dallog/member/dto/response/MemberResponse.java
class MemberResponse (line 6) | public class MemberResponse {
method MemberResponse (line 14) | private MemberResponse() {
method MemberResponse (line 17) | public MemberResponse(final Long id, final String email, final String ...
method MemberResponse (line 26) | public MemberResponse(final Member member) {
method getId (line 31) | public Long getId() {
method getEmail (line 35) | public String getEmail() {
method getDisplayName (line 39) | public String getDisplayName() {
method getProfileImageUrl (line 43) | public String getProfileImageUrl() {
method getSocialType (line 47) | public SocialType getSocialType() {
FILE: backend/src/main/java/com/allog/dallog/member/exception/InvalidMemberException.java
class InvalidMemberException (line 3) | public class InvalidMemberException extends RuntimeException {
method InvalidMemberException (line 5) | public InvalidMemberException(final String message) {
method InvalidMemberException (line 9) | public InvalidMemberException() {
FILE: backend/src/main/java/com/allog/dallog/member/exception/NoSuchMemberException.java
class NoSuchMemberException (line 3) | public class NoSuchMemberException extends RuntimeException {
method NoSuchMemberException (line 5) | public NoSuchMemberException(final String message) {
method NoSuchMemberException (line 9) | public NoSuchMemberException() {
FILE: backend/src/main/java/com/allog/dallog/member/presentation/MemberController.java
class MemberController (line 16) | @RequestMapping("/api/members")
method MemberController (line 22) | public MemberController(final MemberService memberService) {
method findMe (line 26) | @GetMapping("/me")
method update (line 32) | @PatchMapping("/me")
FILE: backend/src/main/java/com/allog/dallog/schedule/application/CheckedSchedulesFinder.java
class CheckedSchedulesFinder (line 20) | @Component
method CheckedSchedulesFinder (line 29) | public CheckedSchedulesFinder(final ScheduleService scheduleService, f...
method findMyCheckedSchedules (line 36) | public IntegrationScheduleResponses findMyCheckedSchedules(final Long ...
method toAccessToken (line 50) | private String toAccessToken(final String refreshToken) {
method toExternalSchedules (line 55) | private List<IntegrationSchedule> toExternalSchedules(final DateRangeR...
method findExternalSchedules (line 69) | private List<IntegrationSchedule> findExternalSchedules(final DateRang...
FILE: backend/src/main/java/com/allog/dallog/schedule/application/ScheduleService.java
class ScheduleService (line 31) | @Transactional(readOnly = true)
method ScheduleService (line 43) | public ScheduleService(final ScheduleRepository scheduleRepository, fi...
method save (line 58) | @Transactional
method findById (line 70) | public ScheduleResponse findById(final Long id) {
method findInternalByMemberIdAndDateRange (line 75) | public MaterialToFindSchedules findInternalByMemberIdAndDateRange(fina...
method toRefreshToken (line 89) | private String toRefreshToken(final Long memberId) {
method toCategoryDetails (line 94) | private List<ExternalCategoryDetail> toCategoryDetails(final Subscript...
method findByCategoryIdAndDateRange (line 98) | public IntegrationScheduleResponses findByCategoryIdAndDateRange(final...
method toIntegrationSchedules (line 110) | private List<IntegrationSchedule> toIntegrationSchedules(final List<Ca...
method update (line 116) | @Transactional
method delete (line 129) | @Transactional
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/IntegrationSchedule.java
class IntegrationSchedule (line 8) | public class IntegrationSchedule {
method IntegrationSchedule (line 19) | public IntegrationSchedule(final Schedule schedule) {
method IntegrationSchedule (line 29) | public IntegrationSchedule(final String id, final Long categoryId, fin...
method IntegrationSchedule (line 35) | public IntegrationSchedule(final String id, final Long categoryId, fin...
method isLongTerms (line 45) | public boolean isLongTerms() {
method isAllDays (line 50) | public boolean isAllDays() {
method isFewHours (line 55) | public boolean isFewHours() {
method isSameCategory (line 59) | public boolean isSameCategory(final Category category) {
method getId (line 64) | public String getId() {
method getCategoryId (line 68) | public Long getCategoryId() {
method getTitle (line 72) | public String getTitle() {
method getStartDateTime (line 76) | public LocalDateTime getStartDateTime() {
method getEndDateTime (line 80) | public LocalDateTime getEndDateTime() {
method getPeriod (line 84) | public Period getPeriod() {
method getMemo (line 88) | public String getMemo() {
method getCategoryType (line 92) | public CategoryType getCategoryType() {
method equals (line 96) | @Override
method hashCode (line 110) | @Override
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/IntegrationScheduleComparator.java
class IntegrationScheduleComparator (line 6) | public class IntegrationScheduleComparator implements Comparator<Integra...
method compare (line 10) | @Override
method compareEndDateTime (line 22) | private int compareEndDateTime(IntegrationSchedule firstSchedule, Inte...
method compareByTitle (line 33) | private int compareByTitle(IntegrationSchedule firstSchedule, Integrat...
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/IntegrationSchedules.java
class IntegrationSchedules (line 6) | public class IntegrationSchedules {
method IntegrationSchedules (line 12) | public IntegrationSchedules() {
method add (line 16) | public void add(final IntegrationSchedule integrationSchedule) {
method getSortedValues (line 20) | public List<IntegrationSchedule> getSortedValues() {
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/Period.java
class Period (line 13) | public class Period {
method Period (line 18) | public Period(final LocalDateTime startDateTime, final LocalDateTime e...
method calculateDayDifference (line 23) | public long calculateDayDifference() {
method isMidnightToMidnight (line 29) | public boolean isMidnightToMidnight() {
method slice (line 35) | public List<Period> slice(final Period otherPeriod) {
method isNotOverlapped (line 42) | private boolean isNotOverlapped(final Period otherPeriod) {
method sliceByOtherPeriod (line 54) | private List<Period> sliceByOtherPeriod(final Period otherPeriod) {
method getStartDateTime (line 67) | public LocalDateTime getStartDateTime() {
method getEndDateTime (line 71) | public LocalDateTime getEndDateTime() {
method equals (line 75) | @Override
method hashCode (line 87) | @Override
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/Schedule.java
class Schedule (line 17) | @Table(name = "schedules")
method Schedule (line 48) | protected Schedule() {
method Schedule (line 51) | public Schedule(final Category category, final String title, final Loc...
method change (line 63) | public void change(final Category category, final String title, final ...
method validateTitleLength (line 75) | private void validateTitleLength(final String title) {
method validatePeriod (line 81) | private void validatePeriod(final LocalDateTime startDateTime, final L...
method isNotValidDateTimeRange (line 93) | private boolean isNotValidDateTimeRange(final LocalDateTime dateTime) {
method validateMemoLength (line 97) | private void validateMemoLength(final String memo) {
method getId (line 103) | public Long getId() {
method getTitle (line 107) | public String getTitle() {
method getStartDateTime (line 111) | public LocalDateTime getStartDateTime() {
method getEndDateTime (line 115) | public LocalDateTime getEndDateTime() {
method getMemo (line 119) | public String getMemo() {
method getCategory (line 123) | public Category getCategory() {
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/ScheduleRepository.java
type ScheduleRepository (line 12) | public interface ScheduleRepository extends JpaRepository<Schedule, Long> {
method deleteByCategoryIdIn (line 14) | void deleteByCategoryIdIn(final List<Long> categoryIds);
method findByCategoriesAndBetween (line 16) | @Query("SELECT s "
method getById (line 25) | default Schedule getById(final Long id) {
method getByCategoriesAndBetween (line 30) | default List<IntegrationSchedule> getByCategoriesAndBetween(final List...
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/ScheduleType.java
type ScheduleType (line 6) | public enum ScheduleType {
method ScheduleType (line 15) | ScheduleType(final String name, final Predicate<IntegrationSchedule> i...
method from (line 20) | public static ScheduleType from(final IntegrationSchedule integrationS...
method getName (line 27) | public String getName() {
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/TypedSchedules.java
class TypedSchedules (line 7) | public class TypedSchedules {
method TypedSchedules (line 11) | public TypedSchedules(final List<IntegrationSchedule> integrationSched...
method initializeValues (line 20) | private void initializeValues() {
method getSortedSchedules (line 27) | public IntegrationSchedules getSortedSchedules(final ScheduleType sche...
FILE: backend/src/main/java/com/allog/dallog/schedule/domain/scheduler/Scheduler.java
class Scheduler (line 9) | public class Scheduler {
method Scheduler (line 15) | public Scheduler(final List<IntegrationSchedule> schedules, final Loca...
method getPeriods (line 22) | public List<Period> getPeriods() {
method slicePeriod (line 34) | private void slicePeriod(final List<Period> periods, final Integration...
FILE: backend/src/main/java/com/allog/dallog/schedule/dto/MaterialToFindSchedules.java
class MaterialToFindSchedules (line 9) | public class MaterialToFindSchedules {
method MaterialToFindSchedules (line 16) | public MaterialToFindSchedules(final Subscriptions subscriptions, fina...
method getSubscriptions (line 25) | public Subscriptions getSubscriptions() {
method getSchedules (line 29) | public List<IntegrationSchedule> getSchedules() {
method getRefreshToken (line 33) | public String getRefreshToken() {
method getExternalCategoryDetails (line 37) | public List<ExternalCategoryDetail> getExternalCategoryDetails() {
FILE: backend/src/main/java/com/allog/dallog/schedule/dto/request/DateRangeRequest.java
class DateRangeRequest (line 6) | public class DateRangeRequest {
method DateRangeRequest (line 13) | public DateRangeRequest(final String startDateTime, final String endDa...
method of (line 18) | public static DateRangeRequest of(final LocalDateTime startDateTime, f...
method getStartDateTime (line 25) | public LocalDateTime getStartDateTime() {
method getEndDateTime (line 29) | public LocalDateTime getEndDateTime() {
FILE: backend/src/main/java/com/allog/dallog/schedule/dto/request/ScheduleCreateRequest.java
class ScheduleCreateRequest (line 9) | public class ScheduleCreateRequest {
method ScheduleCreateRequest (line 23) | private ScheduleCreateRequest() {
method ScheduleCreateRequest (line 26) | public ScheduleCreateRequest(final String title, final LocalDateTime s...
method toEntity (line 34) | public Schedule toEntity(final Category category) {
method getTitle (line 38) | public String getTitle() {
method getStartDateTime (line 42) | public LocalDateTime getStartDateTime() {
method getEndDateTime (line 46) | public LocalDateTime getEndDateTime() {
method getMemo (line 50) | public String getMemo() {
FILE: backend/src/main/java/com/allog/dallog/schedule/dto/request/ScheduleUpdateRequest.java
class ScheduleUpdateRequest (line 7) | public class ScheduleUpdateRequest {
method ScheduleUpdateRequest (line 24) | private ScheduleUpdateRequest() {
method ScheduleUpdateRequest (line 27) | public ScheduleUpdateRequest(final Long categoryId, final String title...
method getTitle (line 36) | public String getTitle() {
method getStartDateTime (line 40) | public LocalDateTime getStartDateTime() {
method getEndDateTime (line 44) | public LocalDateTime getEndDateTime() {
method getMemo (line 48) | public String getMemo() {
method getCategoryId (line 52) | public Long getCategoryId() {
FILE: backend/src/main/java/com/allog/dallog/schedule/dto/response/IntegrationScheduleResponse.java
class IntegrationScheduleResponse (line 7) | public class IntegrationScheduleResponse {
method IntegrationScheduleResponse (line 18) | public IntegrationScheduleResponse(final IntegrationSchedule integrati...
method IntegrationScheduleResponse (line 25) | public IntegrationScheduleResponse(final String id, final String title...
method getId (line 38) | public String getId() {
method getTitle (line 42) | public String getTitle() {
method getStartDateTime (line 46) | public LocalDateTime getStartDateTime() {
method getEndDateTime (line 50) | public LocalDateTime getEndDateTime() {
method getMemo (line 54) | public String getMemo() {
method getCategoryId (line 58) | public Long getCategoryId() {
method getColorCode (line 62) | public String getColorCode() {
method getCategoryType (line 66) | public String getCategoryType() {
FILE: backend/src/main/java/com/allog/dallog/schedule/dto/response/IntegrationScheduleResponses.java
class IntegrationScheduleResponses (line 10) | public class IntegrationScheduleResponses {
method IntegrationScheduleResponses (line 16) | public IntegrationScheduleResponses(final List<IntegrationScheduleResp...
method IntegrationScheduleResponses (line 24) | public IntegrationScheduleResponses(final Subscriptions subscriptions,...
method IntegrationScheduleResponses (line 30) | public IntegrationScheduleResponses(final Color color, final TypedSche...
method getColoredScheduleResponses (line 36) | private List<IntegrationScheduleResponse> getColoredScheduleResponses(...
method getColoredScheduleResponses (line 46) | private List<IntegrationScheduleResponse> getColoredScheduleResponses(...
method getLongTerms (line 56) | public List<IntegrationScheduleResponse> getLongTerms() {
method getAllDays (line 60) | public List<IntegrationScheduleResponse> getAllDays() {
method getFewHours (line 64) | public List<IntegrationScheduleResponse> getFewHours() {
FILE: backend/src/main/java/com/allog/dallog/schedule/dto/response/ScheduleResponse.java
class ScheduleResponse (line 6) | public class ScheduleResponse {
method ScheduleResponse (line 16) | public ScheduleResponse(final Schedule schedule) {
method ScheduleResponse (line 21) | public ScheduleResponse(final Long id, final Long categoryId, final St...
method getId (line 32) | public Long getId() {
method getCategoryId (line 36) | public Long getCategoryId() {
method getTitle (line 40) | public String getTitle() {
method getStartDateTime (line 44) | public LocalDateTime getStartDateTime() {
method getEndDateTime (line 48) | public LocalDateTime getEndDateTime() {
method getMemo (line 52) | public String getMemo() {
method getCategoryType (line 56) | public String getCategoryType() {
FILE: backend/src/main/java/com/allog/dallog/schedule/exception/InvalidScheduleException.java
class InvalidScheduleException (line 3) | public class InvalidScheduleException extends RuntimeException {
method InvalidScheduleException (line 5) | public InvalidScheduleException(final String message) {
method InvalidScheduleException (line 9) | public InvalidScheduleException() {
FILE: backend/src/main/java/com/allog/dallog/schedule/exception/NoSuchScheduleException.java
class NoSuchScheduleException (line 3) | public class NoSuchScheduleException extends RuntimeException {
method NoSuchScheduleException (line 5) | public NoSuchScheduleException(final String message) {
method NoSuchScheduleException (line 9) | public NoSuchScheduleException() {
FILE: backend/src/main/java/com/allog/dallog/schedule/presentation/ScheduleController.java
class ScheduleController (line 25) | @RequestMapping("/api")
method ScheduleController (line 32) | public ScheduleController(final ScheduleService scheduleService,
method save (line 38) | @PostMapping("/categories/{categoryId}/schedules")
method findMyCheckedSchedules (line 46) | @GetMapping("/members/me/schedules")
method findByCategoryId (line 54) | @GetMapping("/categories/{categoryId}/schedules")
method findById (line 61) | @GetMapping("/schedules/{scheduleId}")
method update (line 67) | @PatchMapping("/schedules/{scheduleId}")
method delete (line 75) | @DeleteMapping("/schedules/{scheduleId}")
FILE: backend/src/main/java/com/allog/dallog/subscription/application/ColorPicker.java
type ColorPicker (line 3) | @FunctionalInterface
method pickNumber (line 6) | int pickNumber();
FILE: backend/src/main/java/com/allog/dallog/subscription/application/RandomColorPicker.java
class RandomColorPicker (line 7) | @Component
method pickNumber (line 10) | @Override
FILE: backend/src/main/java/com/allog/dallog/subscription/application/SubscriptionService.java
class SubscriptionService (line 22) | @Transactional(readOnly = true)
method SubscriptionService (line 32) | public SubscriptionService(final SubscriptionRepository subscriptionRe...
method save (line 42) | @Transactional
method createSubscription (line 56) | private Subscription createSubscription(final Member member, final Cat...
method createCategoryRole (line 61) | private void createCategoryRole(final Member member, final Category ca...
method findByMemberId (line 66) | public SubscriptionsResponse findByMemberId(final Long memberId) {
method update (line 76) | @Transactional
method delete (line 83) | @Transactional
method deleteCategoryRole (line 92) | private void deleteCategoryRole(final Long memberId, final Subscriptio...
FILE: backend/src/main/java/com/allog/dallog/subscription/domain/Color.java
type Color (line 6) | public enum Color {
method Color (line 35) | Color(final String colorCode) {
method pick (line 39) | public static Color pick(int index) {
method from (line 43) | public static Color from(final String colorCode) {
method getColorCode (line 50) | public String getColorCode() {
FILE: backend/src/main/java/com/allog/dallog/subscription/domain/Subscription.java
class Subscription (line 19) | @Table(name = "subscriptions")
method Subscription (line 43) | protected Subscription() {
method Subscription (line 46) | public Subscription(final Member member, final Category category, fina...
method change (line 53) | public void change(final Color color, final boolean checked) {
method validateDeletePossible (line 58) | public void validateDeletePossible(final Long memberId) {
method hasInternalCategory (line 64) | public boolean hasInternalCategory() {
method hasExternalCategory (line 68) | public boolean hasExternalCategory() {
method getId (line 72) | public Long getId() {
method getMember (line 76) | public Member getMember() {
method getCategory (line 80) | public Category getCategory() {
method getColor (line 84) | public Color getColor() {
method isChecked (line 88) | public boolean isChecked() {
FILE: backend/src/main/java/com/allog/dallog/subscription/domain/SubscriptionRepository.java
type SubscriptionRepository (line 10) | public interface SubscriptionRepository extends JpaRepository<Subscripti...
method existsByMemberIdAndCategoryId (line 12) | boolean existsByMemberIdAndCategoryId(final Long memberId, final Long ...
method existsByIdAndMemberId (line 14) | boolean existsByIdAndMemberId(final Long id, final Long memberId);
method findByMemberId (line 16) | @EntityGraph(attributePaths = {"category", "category.member"})
method findByCategoryId (line 19) | @EntityGraph(attributePaths = {"category", "category.member"})
method deleteByCategoryIdIn (line 22) | void deleteByCategoryIdIn(final List<Long> id);
method getById (line 24) | default Subscription getById(final Long id) {
method validateNotExistsByMemberIdAndCategoryId (line 29) | default void validateNotExistsByMemberIdAndCategoryId(final Long membe...
method validateExistsByIdAndMemberId (line 35) | default void validateExistsByIdAndMemberId(final Long id, final Long m...
FILE: backend/src/main/java/com/allog/dallog/subscription/domain/Subscriptions.java
class Subscriptions (line 9) | public class Subscriptions {
method Subscriptions (line 13) | public Subscriptions(final List<Subscription> subscriptions) {
method findInternalCategory (line 17) | public List<Category> findInternalCategory() {
method findExternalCategory (line 25) | public List<Category> findExternalCategory() {
method findColor (line 33) | public Color findColor(final IntegrationSchedule schedule) {
FILE: backend/src/main/java/com/allog/dallog/subscription/dto/request/SubscriptionUpdateRequest.java
class SubscriptionUpdateRequest (line 7) | public class SubscriptionUpdateRequest {
method SubscriptionUpdateRequest (line 13) | private SubscriptionUpdateRequest() {
method SubscriptionUpdateRequest (line 16) | public SubscriptionUpdateRequest(final Color color, final boolean chec...
method SubscriptionUpdateRequest (line 20) | public SubscriptionUpdateRequest(final String colorCode, final boolean...
method getColorCode (line 25) | public String getColorCode() {
method getColor (line 29) | @JsonIgnore
method isChecked (line 34) | public boolean isChecked() {
FILE: backend/src/main/java/com/allog/dallog/subscription/dto/response/SubscriptionResponse.java
class SubscriptionResponse (line 7) | public class SubscriptionResponse {
method SubscriptionResponse (line 15) | private SubscriptionResponse() {
method SubscriptionResponse (line 18) | public SubscriptionResponse(final Subscription subscription) {
method SubscriptionResponse (line 23) | public SubscriptionResponse(final Long id, final CategoryResponse cate...
method getId (line 31) | public Long getId() {
method getCategory (line 35) | public CategoryResponse getCategory() {
method getColorCode (line 39) | public String getColorCode() {
method isChecked (line 43) | public boolean isChecked() {
FILE: backend/src/main/java/com/allog/dallog/subscription/dto/response/SubscriptionsResponse.java
class SubscriptionsResponse (line 5) | public class SubscriptionsResponse {
method SubscriptionsResponse (line 9) | private SubscriptionsResponse() {
method SubscriptionsResponse (line 12) | public SubscriptionsResponse(final List<SubscriptionResponse> subscrip...
method getSubscriptions (line 16) | public List<SubscriptionResponse> getSubscriptions() {
FILE: backend/src/main/java/com/allog/dallog/subscription/exception/ExistSubscriptionException.java
class ExistSubscriptionException (line 3) | public class ExistSubscriptionException extends RuntimeException {
method ExistSubscriptionException (line 5) | public ExistSubscriptionException(final String message) {
method ExistSubscriptionException (line 9) | public ExistSubscriptionException() {
FILE: backend/src/main/java/com/allog/dallog/subscription/exception/InvalidSubscriptionException.java
class InvalidSubscriptionException (line 3) | public class InvalidSubscriptionException extends RuntimeException {
method InvalidSubscriptionException (line 5) | public InvalidSubscriptionException(final String message) {
method InvalidSubscriptionException (line 9) | public InvalidSubscriptionException() {
FILE: backend/src/main/java/com/allog/dallog/subscription/exception/NoSuchSubscriptionException.java
class NoSuchSubscriptionException (line 3) | public class NoSuchSubscriptionException extends RuntimeException {
method NoSuchSubscriptionException (line 5) | public NoSuchSubscriptionException(final String message) {
method NoSuchSubscriptionException (line 9) | public NoSuchSubscriptionException() {
FILE: backend/src/main/java/com/allog/dallog/subscription/exception/NotAbleToUnsubscribeException.java
class NotAbleToUnsubscribeException (line 3) | public class NotAbleToUnsubscribeException extends RuntimeException {
method NotAbleToUnsubscribeException (line 5) | public NotAbleToUnsubscribeException(final String message) {
method NotAbleToUnsubscribeException (line 9) | public NotAbleToUnsubscribeException() {
FILE: backend/src/main/java/com/allog/dallog/subscription/presentation/SubscriptionController.java
class SubscriptionController (line 21) | @RequestMapping("/api/members/me")
method SubscriptionController (line 27) | public SubscriptionController(final SubscriptionService subscriptionSe...
method save (line 31) | @PostMapping("/categories/{categoryId}/subscriptions")
method findByMemberId (line 40) | @GetMapping("/subscriptions")
method update (line 47) | @PatchMapping("/subscriptions/{subscriptionId}")
method delete (line 55) | @DeleteMapping("/subscriptions/{subscriptionId}")
FILE: backend/src/main/resources/db/prod/schema.sql
type members (line 1) | CREATE TABLE IF NOT EXISTS members (
type categories (line 12) | CREATE TABLE IF NOT EXISTS categories (
type subscriptions (line 23) | CREATE TABLE IF NOT EXISTS subscriptions (
type schedules (line 36) | CREATE TABLE IF NOT EXISTS schedules (
type oauth_tokens (line 49) | CREATE TABLE IF NOT EXISTS oauth_tokens (
type external_category_details (line 59) | CREATE TABLE IF NOT EXISTS external_category_details (
type category_roles (line 69) | CREATE TABLE IF NOT EXISTS category_roles (
FILE: backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java
class AcceptanceTest (line 13) | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PO...
method setUp (line 26) | @BeforeEach
FILE: backend/src/test/java/com/allog/dallog/acceptance/AuthAcceptanceTest.java
class AuthAcceptanceTest (line 26) | @Import(TokenConfig.class)
method 구글_OAuth_인증_URI를_생성하여_반환한다 (line 30) | @DisplayName("구글 OAuth 인증 URI를 생성하여 반환한다.")
method 최초_회원이거나_기존에_존재하는_회원이_다시_로그인하는_경우_토큰들을_발급하고_상태코드_200을_반환한다 (line 44) | @DisplayName("최초 회원이거나 기존에 존재하는 회원이 다시 로그인하는 경우 토큰들을 발급하고 상태코드 200을 반환...
method 만료된_엑세스_토큰으로_웹페이지를_로드하면_상태코드_401을_반환한다 (line 59) | @DisplayName("만료된 엑세스_토큰으로 웹페이지를 로드하면 상태코드 401을 반환한다.")
method 리프레시_토큰을_통해_새로운_엑세스_토큰을_발급하고_200을_반환한다 (line 72) | @DisplayName("리프레시 토큰을 통해 새로운 엑세스 토큰을 발급하고 200을 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/acceptance/CategoryAcceptanceTest.java
class CategoryAcceptanceTest (line 38) | @DisplayName("카테고리 관련 기능")
method 정상적인_카테고리_정보를_등록하면_상태코드_201을_반환한다 (line 41) | @DisplayName("정상적인 카테고리 정보를 등록하면 상태코드 201을 반환한다.")
method 개인_카테고리를_생성하면_201을_반환한다 (line 54) | @DisplayName("개인 카테고리를 생성하면 201을 반환한다.")
method 카테고리를_등록하고_일반_카테고리_전체를_조회한다 (line 67) | @DisplayName("카테고리를 등록하고 일반 카테고리 전체를 조회한다.")
method 카테고리를_등록하고_제목_검색을_통해_해당하는_카테고리를_조회한다 (line 89) | @DisplayName("카테고리를 등록하고 제목 검색을 통해 해당하는 카테고리를 조회한다.")
method 등록된_개인_카테고리는_카테고리_목록에서_조회할_수_없다 (line 111) | @DisplayName("등록된 개인 카테고리는 카테고리 목록에서 조회할 수 없다.")
method 카테고리를_등록하고_내가_등록한_카테고리를_수정하면_상태코드_204를_반환한다 (line 134) | @DisplayName("카테고리를 등록하고 내가 등록한 카테고리를 수정하면 상태코드 204를 반환한다.")
method 카테고리를_등록하고_내가_등록한_카테고리를_삭제하면_상태코드_204를_반환한다 (line 154) | @DisplayName("카테고리를 등록하고 내가 등록한 카테고리를 삭제하면 상태코드 204를 반환한다.")
method 특정_구독자의_카테고리_역할을_수정하면_상태코드_204를_반환한다 (line 168) | @DisplayName("특정 구독자의 카테고리 역할을 수정하면 상태코드 204를 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/acceptance/ExternalCalendarAcceptanceTest.java
class ExternalCalendarAcceptanceTest (line 23) | @DisplayName("외부 캘린더 관련 기능")
method 자신의_외부_캘린더_리스트를_조회하면_200을_반환한다 (line 26) | @DisplayName("자신의 외부 캘린더 리스트를 조회하면 200을 반환한다.")
method 외부_캘린더를_추가하면_201을_반환한다 (line 49) | @DisplayName("외부 캘린더를 추가하면 201을 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/acceptance/MemberAcceptanceTest.java
class MemberAcceptanceTest (line 24) | @DisplayName("회원 관련 기능")
method 등록된_회원이_자신의_정보를_조회하면_상태코드_200_을_반환한다 (line 27) | @DisplayName("등록된 회원이 자신의 정보를 조회하면 상태코드 200을 반환한다.")
method 등록된_회원이_자신의_이름을_변경하면_상태코드_204를_반환한다 (line 46) | @DisplayName("등록된 회원이 자신의 이름을 변경하면 상태코드 204를 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java
class ScheduleAcceptanceTest (line 43) | @DisplayName("일정 관련 기능")
method 정상적인_일정정보를_등록하면_상태코드_201을_반환한다 (line 46) | @DisplayName("정상적인 일정정보를 등록하면 상태코드 201을 반환한다.")
method 카테고리로_일정을_조회하면_상태코드_200을_반환한다 (line 60) | @DisplayName("카테고리로 일정을 조회하면 상태코드 200을 반환한다.")
method 일정_ID로_일정을_단건조회_하면_상태코드_200을_반환한다 (line 93) | @DisplayName("일정 ID로 일정을 단건조회_하면 상태코드 200을 반환한다.")
method 일정을_수정하면_상태코드_204를_반환한다 (line 108) | @DisplayName("일정을 수정하면 상태코드 204를 반환한다.")
method 일정을_삭제하면_상태코드_204를_반환한다 (line 126) | @DisplayName("일정을 삭제하면 상태코드 204를 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/acceptance/SubscriptionAcceptanceTest.java
class SubscriptionAcceptanceTest (line 32) | @DisplayName("구독 관련 기능")
method 인증된_회원이_카테고리를_구독하면_201을_반환한다 (line 35) | @DisplayName("인증된 회원이 카테고리를 구독하면 201을 반환한다.")
method 인증된_회원이_구독_목록을_조회하면_200을_반환한다 (line 56) | @DisplayName("인증된 회원이 구독 목록을 조회하면 200을 반환한다.")
method 인증된_회원이_자신의_구독_정보를_수정할_경우_204를_반환한다 (line 82) | @DisplayName("인증된 회원이 자신의 구독 정보를 수정할 경우 204를 반환한다.")
method 구독을_취소할_경우_204를_반환한다 (line 120) | @DisplayName("구독을 취소할 경우 204를 반환한다.")
method 새로운_카테고리를_등록한다 (line 148) | private CategoryResponse 새로운_카테고리를_등록한다(final String accessToken, fina...
FILE: backend/src/test/java/com/allog/dallog/acceptance/fixtures/AuthAcceptanceFixtures.java
class AuthAcceptanceFixtures (line 12) | public class AuthAcceptanceFixtures {
method OAuth_인증_URI를_생성한다 (line 14) | public static ExtractableResponse<Response> OAuth_인증_URI를_생성한다(final S...
method 자체_토큰을_생성한다 (line 24) | public static ExtractableResponse<Response> 자체_토큰을_생성한다(final String o...
method 자체_토큰을_생성하고_엑세스_토큰을_반환한다 (line 34) | public static String 자체_토큰을_생성하고_엑세스_토큰을_반환한다(final String oauthProvid...
method 리프레시_토큰을_통해_새로운_엑세스_토큰을_생성한다 (line 47) | public static ExtractableResponse<Response> 리프레시_토큰을_통해_새로운_엑세스_토큰을_생성한다(
method 토큰이_유효한지_검증한다 (line 57) | public static ExtractableResponse<Response> 토큰이_유효한지_검증한다(final String...
FILE: backend/src/test/java/com/allog/dallog/acceptance/fixtures/CategoryAcceptanceFixtures.java
class CategoryAcceptanceFixtures (line 11) | public class CategoryAcceptanceFixtures {
method 새로운_카테고리를_등록한다 (line 13) | public static ExtractableResponse<Response> 새로운_카테고리를_등록한다(final Strin...
method 전체_카테고리를_조회한다 (line 24) | public static ExtractableResponse<Response> 전체_카테고리를_조회한다() {
method 전체_카테고리를_제목_검색을_통해_조회한다 (line 31) | public static ExtractableResponse<Response> 전체_카테고리를_제목_검색을_통해_조회한다(fi...
method id를_통해_카테고리를_조회한다 (line 38) | public static ExtractableResponse<Response> id를_통해_카테고리를_조회한다(final Lo...
method 내가_등록한_카테고리를_수정한다 (line 45) | public static ExtractableResponse<Response> 내가_등록한_카테고리를_수정한다(final St...
method 내가_등록한_카테고리를_삭제한다 (line 57) | public static ExtractableResponse<Response> 내가_등록한_카테고리를_삭제한다(final St...
method 회원의_카테고리_역할을_변경한다 (line 65) | public static ExtractableResponse<Response> 회원의_카테고리_역할을_변경한다(final St...
FILE: backend/src/test/java/com/allog/dallog/acceptance/fixtures/CommonAcceptanceFixtures.java
class CommonAcceptanceFixtures (line 9) | public class CommonAcceptanceFixtures {
method 상태코드_200이_반환된다 (line 11) | public static void 상태코드_200이_반환된다(final ExtractableResponse<Response> ...
method 상태코드_201이_반환된다 (line 15) | public static void 상태코드_201이_반환된다(final ExtractableResponse<Response> ...
method 상태코드_204가_반환된다 (line 19) | public static void 상태코드_204가_반환된다(final ExtractableResponse<Response> ...
method 상태코드_404가_반환된다 (line 23) | public static void 상태코드_404가_반환된다(final ExtractableResponse<Response> ...
method 상태코드_401이_반환된다 (line 27) | public static void 상태코드_401이_반환된다(final ExtractableResponse<Response> ...
FILE: backend/src/test/java/com/allog/dallog/acceptance/fixtures/MemberAcceptanceFixtures.java
class MemberAcceptanceFixtures (line 9) | public class MemberAcceptanceFixtures {
method 자신의_정보를_조회한다 (line 11) | public static ExtractableResponse<Response> 자신의_정보를_조회한다(final String ...
FILE: backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java
class ScheduleAcceptanceFixtures (line 15) | public class ScheduleAcceptanceFixtures {
method 새로운_일정을_등록한다 (line 17) | public static ExtractableResponse<Response> 새로운_일정을_등록한다(final String ...
method 새로운_일정을_등록한다 (line 34) | public static ExtractableResponse<Response> 새로운_일정을_등록한다(final String ...
method 일정을_수정한다 (line 45) | public static ExtractableResponse<Response> 일정을_수정한다(final String acce...
method 일정을_삭제한다 (line 56) | public static ExtractableResponse<Response> 일정을_삭제한다(final String acce...
method 일정_아이디로_일정을_단건_조회한다 (line 64) | public static ExtractableResponse<Response> 일정_아이디로_일정을_단건_조회한다(final ...
method 카테고리_아이디로_일정_리스트를_조회한다 (line 73) | public static ExtractableResponse<Response> 카테고리_아이디로_일정_리스트를_조회한다(fin...
FILE: backend/src/test/java/com/allog/dallog/acceptance/fixtures/SubscriptionAcceptanceFixtures.java
class SubscriptionAcceptanceFixtures (line 10) | public class SubscriptionAcceptanceFixtures {
method 구독_목록을_조회한다 (line 12) | public static ExtractableResponse<Response> 구독_목록을_조회한다(final String a...
method 카테고리를_구독한다 (line 22) | public static SubscriptionResponse 카테고리를_구독한다(final String accessToken...
FILE: backend/src/test/java/com/allog/dallog/auth/application/AuthServiceTest.java
class AuthServiceTest (line 24) | @RecordApplicationEvents
method 토큰_생성을_하면_OAuth_서버에서_인증_후_토큰들을_반환한다 (line 36) | @DisplayName("토큰 생성을 하면 OAuth 서버에서 인증 후 토큰을 반환한다")
method Authorization_Code를_받으면_회원이_데이터베이스에_저장된다 (line 50) | @DisplayName("Authorization Code를 받으면 회원이 데이터베이스에 저장된다.")
method 이미_가입된_회원에_대한_Authorization_Code를_전달받으면_추가로_회원이_생성되지_않는다 (line 66) | @DisplayName("이미 가입된 회원에 대한 Authorization Code를 전달받으면 추가로 회원이 생성되지 않는다")
method 이미_가입된_회원이고_저장된_RefreshToken이_있으면_저장된_RefreshToken을_반환한다 (line 82) | @DisplayName("이미 가입된 회원이고 저장된 RefreshToken이 있으면, 저장된 RefreshToken을 반환한...
method 리프레시_토큰으로_새로운_엑세스_토큰을_발급한다 (line 97) | @DisplayName("리프레시 토큰으로 새로운 엑세스 토큰을 발급한다.")
method 리프레시_토큰으로_새로운_엑세스_토큰을_발급_할_때_리프레시_토큰이_존재하지_않으면_예외를_던진다 (line 111) | @DisplayName("리프레시 토큰으로 새로운 엑세스 토큰을 발급 할 때, 리프레시 토큰이 존재하지 않으면 예외를 던진다.")
FILE: backend/src/test/java/com/allog/dallog/auth/application/AuthTokenCreatorTest.java
class AuthTokenCreatorTest (line 11) | class AuthTokenCreatorTest extends ServiceTest {
method 엑세스_토큰과_리프레시_토큰을_발급한다 (line 16) | @DisplayName("엑세스 토큰과 리프레시 토큰을 발급한다.")
method 리프레시_토큰으로_엑세스_토큰을_발급한다 (line 30) | @DisplayName("리프레시 토큰으로 엑세스 토큰을 발급한다.")
method 토큰에서_페이로드를_추출한다 (line 45) | @DisplayName("토큰에서 페이로드를 추출한다.")
FILE: backend/src/test/java/com/allog/dallog/auth/application/JwtTokenProviderTest.java
class JwtTokenProviderTest (line 10) | class JwtTokenProviderTest {
method 엑세스_토큰을_생성한다 (line 20) | @DisplayName("엑세스 토큰을 생성한다.")
method 리프레시_토큰을_생성한다 (line 30) | @DisplayName("리프레시 토큰을 생성한다.")
method 토큰의_Payload를_가져온다 (line 40) | @DisplayName("토큰의 Payload를 가져온다.")
method 엑세스_토큰을_검증하여_만료된_경우_예외를_던진다 (line 53) | @DisplayName("엑세스 토큰을 검증하여 만료된 경우 예외를 던진다.")
method 리프레시_토큰을_검증하여_만료된_경우_예외를_던진다 (line 65) | @DisplayName("리프레시 토큰을 검증하여 만료된 경우 예외를 던진다.")
method 토큰을_검증하여_유효하지_않으면_예외를_던진다 (line 77) | @DisplayName("토큰을 검증하여 유효하지 않으면 예외를 던진다.")
FILE: backend/src/test/java/com/allog/dallog/auth/application/StubTokenProvider.java
class StubTokenProvider (line 14) | public class StubTokenProvider implements TokenProvider {
method StubTokenProvider (line 20) | public StubTokenProvider(final String secretKey) {
method createAccessToken (line 24) | @Override
method createRefreshToken (line 29) | @Override
method createToken (line 34) | private String createToken(final String payload, final Long validityIn...
method getPayload (line 46) | @Override
method validateToken (line 56) | @Override
FILE: backend/src/test/java/com/allog/dallog/auth/domain/AuthTokenTest.java
class AuthTokenTest (line 9) | class AuthTokenTest {
method 같은_리프레시_토큰_값이면_정상적으로_메서드를_종료한다 (line 11) | @DisplayName("같은 리프레시 토큰 값이면 정상적으로 메서드를 종료한다.")
method 같은_리프레시_토큰_값이_아니면_예외를_발생한다 (line 21) | @DisplayName("같은 리프레시 토큰 값이 아니면 예외를 발생한다.")
FILE: backend/src/test/java/com/allog/dallog/auth/domain/InMemoryAuthTokenRepositoryTest.java
class InMemoryAuthTokenRepositoryTest (line 11) | class InMemoryAuthTokenRepositoryTest {
method setUp (line 15) | @BeforeEach
method 토큰을_저장한다 (line 20) | @DisplayName("토큰을 저장한다.")
method MemberId에_해당하는_토큰이_있으면_true를_반환한다 (line 34) | @DisplayName("MemberId에 해당하는 토큰이 있으면 true를 반환한다.")
method MemberId에_해당하는_토큰이_없으면_false를_반환한다 (line 49) | @DisplayName("MemberId에 해당하는 토큰이 없으면 false를 반환한다.")
method MemberId에_해당하는_토큰을_가져온다 (line 63) | @DisplayName("MemberId에 해당하는 토큰을 가져온다.")
method MemberId에_해당하는_토큰이_없으면_예외를_발생한다 (line 78) | @DisplayName("MemberId에 해당하는 토큰이 없으면 예외를 발생한다.")
FILE: backend/src/test/java/com/allog/dallog/auth/domain/OAuthTokenRepositoryTest.java
class OAuthTokenRepositoryTest (line 15) | class OAuthTokenRepositoryTest extends RepositoryTest {
method member_id의_OAuthToken이_존재할_경우_true를_반환한다 (line 23) | @DisplayName("member id의 OAuthToken이 존재할 경우 true를 반환한다.")
method member_id의_OAuthToken이_존재하지_않을_경우_false를_반환한다 (line 37) | @DisplayName("member id의 OAuthToken이 존재하지 않을 경우 false를 반환한다.")
method member_id의_OAuthToken이_존재할_경우_Optional은_비어있지_않다 (line 47) | @DisplayName("member id의 OAuthToken이 존재할 경우 Optional은 비어있지 않다.")
method member_id의_OAuthToken이_존재하지_않을_경우_비어있다 (line 61) | @DisplayName("member id의 OAuthToken이 존재하지 않을 경우 비어있다.")
FILE: backend/src/test/java/com/allog/dallog/auth/domain/OAuthTokenTest.java
class OAuthTokenTest (line 11) | class OAuthTokenTest {
method OAuth_token을_생성한다 (line 13) | @DisplayName("OAuth token을 생성한다.")
method refresh_token을_교체한다 (line 24) | @DisplayName("refresh token을 교체한다.")
FILE: backend/src/test/java/com/allog/dallog/auth/presentation/AuthControllerTest.java
class AuthControllerTest (line 35) | class AuthControllerTest extends ControllerTest {
method OAuth_소셜_로그인을_위한_링크와_상태코드_200을_반환한다 (line 37) | @DisplayName("OAuth 소셜 로그인을 위한 링크와 상태코드 200을 반환한다.")
method OAuth_로그인을_하면_token과_상태코드_200을_반환한다 (line 63) | @DisplayName("OAuth 로그인을 하면 token과 상태코드 200을 반환한다.")
method OAuth_로그인_과정에서_Resource_Server_에러가_발생하면_상태코드_500을_반환한다 (line 94) | @DisplayName("OAuth 로그인 과정에서 Resource Server 에러가 발생하면 상태코드 500을 반환한다.")
method 리프레시_토큰을_통해_새로운_엑세스_토큰을_발급하면_상태코드_200을_반환한다 (line 121) | @DisplayName("리프레시 토큰을 통해 새로운 엑세스 토큰을 발급하면 상태코드 200을 반환한다.")
method 존재하지_않는_리프레시_토큰으로_새로운_엑세스_토큰을_발급하려_하면_상태코드_401을_반환한다 (line 148) | @DisplayName("잘못된 리프레시 토큰으로 새로운 엑세스 토큰을 발급하려 하면 상태코드 401을 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/category/application/CategoryServiceTest.java
class CategoryServiceTest (line 53) | class CategoryServiceTest extends ServiceTest {
method 카테고리를_생성한다 (line 78) | @Test
method 개인_카테고리를_생성한다 (line 90) | @Test
method 카테고리를_생성할_때_자동으로_구독한다 (line 107) | @Test
method 카테고리를_생성할_때_권한을_최고_관리자로_생성한다 (line 120) | @Transactional
method 카테고리를_생성할_때_이름이_공백이거나_길이가_20을_초과하면_예외가_발생한다 (line 134) | @ParameterizedTest
method 외부_카테고리를_생성한다 (line 147) | @Test
method 중복되는_외부_카테고리를_생성하면_예외가_발생한다 (line 163) | @Test
method 외부_카테고리를_생성할_때_자동으로_구독한다 (line 175) | @Test
method 저장된_회원의_개인_카테고리를_생성하고_자동으로_구독하고_카테고리_역할을_부여한다 (line 188) | @Test
method 제목에_검색어가_포함된_카테고리를_가져온다 (line 216) | @Test
method 제목에_검색어가_포함된_카테고리를_가져올때_개인_카테고리는_제외한다 (line 230) | @Test
method 관리권한이_최고_관리자인_카테고리_목록을_조회한다 (line 244) | @Transactional
method id로_카테고리_단건_조회한다 (line 263) | @Test
method id로_카테고리_단건_조회할_때_없으면_예외가_발생한다 (line 278) | @Test
method 권한이_최고_관리자인_카테고리를_수정한다 (line 288) | @Test
method 권한이_최고_관리자가_아닌_카테고리를_수정하면_예외가_발생한다 (line 303) | @Test
method 카테고리를_수정할_때_카테고리가_없으면_예외가_발생한다 (line 317) | @Test
method 권한이_최고_관리자인_카테고리를_삭제한다 (line 329) | @Test
method 권한이_최고_관리자가_아닌_카테고리를_삭제하려_하면_예외가_발생한다 (line 342) | @Test
method 카테고리를_생성할_때_최고_관리자인_카테고리가_50개면_예외가_발생한다 (line 354) | @Test
method 없는_카테고리를_삭제하려_하면_예외가_발생한다 (line 367) | @Test
method 카테고리를_삭제할_때_생성한_일정도_모두_삭제한다 (line 377) | @Test
method 카테고리를_삭제할_때_구독_정보도_모두_삭제한다 (line 393) | @Test
method 카테고리를_삭제할_때_카테고리_권한도_모두_삭제한다 (line 408) | @Transactional
method 개인_카테고리를_삭제하려_하면_예외가_발생한다 (line 424) | @Test
method 외부_서비스_연동_카테고리를_삭제한다 (line 434) | @Test
FILE: backend/src/test/java/com/allog/dallog/category/application/ExternalCategoryDetailServiceTest.java
class ExternalCategoryDetailServiceTest (line 18) | class ExternalCategoryDetailServiceTest extends ServiceTest {
method 월별_일정을_조회하면_회원의_외부_카테고리_전체를_조회한다 (line 35) | @Test
FILE: backend/src/test/java/com/allog/dallog/category/domain/CategoryRepositoryTest.java
class CategoryRepositoryTest (line 33) | class CategoryRepositoryTest extends RepositoryTest {
method 카테고리_제목과_타입을_통해_해당하는_카테고리를_조회한다 (line 44) | @DisplayName("카테고리 제목과 타입을 통해 해당하는 카테고리를 조회한다.")
method 카테고리_이름_검색_결과가_존재하지_않는_경우_아무것도_조회_하지_않는다 (line 73) | @DisplayName("카테고리 이름 검색 결과가 존재하지 않는 경우 아무것도 조회 하지 않는다.")
method 구독자수가_많은_순서로_정렬하여_반환한다 (line 94) | @DisplayName("구독자수가 많은 순서로 정렬하여 반환한다.")
method member_id와_categoryType을_기반으로_조회한다 (line 129) | @DisplayName("member id와 categoryType을 기반으로 조회한다.")
method 특정_회원이_생성한_카테고리를_조회한다 (line 147) | @DisplayName("특정 회원이 생성한 카테고리를 조회한다.")
FILE: backend/src/test/java/com/allog/dallog/category/domain/CategoryTest.java
class CategoryTest (line 18) | class CategoryTest {
method 카테고리를_생성한다 (line 20) | @DisplayName("카테고리를 생성한다.")
method 카테고리_이름이_공백인_경우_예외를_던진다 (line 30) | @DisplayName("카테고리 이름이 공백인 경우 예외를 던진다.")
method 카테고리_이름의_길이가_20을_초과하는_경우_예외를_던진다 (line 41) | @DisplayName("카테고리 이름의 길이가 20을 초과하는 경우 예외를 던진다.")
method 개인_카테고리의_이름을_수정하는_경우_예외를_던진다 (line 51) | @DisplayName("개인 카테고리의 이름을 수정하는 경우 예외를 던진다.")
method 제공된_회원의_ID와_카테고리를_생성한_회원의_ID가_일치하지_않으면_false를_반환한다 (line 62) | @DisplayName("제공된 회원의 ID와 카테고리를 생성한 회원의 ID가 일치하지 않으면 false를 반환한다.")
method 개인_카테고리면_true를_반환한다 (line 75) | @DisplayName("개인 카테고리면 true를 반환한다.")
method 외부_연동_카테고리면_true를_반환한다 (line 88) | @DisplayName("외부 연동 카테고리면 true를 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/category/domain/CategoryTypeTest.java
class CategoryTypeTest (line 13) | public class CategoryTypeTest {
method 카테고리_종류를_가져온다 (line 15) | @DisplayName("카테고리 종류를 가져온다.")
method 존재하지_않는_카테고리_종류인_경우_예외를_던진다 (line 26) | @DisplayName("존재하지 않는 카테고리 종류인 경우 예외를 던진다.")
FILE: backend/src/test/java/com/allog/dallog/category/domain/ExternalCategoryDetailRepositoryTest.java
class ExternalCategoryDetailRepositoryTest (line 19) | public class ExternalCategoryDetailRepositoryTest extends RepositoryTest {
method 존재하지_않는_외부_카테고리_세부정보를_가져오는_경우_예외를_던진다 (line 30) | @DisplayName("존재하지 않는 외부 카테고리 세부정보를 가져오는 경우 예외를 던진다.")
method 새로운_외부_카테고리_세부정보인_경우_예외를_던지지_않는다 (line 48) | @DisplayName("새로운 외부 카테고리 세부정보인 경우 예외를 던지지 않는다.")
method 이미_존재하는_외부_카테고리_세부정보인_경우_예외를_던진다 (line 65) | @DisplayName("이미 존재하는 외부 카테고리 세부정보인 경우 예외를 던진다.")
FILE: backend/src/test/java/com/allog/dallog/category/presentation/CategoryControllerTest.java
class CategoryControllerTest (line 66) | class CategoryControllerTest extends ControllerTest {
method 카테고리를_생성한다 (line 73) | @DisplayName("카테고리를 생성한다.")
method 잘못된_이름_형식으로_카테고리를_생성하면_400_Bad_Request가_발생한다 (line 113) | @DisplayName("잘못된 이름 형식으로 카테고리를 생성하면 400 Bad Request가 발생한다.")
method 생성된_카테고리를_전부_조회한다 (line 141) | @DisplayName("생성된 카테고리를 전부 조회한다.")
method 카테고리_제목을_활용하여_조회한다 (line 163) | @DisplayName("카테고리 제목을 활용하여 조회한다.")
method 내가_일정을_편집할_수_있는_카테고리를_전부_조회한다 (line 188) | @DisplayName("내가 일정을 편집할 수 있는 카테고리를 전부 조회한다.")
method 내가_ADMIN으로_있는_카테고리를_전부_조회한다 (line 211) | @DisplayName("내가 ADMIN으로 있는 카테고리를 전부 조회한다.")
method 카테고리_ID로_카테고리를_단건_조회한다 (line 234) | @DisplayName("카테고리 ID로 카테고리를 단건 조회한다.")
method 카테고리_ID로_카테고리를_단건_조회시_존재하지_않으면_404_Not_Found를_반환한다 (line 259) | @DisplayName("카테고리 ID로 카테고리를 단건 조회시 존재하지 않으면 404 Not Found가 발생한다.")
method 카테고리를_수정한다 (line 284) | @DisplayName("카테고리를 수정한다.")
method 카테고리_수정_시_존재하지_않으면_404_Not_Found를_반환한다 (line 313) | @DisplayName("카테고리 수정 시 존재하지 않으면 404 Not Found가 발생한다.")
method 잘못된_이름_형식으로_카테고리를_수정하면_400_Bad_Request가_발생한다 (line 343) | @DisplayName("잘못된 이름 형식으로 카테고리를 수정하면 400 Bad Request가 발생한다.")
method 카테고리를_제거한다 (line 373) | @DisplayName("카테고리를 제거한다.")
method 카테고리_제거_시_존재하지_않으면_404_Not_Found가_발생한다 (line 400) | @DisplayName("카테고리 제거 시 존재하지 않으면 404 Not Found가 발생한다")
method ADMIN은_다른_구독자의_카테고리_역할을_변경할_수_있다 (line 428) | @DisplayName("ADMIN은 다른 구독자의 카테고리 역할을 변경할 수 있다.")
method ADMIN이_아닌_회원은_다른_구독자의_카테고리_역할을_변경하면_403_Forbidden이_발생한다 (line 463) | @DisplayName("ADMIN이 아닌 회원은 다른 구독자의 카테고리 역할을 변경하면 403 Forbidden이 발생한다.")
method 카테고리_역할이_변경될_회원이_해당_카테고리를_구독하지_않은_상황이라면_404_NotFound가_발생한다 (line 497) | @DisplayName("카테고리 역할이 변경될 회원이 해당 카테고리를 구독하지 않은 상황이라면 404 NotFound가 발생...
method 자기_자신이_유일한_ADMIN이라면_자신의_역할을_변경할_수_없다 (line 531) | @DisplayName("자기 자신이 유일한 ADMIN이라면 자신의 역할을 변경할 수 없다.")
method 특정_카테고리의_구독자_목록을_조회한다 (line 565) | @DisplayName("특정 카테고리의 구독자 목록을 조회한다.")
method 특정_카테고리의_구독자_목록을_ADMIN이_아닌_회원이_조회하는_경우_403에러가_발생한다 (line 600) | @DisplayName("특정 카테고리의 구독자 목록을 ADMIN이 아닌 회원이 조회하는 경우 403에러가 발생한다.")
FILE: backend/src/test/java/com/allog/dallog/categoryrole/application/CategoryRoleServiceTest.java
class CategoryRoleServiceTest (line 36) | class CategoryRoleServiceTest extends ServiceTest {
method 카테고리의_구독자_목록을_조회한다 (line 56) | @Test
method 관리자_권한이_아닌_회원이_카테고리_구독자_목록을_조회하면_예외가_발생한다 (line 70) | @Test
method 관리자_역할로_변경하려는_회원이_이미_50개_이상의_카테고리에_권한이_있으면_예외가_발생한다 (line 82) | @Test
method 카테고리_권한이_관리자인_회원이_다른_관리자의_권한을_변경한다 (line 99) | @Transactional
method 카테고리_권한이_관리자인_회원이_구독자의_권한을_변경한다 (line 117) | @Transactional
method 카테고리_권한이_관리자인_회원이_자신의_권한을_변경한다 (line 133) | @Transactional
method 카테고리_권한이_없는_회원이_다른_회원의_권한을_변경하려하면_예외가_발생한다 (line 151) | @Test
method 유일한_카테고리_관리자인_회원이_자신의_권한을_변경하려_하면_예외가_발생한다 (line 165) | @Test
method 개인_카테고리에_대한_회원의_권한을_변경하려_하면_예외가_발생한다 (line 176) | @Test
method 외부_카테고리에_대한_회원의_권한을_변경하려_하면_예외가_발생한다 (line 187) | @Test
FILE: backend/src/test/java/com/allog/dallog/categoryrole/domain/CategoryRoleRepositoryTest.java
class CategoryRoleRepositoryTest (line 17) | class CategoryRoleRepositoryTest extends RepositoryTest {
method member_id와_category_id를_기반으로_조회한다 (line 28) | @DisplayName("member id와 category id를 기반으로 조회한다.")
method category_id를_기반으로_조회한다 (line 45) | @DisplayName("category id를 기반으로 조회한다.")
method 특정_카테고리에_admin이_혼자인지_확인한다 (line 60) | @DisplayName("특정 카테고리에 admin이 혼자인지 확인한다.")
FILE: backend/src/test/java/com/allog/dallog/categoryrole/domain/CategoryRoleTest.java
class CategoryRoleTest (line 14) | class CategoryRoleTest {
method 역할이_특정_권한을_가지고_있는지_확인한다 (line 16) | @DisplayName("역할이 특정 권한을 가지고 있는지 확인한다.")
FILE: backend/src/test/java/com/allog/dallog/categoryrole/domain/CategoryRoleTypeTest.java
class CategoryRoleTypeTest (line 13) | class CategoryRoleTypeTest {
method 역할_유형이_권한을_가지고_있는지_확인한다 (line 15) | @DisplayName("역할 유형이 권한을 가지고 있는지 확인한다.")
method 주어진_권한_목록을_가지고_있는_역할_유형_목록을_가져온다 (line 27) | @DisplayName("주어진 권한 목록을 모두 가지고 있는 역할 유형 목록을 가져온다.")
FILE: backend/src/test/java/com/allog/dallog/common/Constants.java
class Constants (line 5) | public class Constants {
FILE: backend/src/test/java/com/allog/dallog/common/DatabaseCleaner.java
class DatabaseCleaner (line 11) | @Component
method DatabaseCleaner (line 17) | public DatabaseCleaner(final EntityManager entityManager) {
method execute (line 28) | @Transactional
FILE: backend/src/test/java/com/allog/dallog/common/annotation/ControllerTest.java
class ControllerTest (line 28) | @AutoConfigureRestDocs
FILE: backend/src/test/java/com/allog/dallog/common/annotation/RepositoryTest.java
class RepositoryTest (line 8) | @DataJpaTest
FILE: backend/src/test/java/com/allog/dallog/common/annotation/ServiceTest.java
class ServiceTest (line 23) | @SpringBootTest(classes = ExternalApiConfig.class)
method setUp (line 39) | @BeforeEach
method toMemberId (line 45) | protected Long toMemberId(final OAuthMember oAuthMember) {
method 나인 (line 50) | protected GivenBuilder 나인() {
method 티거 (line 56) | protected GivenBuilder 티거() {
FILE: backend/src/test/java/com/allog/dallog/common/builder/BuilderSupporter.java
class BuilderSupporter (line 12) | @Component
method BuilderSupporter (line 23) | public BuilderSupporter(final MemberRepository memberRepository,
method memberRepository (line 39) | public MemberRepository memberRepository() {
method oAuthTokenRepository (line 43) | public OAuthTokenRepository oAuthTokenRepository() {
method categoryRepository (line 47) | public CategoryRepository categoryRepository() {
method externalCategoryDetailRepository (line 51) | public ExternalCategoryDetailRepository externalCategoryDetailReposito...
method categoryRoleRepository (line 55) | public CategoryRoleRepository categoryRoleRepository() {
method subscriptionRepository (line 59) | public SubscriptionRepository subscriptionRepository() {
method scheduleRepository (line 63) | public ScheduleRepository scheduleRepository() {
FILE: backend/src/test/java/com/allog/dallog/common/builder/GivenBuilder.java
class GivenBuilder (line 19) | public final class GivenBuilder {
method GivenBuilder (line 28) | public GivenBuilder(final BuilderSupporter bs) {
method 회원_가입을_한다 (line 32) | public GivenBuilder 회원_가입을_한다(final String email, final String name,
method 카테고리를_생성한다 (line 41) | public GivenBuilder 카테고리를_생성한다(final String categoryName,
method 카테고리를_구독한다 (line 52) | public GivenBuilder 카테고리를_구독한다(final Category category) {
method 카테고리_관리_권한을_부여한다 (line 60) | public GivenBuilder 카테고리_관리_권한을_부여한다(final Member otherMember, final C...
method 카테고리_관리_권한을_해제한다 (line 69) | public GivenBuilder 카테고리_관리_권한을_해제한다(final Member otherMember, final C...
method 외부_카테고리를_등록한다 (line 78) | public GivenBuilder 외부_카테고리를_등록한다(final String categoryName, final Cat...
method 일정을_생성한다 (line 88) | public GivenBuilder 일정을_생성한다(final String title, final LocalDateTime s...
method 회원 (line 95) | public Member 회원() {
method 카테고리 (line 99) | public Category 카테고리() {
method 구독 (line 103) | public Subscription 구독() {
method 카테고리_일정 (line 107) | public Schedule 카테고리_일정() {
FILE: backend/src/test/java/com/allog/dallog/common/config/ExternalApiConfig.java
class ExternalApiConfig (line 12) | @TestConfiguration
method oAuthClient (line 15) | @Bean
method externalCalendarClient (line 20) | @Bean
method oAuthUri (line 25) | @Bean
FILE: backend/src/test/java/com/allog/dallog/common/config/TokenConfig.java
class TokenConfig (line 10) | @TestConfiguration
method tokenProvider (line 13) | @Bean
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/AuthFixtures.java
class AuthFixtures (line 14) | public class AuthFixtures {
method 관리자_인증_코드_토큰_요청 (line 42) | public static TokenRequest 관리자_인증_코드_토큰_요청() {
method 파랑_인증_코드_토큰_요청 (line 46) | public static TokenRequest 파랑_인증_코드_토큰_요청() {
method 리버_인증_코드_토큰_요청 (line 50) | public static TokenRequest 리버_인증_코드_토큰_요청() {
method 후디_인증_코드_토큰_요청 (line 54) | public static TokenRequest 후디_인증_코드_토큰_요청() {
method 매트_인증_코드_토큰_요청 (line 58) | public static TokenRequest 매트_인증_코드_토큰_요청() {
method MEMBER_인증_코드_토큰_요청 (line 62) | public static TokenRequest MEMBER_인증_코드_토큰_요청() {
method MEMBER_인증_코드_토큰_응답 (line 66) | public static AccessAndRefreshTokenResponse MEMBER_인증_코드_토큰_응답() {
method MEMBER_리뉴얼_토큰_요청 (line 70) | public static TokenRenewalRequest MEMBER_리뉴얼_토큰_요청() {
method MEMBER_리뉴얼_토큰_응답 (line 74) | public static AccessTokenResponse MEMBER_리뉴얼_토큰_응답() {
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/CategoryFixtures.java
class CategoryFixtures (line 16) | public class CategoryFixtures {
method 공통_일정 (line 48) | public static Category 공통_일정(final Member creator) {
method BE_일정 (line 52) | public static Category BE_일정(final Member creator) {
method FE_일정 (line 56) | public static Category FE_일정(final Member creator) {
method 매트_아고라 (line 60) | public static Category 매트_아고라(final Member creator) {
method 후디_JPA_스터디 (line 64) | public static Category 후디_JPA_스터디(final Member creator) {
method 내_일정 (line 68) | public static Category 내_일정(final Member creator) {
method 우아한테크코스_일정 (line 72) | public static Category 우아한테크코스_일정(final Member creator) {
method 공통_일정_응답 (line 76) | public static CategoryResponse 공통_일정_응답(final MemberResponse creatorRe...
method BE_일정_응답 (line 80) | public static CategoryResponse BE_일정_응답(final MemberResponse creatorRe...
method FE_일정_응답 (line 84) | public static CategoryResponse FE_일정_응답(final MemberResponse creatorRe...
method 매트_아고라_응답 (line 88) | public static CategoryResponse 매트_아고라_응답(final MemberResponse creatorR...
method 후디_JPA_스터디_응답 (line 92) | public static CategoryResponse 후디_JPA_스터디_응답(final MemberResponse crea...
method BE_일정_세부_응답 (line 96) | public static CategoryDetailResponse BE_일정_세부_응답(final MemberResponse ...
method setId (line 101) | public static Category setId(final Category category, final Long id) {
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCalendarFixtures.java
class ExternalCalendarFixtures (line 5) | public class ExternalCalendarFixtures {
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCategoryFixtures.java
class ExternalCategoryFixtures (line 5) | public class ExternalCategoryFixtures {
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/IntegrationScheduleFixtures.java
class IntegrationScheduleFixtures (line 10) | public class IntegrationScheduleFixtures {
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/MemberFixtures.java
class MemberFixtures (line 8) | public class MemberFixtures {
method 관리자 (line 40) | public static Member 관리자() {
method 파랑 (line 44) | public static Member 파랑() {
method 리버 (line 48) | public static Member 리버() {
method 후디 (line 52) | public static Member 후디() {
method 매트 (line 56) | public static Member 매트() {
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/OAuthFixtures.java
type OAuthFixtures (line 7) | public enum OAuthFixtures {
method OAuthFixtures (line 20) | OAuthFixtures(final String code, final OAuthMember oAuthMember) {
method parseOAuthMember (line 25) | public static OAuthMember parseOAuthMember(final String code) {
method 관리자 (line 33) | private static OAuthMember 관리자() {
method 파랑 (line 41) | private static OAuthMember 파랑() {
method 리버 (line 49) | private static OAuthMember 리버() {
method 후디 (line 57) | private static OAuthMember 후디() {
method 매트 (line 65) | private static OAuthMember 매트() {
method MEMBER (line 73) | private static OAuthMember MEMBER() {
method CREATOR (line 81) | private static OAuthMember CREATOR() {
method getCode (line 89) | public String getCode() {
method getOAuthMember (line 93) | public OAuthMember getOAuthMember() {
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/OAuthTokenFixtures.java
class OAuthTokenFixtures (line 6) | public class OAuthTokenFixtures {
method OAUTH_TOKEN (line 10) | public static OAuthToken OAUTH_TOKEN(final Member member) {
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/ScheduleFixtures.java
class ScheduleFixtures (line 9) | public class ScheduleFixtures {
method 알록달록_회의 (line 93) | public static Schedule 알록달록_회의(final Category category) {
method 알록달록_회식 (line 97) | public static Schedule 알록달록_회식(final Category category) {
method 레벨_인터뷰 (line 101) | public static Schedule 레벨_인터뷰(final Category category) {
FILE: backend/src/test/java/com/allog/dallog/common/fixtures/SubscriptionFixtures.java
class SubscriptionFixtures (line 10) | public class SubscriptionFixtures {
method 색상1_구독 (line 12) | public static Subscription 색상1_구독(final Member member, final Category ...
method 색상1_구독_응답 (line 16) | public static SubscriptionResponse 색상1_구독_응답(final CategoryResponse ca...
method 색상2_구독 (line 20) | public static Subscription 색상2_구독(final Member member, final Category ...
method 색상2_구독_응답 (line 24) | public static SubscriptionResponse 색상2_구독_응답(final CategoryResponse ca...
method 색상3_구독 (line 28) | public static Subscription 색상3_구독(final Member member, final Category ...
method 색상3_구독_응답 (line 32) | public static SubscriptionResponse 색상3_구독_응답(final CategoryResponse ca...
FILE: backend/src/test/java/com/allog/dallog/externalcalendar/application/ExternalCalendarServiceTest.java
class ExternalCalendarServiceTest (line 16) | class ExternalCalendarServiceTest extends ServiceTest {
method 회원의_외부_캘린더_목록을_조회한다 (line 27) | @Test
FILE: backend/src/test/java/com/allog/dallog/externalcalendar/presentation/ExternalCalendarControllerTest.java
class ExternalCalendarControllerTest (line 35) | class ExternalCalendarControllerTest extends ControllerTest {
method 외부_캘린더의_일정을_조회하면_상태코드_200을_반환한다 (line 40) | @DisplayName("외부 캘린더의 일정을 조회하면 상태코드 200을 반환한다.")
method 외부_캘린더를_카테고리로_저장하면_상태코드_201을_반환한다 (line 64) | @DisplayName("외부 캘린더를 카테고리로 저장하면 상태코드 201을 반환한다.")
method 외부_캘린더를_중복하여_저장하면_상태코드_400을_반환한다 (line 91) | @DisplayName("외부 캘린더를 중복하여 저장하면 상태코드 400을 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubExternalCalendarClient.java
class StubExternalCalendarClient (line 14) | public class StubExternalCalendarClient implements ExternalCalendarClient {
method getExternalCalendars (line 16) | @Override
method getExternalCalendarSchedules (line 21) | @Override
FILE: backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubOAuthClient.java
class StubOAuthClient (line 10) | public class StubOAuthClient implements OAuthClient {
method getOAuthMember (line 12) | @Override
method getAccessToken (line 17) | @Override
FILE: backend/src/test/java/com/allog/dallog/infrastructure/oauth/uri/StubOAuthUri.java
class StubOAuthUri (line 5) | public class StubOAuthUri implements OAuthUri {
method generate (line 7) | @Override
FILE: backend/src/test/java/com/allog/dallog/member/application/MemberServiceTest.java
class MemberServiceTest (line 17) | class MemberServiceTest extends ServiceTest {
method 회원을_조회한다 (line 27) | @DisplayName("회원을 조회한다.")
method 회원의_이름을_수정한다 (line 38) | @DisplayName("회원의 이름을 수정한다.")
FILE: backend/src/test/java/com/allog/dallog/member/domain/MemberRepositoryTest.java
class MemberRepositoryTest (line 14) | class MemberRepositoryTest extends RepositoryTest {
method 중복된_이메일이_존재하는_경우_true를_반환한다 (line 19) | @DisplayName("중복된 이메일이 존재하는 경우 true를 반환한다.")
method 이메일을_통해_회원을_찾는다 (line 29) | @DisplayName("이메일을 통해 회원을 찾는다.")
method 존재하지_않는_email을_조회할_경우_예외를_던진다 (line 42) | @DisplayName("존재하지 않는 email을 조회할 경우 예외를 던진다.")
method 존재하지_않는_id이면_예외를_던진다 (line 53) | @DisplayName("존재하지 않는 id이면 예외를 던진다.")
FILE: backend/src/test/java/com/allog/dallog/member/domain/MemberTest.java
class MemberTest (line 17) | class MemberTest {
method 회원을_생성한다 (line 19) | @DisplayName("회원을 생성한다.")
method 회원의_email이_형식이_맞지_않으면_예외를_던진다 (line 26) | @DisplayName("회원의 email이 형식이 맞지 않으면 예외를 던진다.")
method 회원의_이름이_1_에서_100_사이가_아닌_경우_예외를_던진다 (line 35) | @DisplayName("회원의 이름이 1 ~ 100 사이가 아닌 경우 예외를 던진다.")
method 회원의_이름을_변경한다 (line 54) | @DisplayName("회원의 이름을 변경한다.")
method 변경하기_위한_회원의_이름이_1_에서_100_사이가_아닌_경우_예외를_던진다 (line 68) | @DisplayName("변경하기 위한 회원의 이름이 1 ~ 100 사이가 아닌 경우 예외를 던진다.")
FILE: backend/src/test/java/com/allog/dallog/member/presentation/MemberControllerTest.java
class MemberControllerTest (line 31) | class MemberControllerTest extends ControllerTest {
method 자신의_회원_정보를_조회한다 (line 36) | @DisplayName("자신의 회원 정보를 조회한다.")
method 존재하지_않는_회원의_정보를_조회하려고_하면_예외를_발생한다 (line 67) | @DisplayName("존재하지 않는 회원의 정보를 조회하려고 하면 예외를 발생한다.")
method 등록된_회원이_자신의_이름을_수정한다 (line 90) | @DisplayName("등록된 회원이 자신의 이름을 수정한다.")
FILE: backend/src/test/java/com/allog/dallog/schedule/application/CheckedSchedulesFinderTest.java
class CheckedSchedulesFinderTest (line 35) | class CheckedSchedulesFinderTest extends ServiceTest {
method 시작일시와_종료일시로_회원의_달력을_일정_유형에_따라_분류하고_정렬하여_반환한다 (line 52) | @DisplayName("시작일시와 종료일시로 회원의 달력을 일정 유형에 따라 분류하고 정렬하여 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/schedule/application/ScheduleServiceTest.java
class ScheduleServiceTest (line 49) | class ScheduleServiceTest extends ServiceTest {
method 관리_권한이_있는_회원은_카테고리에_새로운_일정을_생성할_수_있다 (line 76) | @Test
method 관리_권한이_없는_회원이_카고리에_새로운_일정을_생성하려_하면_예외가_발생한다 (line 86) | @Test
method 카테고리_생성자라도_관리_권한이_없으면_새로운_일정을_생성할_때_예외가_발생한다 (line 99) | @Transactional
method 새로운_일정을_생성_할_때_일정_제목의_길이가_50을_초과하면_예외가_발생한다 (line 116) | @Test
method 새로운_일정을_생성_할_때_일정_메모의_길이가_255를_초과하면_예외가_발생한다 (line 129) | @Test
method 새로운_일정을_생성_할_때_종료일시가_시작일시_이전이라면_예외가_발생한다 (line 142) | @Test
method 존재하지_않는_카테고리에_일정을_추가하려하면_예외가_발생한다 (line 154) | @Test
method 외부_연동_카테고리에_일정을_추가하려하면_예외가_발생한다 (line 164) | @Test
method 단건_일정을_조회한다 (line 174) | @Test
method 존재하지_않는_일정을_단건_조회하면_예외가_발생한다 (line 193) | @Test
method 월별_일정_조회를_하면_통합일정_정보를_반환한다 (line 203) | @Transactional
method 카테고리_별_통합_일정_정보를_조회한다 (line 219) | @Test
method 일정을_수정한다 (line 249) | @Test
method 관리_권한이_없는_회원이_카테고리의_일정을_수정하면_예외가_발생한다 (line 272) | @Test
method 카테고리_생성자라도_관리_권한이_없으면_카테고리의_일정을_수정할_떄_예외가_발생한다 (line 287) | @Transactional
method 존재하지_않은_일정을_수정하려하면_예외가_발생한다 (line 306) | @Test
method 일정의_카테고리를_변경한다 (line 318) | @Test
method 관리_권한이_있는_회원은_카테고리의_일정을_삭제할_수_있다 (line 336) | @Test
method 관리_권한이_없는_회원이_카테고리의_일정을_삭제하려하면_예외가_발생한다 (line 350) | @Test
method 카테고리_생성자라도_관리_권한이_없으면_일정을_삭제하려할때_예외가_발생한다 (line 363) | @Transactional
method 존재하지_않은_일정을_삭제하려하면_예외가_발생한다 (line 380) | @Test
FILE: backend/src/test/java/com/allog/dallog/schedule/domain/IntegrationScheduleTest.java
class IntegrationScheduleTest (line 15) | class IntegrationScheduleTest {
method 일정을_생성한다 (line 17) | @DisplayName("일정을 생성한다.")
method LongTerm인지_확인_할_때_AllDays가_아니고_일정의_시작일과_종료일이_다르면_true를_반환한다 (line 30) | @DisplayName("LongTerm인지 확인 할 때, AllDays가 아니고 일정의 시작일과 종료일이 다르면 true를 ...
method LongTerm인지_확인_할_때_일정의_시작일과_종료일이_같으면_false를_반환한다 (line 47) | @DisplayName("LongTerm인지 확인 할 때, 일정의 시작일과 종료일이 같으면 false를 반환한다.")
method LongTerm인지_확인_할_때_일정의_시작일과_종료일이_달라도_AllDays면_false를_반환한다 (line 64) | @DisplayName("LongTerm인지 확인 할 때, 일정의 시작일과 종료일이 달라도 AllDays면 false를 반환한...
method AllDays인지_확인_할_때_일정의_일차가_하루고_시작시간과_종료시간이_모두_자정이면_true를_반환한다 (line 81) | @DisplayName("AllDays인지 확인 할 때, 일정의 일차가 하루고 시작시간과 종료시간 모두 자정이면 true를 반...
method AllDays인지_확인_할_때_일정의_일차가_하루여도_시작시간과_종료시간이__자정이_아니면_false를_반환한다 (line 98) | @DisplayName("AllDays인지 확인 할 때, 일정의 일차가 하루여도 시작시간과 종료시간이 자정이 아니면 false...
method FewHours인지_확인_할_때_일정의_시작일과_종료일이_같으면_true를_반환한다 (line 115) | @DisplayName("FewHours인지 확인 할 때, 일정의 시작일과 종료일이 같으면 true를 반환한다.")
method FewHours인지_확인_할_때_일정의_시작일과_종료일이_다르면_false를_반환한다 (line 132) | @DisplayName("FewHours인지 확인 할 때, 일정의 시작일과 종료일이 다르면 false를 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/schedule/domain/IntegrationSchedulesTest.java
class IntegrationSchedulesTest (line 10) | class IntegrationSchedulesTest {
method 겹치는_일정이_하나도_없을_때_일정_시작일시가_빠른_순서대로_정렬된다 (line 12) | @DisplayName("겹치는 일정이 하나도 없을 때, 일정 시작일시가 빠른 순서대로 정렬된다.")
method 일정의_시작일시가_겹친다면_일정_종료일시가_느린_순서대로_정렬된다 (line 41) | @DisplayName("일정의 시작일시가 겹친다면, 일정 종료일시가 느린 순서대로 정렬된다.")
method 일정의_시작일시가_겹치고_종료일시도_겹칠때는_일정의_제목을_사전기준_오름차순으로_정렬된다 (line 70) | @DisplayName("일정의 시작일시가 겹치고, 종료일시도 겹칠때는 일정의 제목을 사전기준 오름차순으로 정렬된다.")
FILE: backend/src/test/java/com/allog/dallog/schedule/domain/PeriodTest.java
class PeriodTest (line 11) | class PeriodTest {
method 시작일시와_종료일시의_날짜_차이를_반환한다 (line 13) | @DisplayName("시작일시와 종료일시의 날짜 차이를 반환한다.")
method 시작시간과_종료시간이_모두_자정이면_true를_반환한다 (line 29) | @DisplayName("시작시간과 종료시간이 모두 자정이면 true를 반환한다.")
method 시작시간과_종료시간_중_하나라도_자정이_아니면_false를_반환한다 (line 45) | @DisplayName("시작시간과 종료시간 중 하나라도 자정이 아니면 false를 반환한다.")
method 기간_뺄셈시_상대_기간이_우측에_걸쳐있을_때의_결과를_계산한다 (line 61) | @DisplayName("기간 뺄셈시 상대 기간이 우측에 걸쳐있을 때의 결과를 계산한다.")
method 기간_뺄셈시_상대_기간이_좌측에_걸쳐있을_때의_결과를_계산한다 (line 84) | @DisplayName("기간 뺄셈시 상대 기간이 좌측에 걸쳐있을 때의 결과를 계산한다.")
method 기간_뺄셈시_상대_기간이_안쪽에_포함될때_결과를_계산한다 (line 107) | @DisplayName("기간 뺄셈시 상대 기간이 안쪽에 포함될때 결과를 계산한다.")
method 기간_뺄셈시_상대_기간과_완벽히_일치하면_빈_리스트를_반환한다 (line 132) | @DisplayName("기간 뺄셈시 상대 기간과 완벽히 일치하면 빈 리스트를 반환한다.")
method 기간_뺄셈시_상대_기간과_겹치지_않으면_자기자신을_리스트로_반환한다 (line 151) | @DisplayName("기간 뺄셈시 상대 기간과 겹치지 않으면 자기자신을 리스트로 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/schedule/domain/ScheduleRepositoryTest.java
class ScheduleRepositoryTest (line 49) | class ScheduleRepositoryTest extends RepositoryTest {
method 특정_카테고리들에_속한_일정을_전부_삭제한다 (line 60) | @DisplayName("특정 카테고리들에 속한 일정을 전부 삭제한다.")
method 카테고리와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다 (line 101) | @DisplayName("카테코리와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.")
method 조회하기_위한_category_리스트의_크기가_0인_경우_빈_리스트를_반환한다 (line 123) | @DisplayName("조회하기 위한 category 리스트의 크기가 0인 경우 빈 리스트를 반환한다.")
method 카테고리가_여러_개_일_때_카테고리와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다 (line 148) | @DisplayName("카테고리가 여러 개 일 때, 카테고리와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회...
method 카테고리가_여러_개_일_때_카테고리_리스트와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다 (line 182) | @DisplayName("카테고리가 여러 개 일 때, 카테고리 리스트와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정...
method 시작일시와_종료일시를_전달할_때_일정의_시작일시와_같으면_조회된다 (line 215) | @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 시작날짜가 종료일시와 같으면 조회한다.")
method 카테고리와_시작일시_종료일시를_전달할_때_일정의_시작날짜가_종료일시_이후이면_조회되지_않는다 (line 240) | @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 시작날짜가 종료일시 이후이면 조회되지 않는다.")
method 카테고리와_시작일시와_종료일시를_전달할_때_일정의_종료날짜가_시작일시와_같으면_조회된다 (line 265) | @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 종료날짜가 시작일시와 같으면 조회된다.")
method 카테고리와_시작일시와_종료일시를_전달할_때_일정의_종료날짜가_시작일시_이전이면_조회되지_않는다 (line 290) | @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 종료날짜가 시작일시 이전이면 조회되지 않는다.")
method 시작일시와_종료일시로_특정_카테고리의_일정을_조회한다 (line 315) | @DisplayName("시작일시와 종료일시로 특정 카테고리의 일정을 조회한다.")
method 시작일시와_종료일시로_특정_카테고리의_일정을_조회할_때_범위_밖의_일정은_제외된다 (line 353) | @DisplayName("시작일시와 종료일시로 특정 카테고리의 일정을 조회할 때 범위 밖의 일정은 제외된다.")
FILE: backend/src/test/java/com/allog/dallog/schedule/domain/ScheduleTest.java
class ScheduleTest (line 20) | public class ScheduleTest {
method 일정을_생성한다 (line 22) | @DisplayName("일정을 생성한다.")
method 일정_시작_일시가_가능한_범위를_벗어나는_경우_예외를_던진다 (line 32) | @DisplayName("일정 시작 일시가 가능한 범위를 벗어나는 경우 예외를 던진다.")
method 일정_종료_일시가_가능한_범위를_벗어나는_경우_예외를_던진다 (line 46) | @DisplayName("일정 종료 일시가 가능한 범위를 벗어나는 경우 예외를 던진다.")
method 일정_제목의_길이가_50을_초과하는_경우_예외를_던진다 (line 61) | @DisplayName("일정 제목의 길이가 50을 초과하는 경우 예외를 던진다.")
method 일정_메모의_길이가_255를_초과하는_경우_예외를_던진다 (line 74) | @DisplayName("일정 메모의 길이가 255를 초과하는 경우 예외를 던진다.")
FILE: backend/src/test/java/com/allog/dallog/schedule/domain/scheduler/SchedulerTest.java
class SchedulerTest (line 32) | class SchedulerTest {
method 겹치지_않는_기간을_계산한다 (line 34) | @DisplayName("겹치지 않는 기간을 계산한다.")
FILE: backend/src/test/java/com/allog/dallog/schedule/presentation/ScheduleControllerTest.java
class ScheduleControllerTest (line 46) | class ScheduleControllerTest extends ControllerTest {
method 일정_정보를_등록하면_상태코드_201을_반환한다 (line 51) | @DisplayName("일정 정보를 등록하면 상태코드 201을 반환한다.")
method 일정_정보를_등록할때_해당_카테고리에_권한이_없으면_403을_반환한다 (line 74) | @DisplayName("일정 정보를 등록할때 해당 카테고리에 권한이 없으면 403을 반환한다.")
method 일정_생성시_전달한_카테고리가_존재하지_않는다면_404를_반환한다 (line 97) | @DisplayName("일정 생성시 전달한 카테고리가 존재하지 않는다면 404를 반환한다.")
method 일정을_단건_조회_하면_상태코드_200을_반환한다 (line 120) | @DisplayName("일정을 단건 조회 하면 상태코드 200을 반환한다")
method 일정을_단건_조회_할_때_일정이_존재하지_않으면_상태코드_404를_반환한다 (line 140) | @DisplayName("일정을 단건 조회 할 때 일정이 존재하지 않으면 상태코드 404를 반환한다.")
method 일정을_수정하는데_성공하면_204를_반환한다 (line 161) | @DisplayName("일정을 수정하는데 성공하면 204를 반환한다.")
method 일정을_수정하는데_해당_일정의_카테고리에_대한_권한이_없다면_403을_반환한다 (line 189) | @DisplayName("일정을 수정하는데 해당 일정의 카테고리에 대한 권한이 없다면 403을 반환한다.")
method 일정을_수정하는데_일정이_존재하지_않는_경우_404를_반환한다 (line 214) | @DisplayName("일정을 수정하는데 일정이 존재하지 않는 경우 404를 반환한다")
method 일정을_제거하는데_성공하면_204를_반환한다 (line 239) | @DisplayName("일정을 제거하는데 성공하면 204를 반환한다.")
method 일정을_제거하는데_해당_일정의_카테고리에_대한_권한이_없다면_403을_반환한다 (line 262) | @DisplayName("일정을 제거하는데 해당 일정의 카테고리에 대한 권한이 없다면 403을 반환한다.")
method 일정을_제거하는데_일정이_존재하지_않는_경우_404를_반환한다 (line 282) | @DisplayName("일정을 제거하는데 일정이 존재하지 않는 경우 404를 반환한다")
method 회원의_일정_목록을_정상적으로_조회하면_200을_반환한다 (line 302) | @DisplayName("회원의 일정 목록을 정상적으로 조회하면 200을 반환한다.")
method 카테고리_별_일정_목록을_정상적으로_조회하면_200을_반환한다 (line 353) | @DisplayName("카테고리 별 일정 목록을 정상적으로 조회하면 200을 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/subscription/application/SubscriptionServiceTest.java
class SubscriptionServiceTest (line 42) | class SubscriptionServiceTest extends ServiceTest {
method 구독을_생성한다 (line 61) | @Test
method 타인의_개인_카테고리를_구독하려하면_예외가_발생한다 (line 75) | @Test
method 이미_구독한_카테고리를_다시_구독하려하면_예외가_발생한다 (line 88) | @Test
method 단건_구독_정보를_조회한다 (line 98) | @Test
method 회원의_구독_목록을_조회한다 (line 110) | @Test
method 구독_정보를_수정한다 (line 127) | @Test
method 존재하지_않는_색상으로_구독_정보를_수정하려_하면_예외가_발생한다 (line 143) | @ParameterizedTest
method 구독_정보를_삭제한다 (line 156) | @Test
method 자신의_구독_정보가_아닌_구독을_삭제할_경우_예외가_발생한다 (line 171) | @Test
method 카테고리를_구독하면_카테고리에_대한_구독자_권한이_생성된다 (line 183) | @Transactional
method 카테고리를_구독_해제하면_카테고리에_대한_권한이_제거된다 (line 199) | @Transactional
method 카테고리_권한이_관리자_일때_구독_해제를_하려하면_예외가_발생한다 (line 215) | @Transactional
FILE: backend/src/test/java/com/allog/dallog/subscription/domain/ColorTest.java
class ColorTest (line 15) | class ColorTest {
method 랜덤으로_색상을_가져온다 (line 17) | @DisplayName("랜덤으로 색상을 가져온다.")
method color_code에_맞는_색상을_가져온다 (line 31) | @DisplayName("color code에 맞는 색상을 가져온다.")
method 소문자로_들어온_color_code도_가져온다 (line 39) | @DisplayName("소문자로 들어온 color code도 가져온다.")
method 존재하지_않는_color_code인_경우_예외가_발생한다 (line 50) | @DisplayName("존재하지 않는 color code인 경우 예외가 발생한다.")
FILE: backend/src/test/java/com/allog/dallog/subscription/domain/SubscriptionRepositoryTest.java
class SubscriptionRepositoryTest (line 29) | class SubscriptionRepositoryTest extends RepositoryTest {
method 존재하지_않는_카테고리를_확인할_경우_true를_반환한다 (line 40) | @DisplayName("존재하지 않는 카테고리를 확인할 경우 true를 반환한다.")
method 이미_존재하는_카테고리를_확인할_경우_true를_반환한다 (line 56) | @DisplayName("이미 존재하는 카테고리를 확인할 경우 true를 반환한다.")
method 회원의_특정_구독_정보_여부를_확인한다 (line 73) | @DisplayName("회원의 특정 구독 정보 여부를 확인한다.")
method 회원의_존재하지_않는_구독_정보_여부를_확인한다 (line 91) | @DisplayName("회원의 존재하지 않는 구독 정보 여부를 확인한다.")
method 회원_정보를_기반으로_구독_정보를_조회한다 (line 104) | @DisplayName("회원 정보를 기반으로 구독 정보를 조회한다.")
method 회원의_구독_정보가_존재하지_않는_경우_빈_리스트가_조회된다 (line 125) | @DisplayName("회원의 구독 정보가 존재하지 않는 경우 빈 리스트가 조회된다.")
method 특정_카테고리들에_속한_구독을_전부_삭제한다 (line 138) | @DisplayName("특정 카테고리들에 속한 구독을 전부 삭제한다")
method 존재하지_않는_id인_경우_예외를_던진다 (line 170) | @DisplayName("존재하지 않는 id인 경우 예외를 던진다.")
method 특정_member가_특정_category를_구독한_경우_예외를_던진다 (line 181) | @DisplayName("특정 member가 특정 category를 구독한 경우 예외를 던진다.")
method 특정_구독_id가_특정_member의_구독이_아닌_경우_예외를_던진다 (line 199) | @DisplayName("특정 구독 id가 특정 member의 구독이 아닌 경우 예외를 던진다.")
FILE: backend/src/test/java/com/allog/dallog/subscription/domain/SubscriptionTest.java
class SubscriptionTest (line 16) | class SubscriptionTest {
method 구독을_생성한다 (line 18) | @DisplayName("구독을 생성한다.")
method 구독이_생성되면_기본적으로_체크된다 (line 30) | @DisplayName("구독이 생성되면 기본적으로 체크된다.")
method 구독의_색_정보를_수정한다 (line 45) | @DisplayName("구독의 색 정보를 수정한다.")
method 구독의_체크_유무를_수정한다 (line 60) | @DisplayName("구독의 체크 유무를 수정한다.")
FILE: backend/src/test/java/com/allog/dallog/subscription/domain/SubscriptionsTest.java
class SubscriptionsTest (line 24) | class SubscriptionsTest {
method 체크된_카테고리_중_내부_카테고리의_아이디를_찾는다 (line 26) | @DisplayName("체크된 카테고리 중 내부 카테고리의 아이디를 찾는다.")
method 체크된_카테고리_중_외부_카테고리의_아이디를_찾는다 (line 50) | @DisplayName("체크된 카테고리 중 외부 카테고리의 아이디를 찾는다.")
method 특정_스케줄의_구독_색상을_찾는다 (line 72) | @DisplayName("특정 스케줄의 구독 색상을 찾는다.")
method 구독하지_않은_스케줄의_구독_색상을_찾는_경우_예외를_던진다 (line 94) | @DisplayName("구독하지 않은 스케줄의 구독 색상을 찾는 경우 예외를 던진다")
method 구독한_카테고리중_내부_카테고리를_찾아_반환한다 (line 110) | @DisplayName("구독한 카테고리중 내부 카테고리를 찾아 반환한다.")
method 구독한_카테고리중_외부_카테고리를_찾아_반환한다 (line 129) | @DisplayName("구독한 카테고리중 외부 카테고리를 찾아 반환한다.")
FILE: backend/src/test/java/com/allog/dallog/subscription/presentation/SubscriptionControllerTest.java
class SubscriptionControllerTest (line 46) | class SubscriptionControllerTest extends ControllerTest {
method 회원과_카테고리_정보를_기반으로_구독한다 (line 51) | @DisplayName("회원과 카테고리 정보를 기반으로 구독한다.")
method 회원이_이미_카테고리를_구독한_경우_예외를_던진다 (line 78) | @DisplayName("회원이 이미 카테고리를 구독한 경우 예외를 던진다.")
method 타인의_개인_카테고리_구독_요청시_403_Forbidden을_반환한다 (line 103) | @DisplayName("타인의 개인 카테고리 구독 요청시 403 Forbidden을 반환한다.")
method 자신의_구독_정보를_조회한다 (line 128) | @DisplayName("자신의 구독 정보를 조회한다.")
method 자신의_구독_정보를_수정한다 (line 160) | @DisplayName("자신의 구독 정보를 수정한다.")
method 구독_id를_기반으로_자신의_구독_정보를_삭제한다 (line 194) | @DisplayName("구독 id를 기반으로 자신의 구독 정보를 삭제한다.")
method 구독_제거시_자신이_가지고_있지_않은_구독_정보인_경우_예외를_던진다 (line 222) | @DisplayName("구독 제거시 자신이 가지고 있지 않은 구독 정보인 경우 예외를 던진다.")
FILE: frontend/src/@types/calendar.ts
type CalendarType (line 1) | interface CalendarType {
FILE: frontend/src/@types/category.ts
type CategoryRoleType (line 6) | type CategoryRoleType = ValueOf<typeof ROLE>;
type CategoryType (line 8) | interface CategoryType {
type CategorySubscriberType (line 16) | interface CategorySubscriberType {
type SingleCategoryType (line 21) | interface SingleCategoryType extends CategoryType {
FILE: frontend/src/@types/emotion.d.ts
type ColorsType (line 3) | interface ColorsType {
type FlexType (line 7) | type FlexType = Record<'row' | 'column', SerializedStyles>;
type MQType (line 9) | interface MQType {
type Theme (line 16) | interface Theme {
FILE: frontend/src/@types/googleCalendar.ts
type GoogleCalendarGetResponseType (line 1) | interface GoogleCalendarGetResponseType {
type GoogleCalendarPostBodyType (line 8) | interface GoogleCalendarPostBodyType {
FILE: frontend/src/@types/index.ts
type FieldsetCssPropType (line 3) | interface FieldsetCssPropType {
type SelectCssPropType (line 9) | interface SelectCssPropType {
type InputRefType (line 15) | interface InputRefType {
type ModalPosType (line 19) | interface ModalPosType {
FILE: frontend/src/@types/profile.ts
type ProfileType (line 1) | interface ProfileType {
type ProfileGetResponseType (line 9) | interface ProfileGetResponseType {
FILE: frontend/src/@types/schedule.ts
type ScheduleResponseKeyType (line 6) | type ScheduleResponseKeyType = ValueOf<typeof SCHEDULE.RESPONSE_TYPE>;
type ScheduleResponseType (line 8) | type ScheduleResponseType = Record<ScheduleResponseKeyType, Array<Schedu...
type ScheduleType (line 10) | interface ScheduleType {
FILE: frontend/src/@types/subscription.ts
type SubscriptionType (line 3) | interface SubscriptionType {
FILE: frontend/src/@types/util.ts
type ValueOf (line 1) | type ValueOf<T> = T[keyof T];
FILE: frontend/src/App.tsx
function App (line 23) | function App() {
FILE: frontend/src/components/@common/Button/Button.tsx
type ButtonProps (line 5) | interface ButtonProps {
function Button (line 13) | function Button({
FILE: frontend/src/components/@common/ErrorBoundary/ErrorBoundary.tsx
type Props (line 5) | interface Props {
type State (line 9) | interface State {
class ErrorBoundary (line 13) | class ErrorBoundary extends Component<Props, State> {
method getDerivedStateFromError (line 18) | public static getDerivedStateFromError(): State {
method render (line 22) | public render() {
FILE: frontend/src/components/@common/Fieldset/Fieldset.tsx
type FieldsetProps (line 7) | interface FieldsetProps extends React.HTMLAttributes<HTMLInputElement> {
function Fieldset (line 23) | function Fieldset({
FILE: frontend/src/components/@common/ModalPortal/ModalPortal.tsx
type ModalPortalProps (line 8) | interface ModalPortalProps {
function ModalPortal (line 15) | function ModalPortal({ isOpen, closeModal, children, dimmerBackground }:...
FILE: frontend/src/components/@common/PageLayout/PageLayout.tsx
type PageLayoutProps (line 12) | interface PageLayoutProps {
function PageLayout (line 17) | function PageLayout({ children, type = PAGE_LAYOUT.DEFAULT }: PageLayout...
FILE: frontend/src/components/@common/Responsive/Responsive.tsx
type ResponsiveProps (line 5) | interface ResponsiveProps {
function Responsive (line 10) | function Responsive({ type = 'all', children }: ResponsiveProps) {
FILE: frontend/src/components/@common/Select/Select.tsx
type OptionsType (line 21) | type OptionsType = { id: number | string; name: number | string };
type SelectProps (line 23) | interface SelectProps {
function Select (line 33) | function Select({ options, value, onChange, cssProp, description = '옵션 선...
FILE: frontend/src/components/@common/Skeleton/Skeleton.tsx
type SkeletonProps (line 5) | interface SkeletonProps {
function Skeleton (line 11) | function Skeleton({ cssProp, width = '100%', height = '100%' }: Skeleton...
FILE: frontend/src/components/@common/Spinner/Spinner.tsx
type SpinnerProps (line 5) | interface SpinnerProps {
function Spinner (line 9) | function Spinner({ size = 5 }: SpinnerProps) {
FILE: frontend/src/components/AdminCategoryManageModal/AdminCategoryManageModal.tsx
type AdminCategoryManageModalProps (line 45) | interface AdminCategoryManageModalProps {
function AdminCategoryManageModal (line 50) | function AdminCategoryManageModal({ subscription, closeModal }: AdminCat...
FILE: frontend/src/components/AdminItem/AdminItem.tsx
type AdminItemProps (line 23) | interface AdminItemProps {
function AdminItem (line 28) | function AdminItem({ categoryId, admin }: AdminItemProps) {
FILE: frontend/src/components/Calendar/Calendar.fallback.tsx
type CalendarFallbackProps (line 29) | interface CalendarFallbackProps {
function CalendarFallback (line 38) | function CalendarFallback({
FILE: frontend/src/components/Calendar/Calendar.tsx
type CalendarProps (line 28) | interface CalendarProps {
function Calendar (line 37) | function Calendar({
FILE: frontend/src/components/CategoryAddModal/CategoryAddModal.tsx
type CategoryAddModalProps (line 21) | interface CategoryAddModalProps {
function CategoryAddModal (line 25) | function CategoryAddModal({ closeModal }: CategoryAddModalProps) {
FILE: frontend/src/components/CategoryControl/CategoryControl.tsx
type CategoryControlProps (line 31) | interface CategoryControlProps {
function CategoryControl (line 35) | function CategoryControl({ setCategory }: CategoryControlProps) {
FILE: frontend/src/components/CategoryList/CategoryList.fallback.tsx
function CategoryListFallback (line 9) | function CategoryListFallback() {
FILE: frontend/src/components/CategoryList/CategoryList.tsx
type CategoryListProps (line 13) | interface CategoryListProps {
function CategoryList (line 18) | function CategoryList({ keyword, setCategory }: CategoryListProps) {
FILE: frontend/src/components/CategoryModifyModal/CategoryModifyModal.tsx
type CategoryModifyModalProps (line 21) | interface CategoryModifyModalProps {
function CategoryModifyModal (line 26) | function CategoryModifyModal({ category, closeModal }: CategoryModifyMod...
FILE: frontend/src/components/DateCell/DateCell.tsx
type DateCellProps (line 24) | interface DateCellProps {
function DateCell (line 44) | function DateCell({
FILE: frontend/src/components/Footer/Footer.tsx
function Footer (line 9) | function Footer() {
FILE: frontend/src/components/GoogleCategoryManageModal/GoogleCategoryManageModal.tsx
type GoogleCategoryManageModalProps (line 26) | interface GoogleCategoryManageModalProps {
function GoogleCategoryManageModal (line 31) | function GoogleCategoryManageModal({ subscription, closeModal }: GoogleC...
FILE: frontend/src/components/GoogleImportModal/GoogleImportModal.tsx
type GoogleImportModal (line 30) | interface GoogleImportModal {
function GoogleImportModal (line 34) | function GoogleImportModal({ closeModal }: GoogleImportModal) {
FILE: frontend/src/components/MoreScheduleModal/MoreScheduleModal.tsx
type MoreScheduleModalProps (line 36) | interface MoreScheduleModalProps {
function MoreScheduleModal (line 45) | function MoreScheduleModal({
FILE: frontend/src/components/NavBar/NavBar.tsx
function NavBar (line 25) | function NavBar() {
FILE: frontend/src/components/Profile/Profile.fallback.tsx
function ProfileFallback (line 5) | function ProfileFallback() {
FILE: frontend/src/components/Profile/Profile.tsx
function Profile (line 35) | function Profile() {
FILE: frontend/src/components/ProtectRoute/ProtectRoute.tsx
function ProtectRoute (line 14) | function ProtectRoute() {
FILE: frontend/src/components/Schedule/Schedule.tsx
type ScheduleProps (line 21) | interface ScheduleProps {
function Schedule (line 31) | function Schedule({
FILE: frontend/src/components/ScheduleAddButton/ScheduleAddButton.tsx
type ScheduleAddButtonProps (line 9) | interface ScheduleAddButtonProps {
function ScheduleAddButton (line 13) | function ScheduleAddButton({ onClick }: ScheduleAddButtonProps) {
FILE: frontend/src/components/ScheduleAddModal/ScheduleAddModal.tsx
type ScheduleAddModalProps (line 37) | interface ScheduleAddModalProps {
function ScheduleAddModal (line 42) | function ScheduleAddModal({ dateInfo, closeModal }: ScheduleAddModalProp...
FILE: frontend/src/components/ScheduleModal/ScheduleModal.tsx
type ScheduleModalProps (line 35) | interface ScheduleModalProps {
function ScheduleModal (line 43) | function ScheduleModal({
FILE: frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.tsx
type ScheduleModifyModalProps (line 43) | interface ScheduleModifyModalProps {
function ScheduleModifyModal (line 48) | function ScheduleModifyModal({ scheduleInfo, closeModal }: ScheduleModif...
FILE: frontend/src/components/SideAdminList/SideAdminList.tsx
type SideAdminListProps (line 27) | interface SideAdminListProps {
function SideAdminList (line 31) | function SideAdminList({ categories }: SideAdminListProps) {
FILE: frontend/src/components/SideBar/SideBar.fallback.tsx
function SideBarFallback (line 10) | function SideBarFallback() {
FILE: frontend/src/components/SideBar/SideBar.tsx
function SideBar (line 17) | function SideBar() {
FILE: frontend/src/components/SideBarButton/SideBarButton.tsx
function SideBarButton (line 13) | function SideBarButton() {
FILE: frontend/src/components/SideGoogleList/SideGoogleList.tsx
type SideGoogleListProps (line 27) | interface SideGoogleListProps {
function SideGoogleList (line 31) | function SideGoogleList({ categories }: SideGoogleListProps) {
FILE: frontend/src/components/SideItem/SideItem.tsx
type SideItemProps (line 24) | interface SideItemProps {
function SideItem (line 28) | function SideItem({ subscription }: SideItemProps) {
FILE: frontend/src/components/SideSubscribedList/SideSubscribedList.tsx
type SideSubscribedListProps (line 28) | interface SideSubscribedListProps {
function SideSubscribedList (line 32) | function SideSubscribedList({ categories }: SideSubscribedListProps) {
FILE: frontend/src/components/SnackBar/SnackBar.tsx
function SnackBar (line 9) | function SnackBar() {
FILE: frontend/src/components/SubscribedCategoryItem/SubscribedCategoryItem.tsx
type SubscribedCategoryItemProps (line 22) | interface SubscribedCategoryItemProps {
function SubscribedCategoryItem (line 28) | function SubscribedCategoryItem({
FILE: frontend/src/components/SubscriberItem/SubscriberItem.tsx
type SubscriberItemProps (line 19) | interface SubscriberItemProps {
function SubscriberItem (line 24) | function SubscriberItem({ categoryId, subscriber }: SubscriberItemProps) {
FILE: frontend/src/components/SubscriptionModifyModal/SubscriptionModifyModal.tsx
type SubscriptionModifyModalProps (line 32) | interface SubscriptionModifyModalProps {
function SubscriptionModifyModal (line 44) | function SubscriptionModifyModal({
FILE: frontend/src/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem.tsx
type UnsubscribedCategoryItemProps (line 23) | interface UnsubscribedCategoryItemProps {
function UnsubscribedCategoryItem (line 28) | function UnsubscribedCategoryItem({ category, onClick }: UnsubscribedCat...
FILE: frontend/src/components/WithdrawalModal/WithdrawalModal.tsx
type WithdrawalModalProps (line 19) | interface WithdrawalModalProps {
function WithdrawalModal (line 23) | function WithdrawalModal({ closeModal }: WithdrawalModalProps) {
FILE: frontend/src/constants/api.ts
constant API (line 1) | const API = {
constant API_URL (line 6) | const API_URL = process.env.API_URL;
constant CACHE_KEY (line 8) | const CACHE_KEY = {
constant RESPONSE (line 25) | const RESPONSE = {
FILE: frontend/src/constants/category.ts
constant CATEGORY_TYPE (line 1) | const CATEGORY_TYPE = {
constant ROLE (line 7) | const ROLE = {
FILE: frontend/src/constants/date.ts
constant DATE_TIME (line 3) | const DATE_TIME = {
constant DAYS (line 9) | const DAYS = ['일', '월', '화', '수', '목', '금', '토'];
constant TIMES (line 11) | const TIMES = new Array(48)
FILE: frontend/src/constants/index.ts
constant ATOM_KEY (line 1) | const ATOM_KEY = {
constant CALENDAR (line 8) | const CALENDAR = {
constant EVENT (line 13) | const EVENT = {
constant SELECTOR_KEY (line 17) | const SELECTOR_KEY = {
constant STORAGE_KEY (line 21) | const STORAGE_KEY = {
constant PATH (line 26) | const PATH = {
FILE: frontend/src/constants/message.ts
constant CONFIRM_MESSAGE (line 1) | const CONFIRM_MESSAGE = {
constant ERROR_MESSAGE (line 11) | const ERROR_MESSAGE = {
constant SUCCESS_MESSAGE (line 15) | const SUCCESS_MESSAGE = {
constant TOOLTIP_MESSAGE (line 30) | const TOOLTIP_MESSAGE = {
FILE: frontend/src/constants/schedule.ts
constant SCHEDULE (line 1) | const SCHEDULE = {
FILE: frontend/src/constants/style.ts
constant OPTION_HEIGHT (line 1) | const OPTION_HEIGHT = 36;
constant PAGE_LAYOUT (line 3) | const PAGE_LAYOUT = {
constant PALETTE (line 8) | const PALETTE = [
constant RESPONSIVE (line 35) | const RESPONSIVE = {
constant SCHEDULE (line 53) | const SCHEDULE = {
constant TRANSPARENT (line 58) | const TRANSPARENT = 'transparent';
FILE: frontend/src/constants/validate.ts
constant VALIDATION_SIZE (line 1) | const VALIDATION_SIZE = {
constant VALIDATION_STRING (line 9) | const VALIDATION_STRING = {
constant VALIDATION_MESSAGE (line 14) | const VALIDATION_MESSAGE = {
FILE: frontend/src/domains/schedule.ts
function getSchedulePriority (line 7) | function getSchedulePriority(calendar: string[]) {
FILE: frontend/src/hooks/@queries/category.ts
type UseDeleteCategoryParams (line 21) | interface UseDeleteCategoryParams {
type UseGetAdminCategoriesParams (line 26) | interface UseGetAdminCategoriesParams {
type UseGetEditableCategoriesParams (line 30) | interface UseGetEditableCategoriesParams {
type UseGetEntireCategoriesParams (line 34) | interface UseGetEntireCategoriesParams {
type UseGetSchedulesWithCategoryParams (line 38) | interface UseGetSchedulesWithCategoryParams {
type UseGetSingleCategoryParams (line 44) | interface UseGetSingleCategoryParams
type UseGetSubscribersParams (line 57) | interface UseGetSubscribersParams {
type UsePatchCategoryRoleParams (line 61) | interface UsePatchCategoryRoleParams {
type UsePatchCategoryNameParams (line 67) | interface UsePatchCategoryNameParams {
type UsePostCategoryParams (line 72) | interface UsePostCategoryParams {
function useDeleteCategory (line 76) | function useDeleteCategory({ categoryId, onSuccess }: UseDeleteCategoryP...
function useGetAdminCategories (line 95) | function useGetAdminCategories({ enabled }: UseGetAdminCategoriesParams) {
function useGetEditableCategories (line 109) | function useGetEditableCategories({ enabled }: UseGetEditableCategoriesP...
function useGetEntireCategories (line 124) | function useGetEntireCategories({ keyword }: UseGetEntireCategoriesParam...
function useGetMyCategories (line 136) | function useGetMyCategories() {
function useGetSchedulesWithCategory (line 147) | function useGetSchedulesWithCategory({
function useGetSingleCategory (line 163) | function useGetSingleCategory({ categoryId, ...options }: UseGetSingleCa...
function useGetSubscribers (line 173) | function useGetSubscribers({ categoryId }: UseGetSubscribersParams) {
function usePatchCategoryName (line 184) | function usePatchCategoryName({ categoryId, onSuccess }: UsePatchCategor...
function usePatchCategoryRole (line 208) | function usePatchCategoryRole({ categoryId, memberId, onSuccess }: UsePa...
function usePostCategory (line 232) | function usePostCategory({ onSuccess }: UsePostCategoryParams) {
FILE: frontend/src/hooks/@queries/googleCalendar.ts
type UsePostGoogleCalendarCategoryParams (line 16) | interface UsePostGoogleCalendarCategoryParams {
function useGetGoogleCalendar (line 20) | function useGetGoogleCalendar() {
function usePostGoogleCalendarCategory (line 31) | function usePostGoogleCalendarCategory({ onSuccess }: UsePostGoogleCalen...
FILE: frontend/src/hooks/@queries/login.ts
function useAuth (line 23) | function useAuth(code: string | null) {
function useGetLoginUrl (line 54) | function useGetLoginUrl() {
function useLoginAgain (line 70) | function useLoginAgain() {
function useLoginValidate (line 97) | function useLoginValidate() {
FILE: frontend/src/hooks/@queries/profile.ts
type UseDeleteProfileParams (line 17) | interface UseDeleteProfileParams {
type UsePatchProfileParams (line 21) | interface UsePatchProfileParams {
function useDeleteProfile (line 25) | function useDeleteProfile({ onSuccess }: UseDeleteProfileParams) {
function usePatchProfile (line 44) | function usePatchProfile({ accessToken }: UsePatchProfileParams) {
FILE: frontend/src/hooks/@queries/schedule.ts
type UseDeleteScheduleParams (line 16) | interface UseDeleteScheduleParams {
type UseGetSchedulesParams (line 21) | interface UseGetSchedulesParams {
type UsePatchScheduleParams (line 26) | interface UsePatchScheduleParams {
type UsePostScheduleParams (line 31) | interface UsePostScheduleParams {
function useDeleteSchedule (line 36) | function useDeleteSchedule({ scheduleId, onSuccess }: UseDeleteScheduleP...
function useGetSchedules (line 56) | function useGetSchedules({ startDateTime, endDateTime }: UseGetSchedules...
function usePatchSchedule (line 70) | function usePatchSchedule({ scheduleId, onSuccess }: UsePatchSchedulePar...
function usePostSchedule (line 92) | function usePostSchedule({ categoryId, onSuccess }: UsePostScheduleParam...
FILE: frontend/src/hooks/@queries/subscription.ts
type UseDeleteSubscriptionParams (line 13) | interface UseDeleteSubscriptionParams {
type UseGetSubscriptionsParams (line 18) | interface UseGetSubscriptionsParams {
type UsePatchSubscriptionParams (line 22) | interface UsePatchSubscriptionParams {
type UsePostSubscriptionParams (line 27) | interface UsePostSubscriptionParams {
function useDeleteSubscriptions (line 32) | function useDeleteSubscriptions({ subscriptionId, onSuccess }: UseDelete...
function useGetSubscriptions (line 49) | function useGetSubscriptions({ enabled }: UseGetSubscriptionsParams) {
function usePatchSubscription (line 64) | function usePatchSubscription({ subscriptionId, onSuccess }: UsePatchSub...
function usePostSubscription (line 84) | function usePostSubscription({ categoryId, onSuccess }: UsePostSubscript...
FILE: frontend/src/hooks/useCalendar.ts
type CalendarControllerType (line 14) | interface CalendarControllerType {
function useCalendar (line 30) | function useCalendar() {
FILE: frontend/src/hooks/useControlledInput.ts
function useControlledInput (line 3) | function useControlledInput(initialInputValue?: string) {
FILE: frontend/src/hooks/useHoverCategoryItem.ts
function useHoverCategoryItem (line 5) | function useHoverCategoryItem() {
FILE: frontend/src/hooks/useIntersect.ts
type IntersectHandler (line 3) | type IntersectHandler = (entry: IntersectionObserverEntry, observer: Int...
function useIntersect (line 5) | function useIntersect(onIntersect: IntersectHandler, options?: Intersect...
FILE: frontend/src/hooks/useModalPosition.ts
function useModalPosition (line 7) | function useModalPosition() {
FILE: frontend/src/hooks/useRootFontSize.ts
function useRootFontSize (line 7) | function useRootFontSize() {
FILE: frontend/src/hooks/useSnackBar.ts
function useSnackBar (line 5) | function useSnackBar() {
FILE: frontend/src/hooks/useToggle.ts
function useToggle (line 3) | function useToggle(initialState = false) {
FILE: frontend/src/hooks/useUserValue.ts
function useUserValue (line 17) | function useUserValue() {
FILE: frontend/src/hooks/useValidateCategory.ts
function useValidateCategory (line 7) | function useValidateCategory(initialCategory?: string) {
FILE: frontend/src/hooks/useValidateSchedule.ts
type useValidateScheduleParametersType (line 11) | interface useValidateScheduleParametersType {
function useValidateSchedule (line 20) | function useValidateSchedule({
FILE: frontend/src/pages/AuthPage/AuthPage.tsx
function AuthPage (line 11) | function AuthPage() {
FILE: frontend/src/pages/CalendarPage/CalendarPage.tsx
function CalendarPage (line 20) | function CalendarPage() {
FILE: frontend/src/pages/CategoryPage/CategoryPage.tsx
function CategoryPage (line 17) | function CategoryPage() {
FILE: frontend/src/pages/ErrorPage/ErrorPage.tsx
function ErrorPage (line 11) | function ErrorPage() {
FILE: frontend/src/pages/NotFoundPage/NotFoundPage.tsx
function NotFoundPage (line 15) | function NotFoundPage() {
FILE: frontend/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.tsx
function PrivacyPolicyPage (line 5) | function PrivacyPolicyPage() {
FILE: frontend/src/pages/StartPage/StartPage.tsx
function StartPage (line 30) | function StartPage() {
FILE: frontend/src/recoil/atoms/index.ts
type UserStateType (line 9) | interface UserStateType extends Partial<ProfileType> {
FILE: frontend/src/styles/GlobalStyle.tsx
function GlobalStyle (line 50) | function GlobalStyle() {
Condensed preview — 426 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (971K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug-template.md",
"chars": 157,
"preview": "---\nname: Bug Template\nabout: 버그를 이슈에 등록한다.\ntitle: '이슈의 제목을 입력해주세요!'\nlabels: ''\nassignees: ''\n---\n\n## 🤷 버그 내용\n\n## ⚠ 버그 재"
},
{
"path": ".github/ISSUE_TEMPLATE/feature-template.md",
"chars": 204,
"preview": "---\nname: Feature Template\nabout: 구현할 기능을 이슈에 등록한다.\ntitle: '이슈의 제목을 입력해주세요!'\nlabels: ''\nassignees: ''\n---\n\n## 🤷 구현할 기능\n\n"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 247,
"preview": "- [ ] 🔀 PR 제목의 형식을 잘 작성했나요? e.g. `[feat] PR을 등록한다.` \n- [ ] 💯 테스트는 잘 통과했나요?\n- [ ] 🏗️ 빌드는 성공했나요?\n- [ ] 🧹 불필요한 코드는 제거했나요?\n-"
},
{
"path": ".github/workflows/backend-cd.yml",
"chars": 1245,
"preview": "name: Backend CD\n\non:\n push:\n branches:\n - main\n - develop\n\njobs:\n build:\n runs-on: ubuntu-latest\n\n "
},
{
"path": ".github/workflows/backend-ci.yml",
"chars": 812,
"preview": "name: Backend CI\n\non:\n pull_request:\n branches:\n - main\n - develop\n paths:\n - backend/**\n - ."
},
{
"path": ".github/workflows/frontend-ci.yml",
"chars": 755,
"preview": "name: Frontend CI\n\non:\n pull_request:\n branches:\n - main\n - develop\n paths:\n - frontend/**\n -"
},
{
"path": ".gitignore",
"chars": 17,
"preview": ".idea\n.DS_Store\n\n"
},
{
"path": ".gitmodules",
"chars": 134,
"preview": "[submodule \"backend/src/main/resources/config\"]\n\tpath = backend/src/main/resources/config\n\turl = https://github.com/dall"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2023 dallog\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of "
},
{
"path": "README.md",
"chars": 2875,
"preview": "<div align=\"center\">\n<img src=\"https://user-images.githubusercontent.com/11745691/185735071-5eb23eaa-745b-4d69-a336-b64e"
},
{
"path": "backend/.gitignore",
"chars": 515,
"preview": "HELP.md\n.gradle\nbuild/\n!gradle/wrapper/gradle-wrapper.jar\n!**/src/main/**/build/\n!**/src/test/**/build/\n\n### STS ###\n.ap"
},
{
"path": "backend/build.gradle",
"chars": 4405,
"preview": "plugins {\n id 'org.springframework.boot' version '2.7.1'\n id 'io.spring.dependency-management' version '1.0.11.REL"
},
{
"path": "backend/gradle/wrapper/gradle-wrapper.properties",
"chars": 202,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
},
{
"path": "backend/gradlew",
"chars": 8047,
"preview": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lice"
},
{
"path": "backend/gradlew.bat",
"chars": 2763,
"preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "backend/src/docs/asciidoc/auth.adoc",
"chars": 1565,
"preview": "== Auth(인증)\n\n=== OAuth 로그인 링크 생성\n\n==== HTTP Request\n\ninclude::{snippets}/auth/generateLink/http-request.adoc[]\n\n==== Pat"
},
{
"path": "backend/src/docs/asciidoc/category.adoc",
"chars": 4121,
"preview": "== Category(카테고리)\n\n=== 카테고리 생성\n\n==== HTTP Request\n\ninclude::{snippets}/category/save/http-request.adoc[]\n\n==== Request F"
},
{
"path": "backend/src/docs/asciidoc/external-calendar.adoc",
"chars": 610,
"preview": "== External Calendar (외부 캘린더)\n\n=== 자신의 외부 캘린더 조회\n\n==== HTTP Request\n\ninclude::{snippets}/externalCalendar/get/http-reque"
},
{
"path": "backend/src/docs/asciidoc/index.adoc",
"chars": 256,
"preview": "= 달록 API 문서\n:doctype: book\n:icons: font\n:source-highlighter: highlightjs\n:toc: left\n:toclevels: 3\n\ninclude::auth.adoc[]\n"
},
{
"path": "backend/src/docs/asciidoc/member.adoc",
"chars": 618,
"preview": "== Member(회원)\n\n=== 내 정보 조회\n\n==== HTTP Request\n\ninclude::{snippets}/member/findMe/http-request.adoc[]\n\n==== HTTP Response"
},
{
"path": "backend/src/docs/asciidoc/schedule.adoc",
"chars": 2336,
"preview": "== Schedule(일정)\n\n=== 회원 일정 목록 조회\n\n==== HTTP Request\n\ninclude::{snippets}/schedule/findSchedulesByMemberId/http-request.a"
},
{
"path": "backend/src/docs/asciidoc/subscription.adoc",
"chars": 1558,
"preview": "== Subscription(구독)\n\n=== 구독 등록\n\n==== HTTP Request\n\ninclude::{snippets}/subscription/save/http-request.adoc[]\n\n==== Path "
},
{
"path": "backend/src/main/java/com/allog/dallog/DallogApplication.java",
"chars": 320,
"preview": "package com.allog.dallog;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfig"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/application/AuthService.java",
"chars": 3420,
"preview": "package com.allog.dallog.auth.application;\n\nimport com.allog.dallog.auth.domain.AuthToken;\nimport com.allog.dallog.auth."
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/application/AuthTokenCreator.java",
"chars": 1921,
"preview": "package com.allog.dallog.auth.application;\n\nimport com.allog.dallog.auth.domain.AuthToken;\nimport com.allog.dallog.auth."
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/application/JwtTokenProvider.java",
"chars": 2853,
"preview": "package com.allog.dallog.auth.application;\n\nimport com.allog.dallog.auth.exception.InvalidTokenException;\nimport io.json"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/application/OAuthClient.java",
"chars": 343,
"preview": "package com.allog.dallog.auth.application;\n\nimport com.allog.dallog.auth.dto.OAuthMember;\nimport com.allog.dallog.auth.d"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/application/OAuthUri.java",
"chars": 143,
"preview": "package com.allog.dallog.auth.application;\n\n@FunctionalInterface\npublic interface OAuthUri {\n\n String generate(final "
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/application/TokenCreator.java",
"chars": 292,
"preview": "package com.allog.dallog.auth.application;\n\nimport com.allog.dallog.auth.domain.AuthToken;\n\npublic interface TokenCreato"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/application/TokenProvider.java",
"chars": 275,
"preview": "package com.allog.dallog.auth.application;\n\npublic interface TokenProvider {\n\n String createAccessToken(final String "
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/domain/AuthToken.java",
"chars": 721,
"preview": "package com.allog.dallog.auth.domain;\n\nimport com.allog.dallog.auth.exception.NoSuchTokenException;\n\npublic class AuthTo"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/domain/InMemoryAuthTokenRepository.java",
"chars": 1189,
"preview": "package com.allog.dallog.auth.domain;\n\nimport com.allog.dallog.auth.exception.NoSuchTokenException;\nimport java.util.Map"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/domain/OAuthToken.java",
"chars": 1394,
"preview": "package com.allog.dallog.auth.domain;\n\nimport com.allog.dallog.global.entity.BaseEntity;\nimport com.allog.dallog.member."
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/domain/OAuthTokenRepository.java",
"chars": 720,
"preview": "package com.allog.dallog.auth.domain;\n\nimport com.allog.dallog.auth.exception.NoSuchOAuthTokenException;\nimport java.uti"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/domain/TokenRepository.java",
"chars": 298,
"preview": "package com.allog.dallog.auth.domain;\n\npublic interface TokenRepository {\n\n String save(final Long memberId, final St"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/dto/LoginMember.java",
"chars": 243,
"preview": "package com.allog.dallog.auth.dto;\n\npublic class LoginMember {\n\n private Long id;\n\n private LoginMember() {\n }\n"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/dto/OAuthMember.java",
"chars": 1037,
"preview": "package com.allog.dallog.auth.dto;\n\nimport com.allog.dallog.member.domain.Member;\nimport com.allog.dallog.member.domain."
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/dto/request/TokenRenewalRequest.java",
"chars": 434,
"preview": "package com.allog.dallog.auth.dto.request;\n\nimport javax.validation.constraints.NotNull;\n\npublic class TokenRenewalReque"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/dto/request/TokenRequest.java",
"chars": 621,
"preview": "package com.allog.dallog.auth.dto.request;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constr"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/dto/response/AccessAndRefreshTokenResponse.java",
"chars": 542,
"preview": "package com.allog.dallog.auth.dto.response;\n\npublic class AccessAndRefreshTokenResponse {\n\n private String accessToke"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/dto/response/AccessTokenResponse.java",
"chars": 336,
"preview": "package com.allog.dallog.auth.dto.response;\n\npublic class AccessTokenResponse {\n\n private String accessToken;\n\n pr"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/dto/response/OAuthAccessTokenResponse.java",
"chars": 539,
"preview": "package com.allog.dallog.auth.dto.response;\n\nimport com.fasterxml.jackson.databind.PropertyNamingStrategies;\nimport com."
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/dto/response/OAuthUriResponse.java",
"chars": 346,
"preview": "package com.allog.dallog.auth.dto.response;\n\n// OAuth 인증 URI(소셜 로그인 링크)를 전달하는 DTO\npublic class OAuthUriResponse {\n\n p"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/event/MemberSavedEvent.java",
"chars": 262,
"preview": "package com.allog.dallog.auth.event;\n\npublic class MemberSavedEvent {\n\n private final Long memberId;\n\n public Memb"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/exception/EmptyAuthorizationHeaderException.java",
"chars": 325,
"preview": "package com.allog.dallog.auth.exception;\n\npublic class EmptyAuthorizationHeaderException extends RuntimeException {\n\n "
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/exception/InvalidTokenException.java",
"chars": 270,
"preview": "package com.allog.dallog.auth.exception;\n\npublic class InvalidTokenException extends RuntimeException {\n\n public Inva"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/exception/NoPermissionException.java",
"chars": 270,
"preview": "package com.allog.dallog.auth.exception;\n\npublic class NoPermissionException extends RuntimeException {\n\n public NoPe"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/exception/NoSuchOAuthTokenException.java",
"chars": 291,
"preview": "package com.allog.dallog.auth.exception;\n\npublic class NoSuchOAuthTokenException extends RuntimeException {\n\n public "
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/exception/NoSuchTokenException.java",
"chars": 271,
"preview": "package com.allog.dallog.auth.exception;\n\npublic class NoSuchTokenException extends RuntimeException {\n\n public NoSuc"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/presentation/AuthController.java",
"chars": 2984,
"preview": "package com.allog.dallog.auth.presentation;\n\nimport com.allog.dallog.auth.application.OAuthClient;\nimport com.allog.dall"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/presentation/AuthenticationPrincipal.java",
"chars": 320,
"preview": "package com.allog.dallog.auth.presentation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retent"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/presentation/AuthenticationPrincipalArgumentResolver.java",
"chars": 1525,
"preview": "package com.allog.dallog.auth.presentation;\n\nimport com.allog.dallog.auth.application.AuthService;\nimport com.allog.dall"
},
{
"path": "backend/src/main/java/com/allog/dallog/auth/presentation/AuthorizationExtractor.java",
"chars": 1071,
"preview": "package com.allog.dallog.auth.presentation;\n\nimport com.allog.dallog.auth.exception.InvalidTokenException;\nimport com.al"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/application/CategoryService.java",
"chars": 8498,
"preview": "package com.allog.dallog.category.application;\n\nimport static com.allog.dallog.category.domain.CategoryType.NORMAL;\nimpo"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/application/ExternalCategoryDetailService.java",
"chars": 1442,
"preview": "package com.allog.dallog.category.application;\n\nimport com.allog.dallog.category.domain.Category;\nimport com.allog.dallo"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/domain/Category.java",
"chars": 3588,
"preview": "package com.allog.dallog.category.domain;\n\nimport com.allog.dallog.auth.exception.NoPermissionException;\nimport com.allo"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/domain/CategoryRepository.java",
"chars": 1234,
"preview": "package com.allog.dallog.category.domain;\n\nimport com.allog.dallog.category.exception.NoSuchCategoryException;\nimport ja"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/domain/CategoryType.java",
"chars": 463,
"preview": "package com.allog.dallog.category.domain;\n\nimport com.allog.dallog.category.exception.NoSuchCategoryException;\n\npublic e"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/domain/ExternalCategoryDetail.java",
"chars": 1247,
"preview": "package com.allog.dallog.category.domain;\n\nimport com.allog.dallog.global.entity.BaseEntity;\nimport javax.persistence.Co"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/domain/ExternalCategoryDetailRepository.java",
"chars": 1336,
"preview": "package com.allog.dallog.category.domain;\n\nimport com.allog.dallog.category.exception.ExistExternalCategoryException;\nim"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/dto/request/CategoryCreateRequest.java",
"chars": 903,
"preview": "package com.allog.dallog.category.dto.request;\n\nimport com.allog.dallog.category.domain.Category;\nimport com.allog.dallo"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/dto/request/CategoryUpdateRequest.java",
"chars": 398,
"preview": "package com.allog.dallog.category.dto.request;\n\nimport javax.validation.constraints.NotBlank;\n\npublic class CategoryUpda"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/dto/request/ExternalCategoryCreateRequest.java",
"chars": 640,
"preview": "package com.allog.dallog.category.dto.request;\n\nimport javax.validation.constraints.NotBlank;\n\npublic class ExternalCate"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/dto/response/CategoriesResponse.java",
"chars": 709,
"preview": "package com.allog.dallog.category.dto.response;\n\nimport com.allog.dallog.category.domain.Category;\nimport java.util.List"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/dto/response/CategoryDetailResponse.java",
"chars": 1620,
"preview": "package com.allog.dallog.category.dto.response;\n\nimport com.allog.dallog.category.domain.Category;\nimport com.allog.dall"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/dto/response/CategoryResponse.java",
"chars": 1327,
"preview": "package com.allog.dallog.category.dto.response;\n\nimport com.allog.dallog.category.domain.Category;\nimport com.allog.dall"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/exception/ExistExternalCategoryException.java",
"chars": 305,
"preview": "package com.allog.dallog.category.exception;\n\npublic class ExistExternalCategoryException extends RuntimeException {\n\n "
},
{
"path": "backend/src/main/java/com/allog/dallog/category/exception/InvalidCategoryException.java",
"chars": 281,
"preview": "package com.allog.dallog.category.exception;\n\npublic class InvalidCategoryException extends RuntimeException {\n\n publ"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/exception/NoSuchCategoryException.java",
"chars": 282,
"preview": "package com.allog.dallog.category.exception;\n\npublic class NoSuchCategoryException extends RuntimeException {\n\n publi"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/exception/NoSuchExternalCategoryDetailException.java",
"chars": 331,
"preview": "package com.allog.dallog.category.exception;\n\npublic class NoSuchExternalCategoryDetailException extends RuntimeExceptio"
},
{
"path": "backend/src/main/java/com/allog/dallog/category/presentaion/CategoryController.java",
"chars": 5161,
"preview": "package com.allog.dallog.category.presentaion;\n\nimport com.allog.dallog.auth.dto.LoginMember;\nimport com.allog.dallog.au"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/application/CategoryRoleService.java",
"chars": 3786,
"preview": "package com.allog.dallog.categoryrole.application;\n\nimport com.allog.dallog.categoryrole.domain.CategoryAuthority;\nimpor"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryAuthority.java",
"chars": 475,
"preview": "package com.allog.dallog.categoryrole.domain;\n\npublic enum CategoryAuthority {\n UPDATE_CATEGORY(\"카테고리 수정\"),\n DELET"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryRole.java",
"chars": 2463,
"preview": "package com.allog.dallog.categoryrole.domain;\n\nimport com.allog.dallog.category.domain.Category;\nimport com.allog.dallog"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryRoleRepository.java",
"chars": 2594,
"preview": "package com.allog.dallog.categoryrole.domain;\n\nimport com.allog.dallog.categoryrole.exception.ManagingCategoryLimitExces"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/domain/CategoryRoleType.java",
"chars": 1871,
"preview": "package com.allog.dallog.categoryrole.domain;\n\nimport static com.allog.dallog.categoryrole.domain.CategoryAuthority.ADD_"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/dto/request/CategoryRoleUpdateRequest.java",
"chars": 569,
"preview": "package com.allog.dallog.categoryrole.dto.request;\n\nimport com.allog.dallog.categoryrole.domain.CategoryRoleType;\nimport"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/dto/response/MemberWithRoleTypeResponse.java",
"chars": 789,
"preview": "package com.allog.dallog.categoryrole.dto.response;\n\nimport com.allog.dallog.categoryrole.domain.CategoryRoleType;\nimpor"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/dto/response/SubscribersResponse.java",
"chars": 788,
"preview": "package com.allog.dallog.categoryrole.dto.response;\n\nimport com.allog.dallog.categoryrole.domain.CategoryRole;\nimport ja"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/exception/ManagingCategoryLimitExcessException.java",
"chars": 336,
"preview": "package com.allog.dallog.categoryrole.exception;\n\npublic class ManagingCategoryLimitExcessException extends RuntimeExcep"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/exception/NoCategoryAuthorityException.java",
"chars": 243,
"preview": "package com.allog.dallog.categoryrole.exception;\n\npublic class NoCategoryAuthorityException extends RuntimeException {\n\n"
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/exception/NoSuchCategoryRoleException.java",
"chars": 296,
"preview": "package com.allog.dallog.categoryrole.exception;\n\npublic class NoSuchCategoryRoleException extends RuntimeException {\n\n "
},
{
"path": "backend/src/main/java/com/allog/dallog/categoryrole/exception/NotAbleToChangeRoleException.java",
"chars": 462,
"preview": "package com.allog.dallog.categoryrole.exception;\n\npublic class NotAbleToChangeRoleException extends RuntimeException {\n\n"
},
{
"path": "backend/src/main/java/com/allog/dallog/externalcalendar/application/ExternalCalendarClient.java",
"chars": 706,
"preview": "package com.allog.dallog.externalcalendar.application;\n\nimport com.allog.dallog.externalcalendar.dto.ExternalCalendar;\ni"
},
{
"path": "backend/src/main/java/com/allog/dallog/externalcalendar/application/ExternalCalendarService.java",
"chars": 1308,
"preview": "package com.allog.dallog.externalcalendar.application;\n\nimport com.allog.dallog.auth.application.OAuthClient;\nimport com"
},
{
"path": "backend/src/main/java/com/allog/dallog/externalcalendar/dto/ExternalCalendar.java",
"chars": 470,
"preview": "package com.allog.dallog.externalcalendar.dto;\n\npublic class ExternalCalendar {\n\n private String calendarId;\n priv"
},
{
"path": "backend/src/main/java/com/allog/dallog/externalcalendar/dto/ExternalCalendarsResponse.java",
"chars": 465,
"preview": "package com.allog.dallog.externalcalendar.dto;\n\nimport java.util.List;\n\npublic class ExternalCalendarsResponse {\n\n pr"
},
{
"path": "backend/src/main/java/com/allog/dallog/externalcalendar/presentation/ExternalCalendarController.java",
"chars": 2144,
"preview": "package com.allog.dallog.externalcalendar.presentation;\n\nimport com.allog.dallog.auth.dto.LoginMember;\nimport com.allog."
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/JpaConfig.java",
"chars": 237,
"preview": "package com.allog.dallog.global.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springf"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/PropertiesConfig.java",
"chars": 356,
"preview": "package com.allog.dallog.global.config;\n\nimport com.allog.dallog.global.config.properties.GoogleProperties;\nimport org.s"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/WebConfig.java",
"chars": 1477,
"preview": "package com.allog.dallog.global.config;\n\nimport java.util.List;\nimport org.springframework.beans.factory.annotation.Valu"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/cache/CacheConfig.java",
"chars": 1177,
"preview": "package com.allog.dallog.global.config.cache;\n\nimport java.util.List;\nimport org.springframework.cache.CacheManager;\nimp"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/cache/ExpiringConcurrentMapCache.java",
"chars": 1590,
"preview": "package com.allog.dallog.global.config.cache;\n\nimport java.time.LocalDateTime;\nimport java.util.Map;\nimport java.util.Ob"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/properties/GoogleProperties.java",
"chars": 1614,
"preview": "package com.allog.dallog.global.config.properties;\n\nimport java.util.List;\nimport org.springframework.boot.context.prope"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/replication/DataSourceConfiguration.java",
"chars": 2839,
"preview": "package com.allog.dallog.global.config.replication;\n\nimport static com.allog.dallog.global.config.replication.DataSource"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/replication/DataSourceKey.java",
"chars": 959,
"preview": "package com.allog.dallog.global.config.replication;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.st"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/replication/RandomReplicaKeys.java",
"chars": 638,
"preview": "package com.allog.dallog.global.config.replication;\n\nimport java.util.List;\nimport java.util.concurrent.ThreadLocalRando"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/config/replication/RoutingDataSource.java",
"chars": 717,
"preview": "package com.allog.dallog.global.config.replication;\n\nimport static com.allog.dallog.global.config.replication.DataSource"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/entity/BaseEntity.java",
"chars": 872,
"preview": "package com.allog.dallog.global.entity;\n\nimport java.time.LocalDateTime;\nimport javax.persistence.Column;\nimport javax.p"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java",
"chars": 6898,
"preview": "package com.allog.dallog.global.error;\n\nimport com.allog.dallog.auth.exception.EmptyAuthorizationHeaderException;\nimport"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/error/dto/ErrorReportRequest.java",
"chars": 821,
"preview": "package com.allog.dallog.global.error.dto;\n\nimport javax.servlet.http.HttpServletRequest;\n\npublic class ErrorReportReque"
},
{
"path": "backend/src/main/java/com/allog/dallog/global/error/dto/ErrorResponse.java",
"chars": 262,
"preview": "package com.allog.dallog.global.error.dto;\n\npublic class ErrorResponse {\n\n private final String message;\n\n public "
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/log/DiscordAppender.java",
"chars": 3729,
"preview": "package com.allog.dallog.infrastructure.log;\n\nimport ch.qos.logback.classic.spi.ILoggingEvent;\nimport ch.qos.logback.cla"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/log/dto/DiscordWebhookRequest.java",
"chars": 508,
"preview": "package com.allog.dallog.infrastructure.log.dto;\n\nimport java.util.List;\n\npublic class DiscordWebhookRequest {\n\n priv"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Embed.java",
"chars": 852,
"preview": "package com.allog.dallog.infrastructure.log.dto;\n\nimport java.util.List;\n\npublic class Embed {\n\n private String title"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Field.java",
"chars": 671,
"preview": "package com.allog.dallog.infrastructure.log.dto;\n\npublic class Field {\n\n private String name;\n private String valu"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java",
"chars": 5261,
"preview": "package com.allog.dallog.infrastructure.oauth.client;\n\nimport com.allog.dallog.externalcalendar.application.ExternalCale"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleOAuthClient.java",
"chars": 5432,
"preview": "package com.allog.dallog.infrastructure.oauth.client;\n\nimport com.allog.dallog.auth.application.OAuthClient;\nimport com."
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java",
"chars": 2053,
"preview": "package com.allog.dallog.infrastructure.oauth.dto;\n\nimport com.allog.dallog.category.domain.CategoryType;\nimport com.all"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java",
"chars": 439,
"preview": "package com.allog.dallog.infrastructure.oauth.dto;\n\nimport java.util.List;\n\npublic class GoogleCalendarEventsResponse {\n"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java",
"chars": 418,
"preview": "package com.allog.dallog.infrastructure.oauth.dto;\n\nimport java.util.List;\n\npublic class GoogleCalendarListResponse {\n\n "
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java",
"chars": 614,
"preview": "package com.allog.dallog.infrastructure.oauth.dto;\n\npublic class GoogleCalendarResponse {\n\n private String id;\n pr"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java",
"chars": 444,
"preview": "package com.allog.dallog.infrastructure.oauth.dto;\n\npublic class GoogleDateFormat {\n\n private String date;\n privat"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java",
"chars": 683,
"preview": "package com.allog.dallog.infrastructure.oauth.dto;\n\nimport com.fasterxml.jackson.databind.PropertyNamingStrategies;\nimpo"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/UserInfo.java",
"chars": 548,
"preview": "package com.allog.dallog.infrastructure.oauth.dto;\n\npublic class UserInfo {\n\n private String email;\n private Strin"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/exception/OAuthException.java",
"chars": 409,
"preview": "package com.allog.dallog.infrastructure.oauth.exception;\n\npublic class OAuthException extends RuntimeException {\n\n pu"
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/DevGoogleOAuthUri.java",
"chars": 992,
"preview": "package com.allog.dallog.infrastructure.oauth.uri;\n\nimport com.allog.dallog.auth.application.OAuthUri;\nimport com.allog."
},
{
"path": "backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/GoogleOAuthUri.java",
"chars": 935,
"preview": "package com.allog.dallog.infrastructure.oauth.uri;\n\nimport com.allog.dallog.auth.application.OAuthUri;\nimport com.allog."
},
{
"path": "backend/src/main/java/com/allog/dallog/member/application/MemberService.java",
"chars": 964,
"preview": "package com.allog.dallog.member.application;\n\nimport com.allog.dallog.member.domain.Member;\nimport com.allog.dallog.memb"
},
{
"path": "backend/src/main/java/com/allog/dallog/member/domain/Member.java",
"chars": 2770,
"preview": "package com.allog.dallog.member.domain;\n\nimport com.allog.dallog.global.entity.BaseEntity;\nimport com.allog.dallog.membe"
},
{
"path": "backend/src/main/java/com/allog/dallog/member/domain/MemberRepository.java",
"chars": 808,
"preview": "package com.allog.dallog.member.domain;\n\nimport com.allog.dallog.member.exception.NoSuchMemberException;\nimport java.uti"
},
{
"path": "backend/src/main/java/com/allog/dallog/member/domain/SocialType.java",
"chars": 89,
"preview": "package com.allog.dallog.member.domain;\n\npublic enum SocialType {\n\n GOOGLE, GITHUB;\n}\n"
},
{
"path": "backend/src/main/java/com/allog/dallog/member/dto/request/MemberUpdateRequest.java",
"chars": 430,
"preview": "package com.allog.dallog.member.dto.request;\n\nimport javax.validation.constraints.NotBlank;\n\npublic class MemberUpdateRe"
},
{
"path": "backend/src/main/java/com/allog/dallog/member/dto/response/MemberResponse.java",
"chars": 1259,
"preview": "package com.allog.dallog.member.dto.response;\n\nimport com.allog.dallog.member.domain.Member;\nimport com.allog.dallog.mem"
},
{
"path": "backend/src/main/java/com/allog/dallog/member/exception/InvalidMemberException.java",
"chars": 275,
"preview": "package com.allog.dallog.member.exception;\n\npublic class InvalidMemberException extends RuntimeException {\n\n public I"
},
{
"path": "backend/src/main/java/com/allog/dallog/member/exception/NoSuchMemberException.java",
"chars": 272,
"preview": "package com.allog.dallog.member.exception;\n\npublic class NoSuchMemberException extends RuntimeException {\n\n public No"
},
{
"path": "backend/src/main/java/com/allog/dallog/member/presentation/MemberController.java",
"chars": 1562,
"preview": "package com.allog.dallog.member.presentation;\n\nimport com.allog.dallog.auth.dto.LoginMember;\nimport com.allog.dallog.aut"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/application/CheckedSchedulesFinder.java",
"chars": 3910,
"preview": "package com.allog.dallog.schedule.application;\n\nimport com.allog.dallog.auth.application.OAuthClient;\nimport com.allog.d"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/application/ScheduleService.java",
"chars": 7225,
"preview": "package com.allog.dallog.schedule.application;\n\nimport com.allog.dallog.auth.domain.OAuthToken;\nimport com.allog.dallog."
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/domain/IntegrationSchedule.java",
"chars": 3503,
"preview": "package com.allog.dallog.schedule.domain;\n\nimport com.allog.dallog.category.domain.CategoryType;\nimport com.allog.dallog"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/domain/IntegrationScheduleComparator.java",
"chars": 1452,
"preview": "package com.allog.dallog.schedule.domain;\n\nimport java.time.LocalDateTime;\nimport java.util.Comparator;\n\npublic class In"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/domain/IntegrationSchedules.java",
"chars": 626,
"preview": "package com.allog.dallog.schedule.domain;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class IntegrationS"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/domain/Period.java",
"chars": 2776,
"preview": "package com.allog.dallog.schedule.domain;\n\nimport static java.time.LocalTime.MIDNIGHT;\n\nimport java.time.LocalDate;\nimpo"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/domain/Schedule.java",
"chars": 4111,
"preview": "package com.allog.dallog.schedule.domain;\n\nimport com.allog.dallog.global.entity.BaseEntity;\nimport com.allog.dallog.cat"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/domain/ScheduleRepository.java",
"chars": 1717,
"preview": "package com.allog.dallog.schedule.domain;\n\nimport com.allog.dallog.category.domain.Category;\nimport com.allog.dallog.sch"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/domain/ScheduleType.java",
"chars": 935,
"preview": "package com.allog.dallog.schedule.domain;\n\nimport java.util.Arrays;\nimport java.util.function.Predicate;\n\npublic enum Sc"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/domain/TypedSchedules.java",
"chars": 957,
"preview": "package com.allog.dallog.schedule.domain;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npubli"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/domain/scheduler/Scheduler.java",
"chars": 1308,
"preview": "package com.allog.dallog.schedule.domain.scheduler;\n\nimport com.allog.dallog.schedule.domain.IntegrationSchedule;\nimport"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/dto/MaterialToFindSchedules.java",
"chars": 1392,
"preview": "package com.allog.dallog.schedule.dto;\n\nimport com.allog.dallog.category.domain.ExternalCategoryDetail;\nimport com.allog"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/dto/request/DateRangeRequest.java",
"chars": 1162,
"preview": "package com.allog.dallog.schedule.dto.request;\n\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatte"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/dto/request/ScheduleCreateRequest.java",
"chars": 1433,
"preview": "package com.allog.dallog.schedule.dto.request;\n\nimport com.allog.dallog.category.domain.Category;\nimport com.allog.dallo"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/dto/request/ScheduleUpdateRequest.java",
"chars": 1389,
"preview": "package com.allog.dallog.schedule.dto.request;\n\nimport java.time.LocalDateTime;\nimport javax.validation.constraints.NotN"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/dto/response/IntegrationScheduleResponse.java",
"chars": 2129,
"preview": "package com.allog.dallog.schedule.dto.response;\n\nimport com.allog.dallog.schedule.domain.IntegrationSchedule;\nimport com"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/dto/response/IntegrationScheduleResponses.java",
"chars": 3264,
"preview": "package com.allog.dallog.schedule.dto.response;\n\nimport com.allog.dallog.schedule.domain.ScheduleType;\nimport com.allog."
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/dto/response/ScheduleResponse.java",
"chars": 1662,
"preview": "package com.allog.dallog.schedule.dto.response;\n\nimport com.allog.dallog.schedule.domain.Schedule;\nimport java.time.Loca"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/exception/InvalidScheduleException.java",
"chars": 279,
"preview": "package com.allog.dallog.schedule.exception;\n\npublic class InvalidScheduleException extends RuntimeException {\n\n publ"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/exception/NoSuchScheduleException.java",
"chars": 280,
"preview": "package com.allog.dallog.schedule.exception;\n\npublic class NoSuchScheduleException extends RuntimeException {\n\n publi"
},
{
"path": "backend/src/main/java/com/allog/dallog/schedule/presentation/ScheduleController.java",
"chars": 4234,
"preview": "package com.allog.dallog.schedule.presentation;\n\nimport com.allog.dallog.auth.dto.LoginMember;\nimport com.allog.dallog.a"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/application/ColorPicker.java",
"chars": 129,
"preview": "package com.allog.dallog.subscription.application;\n\n@FunctionalInterface\npublic interface ColorPicker {\n\n int pickNum"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/application/RandomColorPicker.java",
"chars": 493,
"preview": "package com.allog.dallog.subscription.application;\n\nimport com.allog.dallog.subscription.domain.Color;\nimport java.util."
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/application/SubscriptionService.java",
"chars": 4665,
"preview": "package com.allog.dallog.subscription.application;\n\nimport com.allog.dallog.categoryrole.domain.CategoryRoleType;\nimport"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/domain/Color.java",
"chars": 1368,
"preview": "package com.allog.dallog.subscription.domain;\n\nimport com.allog.dallog.subscription.exception.InvalidSubscriptionExcepti"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/domain/Subscription.java",
"chars": 2352,
"preview": "package com.allog.dallog.subscription.domain;\n\nimport com.allog.dallog.auth.exception.NoPermissionException;\nimport com."
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/domain/SubscriptionRepository.java",
"chars": 1555,
"preview": "package com.allog.dallog.subscription.domain;\n\nimport com.allog.dallog.auth.exception.NoPermissionException;\nimport com."
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/domain/Subscriptions.java",
"chars": 1430,
"preview": "package com.allog.dallog.subscription.domain;\n\nimport com.allog.dallog.category.exception.NoSuchCategoryException;\nimpor"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/dto/request/SubscriptionUpdateRequest.java",
"chars": 917,
"preview": "package com.allog.dallog.subscription.dto.request;\n\nimport com.allog.dallog.subscription.domain.Color;\nimport com.faster"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/dto/response/SubscriptionResponse.java",
"chars": 1215,
"preview": "package com.allog.dallog.subscription.dto.response;\n\nimport com.allog.dallog.category.dto.response.CategoryResponse;\nimp"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/dto/response/SubscriptionsResponse.java",
"chars": 446,
"preview": "package com.allog.dallog.subscription.dto.response;\n\nimport java.util.List;\n\npublic class SubscriptionsResponse {\n\n p"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/exception/ExistSubscriptionException.java",
"chars": 297,
"preview": "package com.allog.dallog.subscription.exception;\n\npublic class ExistSubscriptionException extends RuntimeException {\n\n "
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/exception/InvalidSubscriptionException.java",
"chars": 302,
"preview": "package com.allog.dallog.subscription.exception;\n\npublic class InvalidSubscriptionException extends RuntimeException {\n\n"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/exception/NoSuchSubscriptionException.java",
"chars": 299,
"preview": "package com.allog.dallog.subscription.exception;\n\npublic class NoSuchSubscriptionException extends RuntimeException {\n\n "
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/exception/NotAbleToUnsubscribeException.java",
"chars": 302,
"preview": "package com.allog.dallog.subscription.exception;\n\npublic class NotAbleToUnsubscribeException extends RuntimeException {\n"
},
{
"path": "backend/src/main/java/com/allog/dallog/subscription/presentation/SubscriptionController.java",
"chars": 3051,
"preview": "package com.allog.dallog.subscription.presentation;\n\nimport com.allog.dallog.auth.dto.LoginMember;\nimport com.allog.dall"
},
{
"path": "backend/src/main/resources/application-test.yml",
"chars": 964,
"preview": "spring:\n data:\n web:\n pageable:\n max-page-size: 100\n\n main:\n allow-bean-definition-overriding: true\n"
},
{
"path": "backend/src/main/resources/db/prod/schema.sql",
"chars": 2788,
"preview": "CREATE TABLE IF NOT EXISTS members (\n id BIGINT AUTO_INCREMENT,\n email VARCHAR(255) NOT NULL,\n display_name VARCHAR(2"
},
{
"path": "backend/src/main/resources/logback-spring.xml",
"chars": 3131,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<configuration>\n <include resource=\"org/springframework/boot/logging/logback/"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java",
"chars": 1000,
"preview": "package com.allog.dallog.acceptance;\n\nimport com.allog.dallog.auth.domain.TokenRepository;\nimport com.allog.dallog.commo"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/AuthAcceptanceTest.java",
"chars": 4011,
"preview": "package com.allog.dallog.acceptance;\n\nimport static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.OAuth_인증"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/CategoryAcceptanceTest.java",
"chars": 8071,
"preview": "package com.allog.dallog.acceptance;\n\nimport static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/ExternalCalendarAcceptanceTest.java",
"chars": 3100,
"preview": "package com.allog.dallog.acceptance;\n\nimport static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/MemberAcceptanceTest.java",
"chars": 2816,
"preview": "package com.allog.dallog.acceptance;\n\nimport static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java",
"chars": 6775,
"preview": "package com.allog.dallog.acceptance;\n\nimport static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/SubscriptionAcceptanceTest.java",
"chars": 7360,
"preview": "package com.allog.dallog.acceptance;\n\nimport static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/fixtures/AuthAcceptanceFixtures.java",
"chars": 2859,
"preview": "package com.allog.dallog.acceptance.fixtures;\n\nimport com.allog.dallog.auth.dto.request.TokenRenewalRequest;\nimport com."
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/fixtures/CategoryAcceptanceFixtures.java",
"chars": 3348,
"preview": "package com.allog.dallog.acceptance.fixtures;\n\nimport com.allog.dallog.category.dto.request.CategoryCreateRequest;\nimpor"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/fixtures/CommonAcceptanceFixtures.java",
"chars": 1161,
"preview": "package com.allog.dallog.acceptance.fixtures;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport io.rest"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/fixtures/MemberAcceptanceFixtures.java",
"chars": 705,
"preview": "package com.allog.dallog.acceptance.fixtures;\n\nimport io.restassured.RestAssured;\nimport io.restassured.response.Extract"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java",
"chars": 3959,
"preview": "package com.allog.dallog.acceptance.fixtures;\n\nimport static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메"
},
{
"path": "backend/src/test/java/com/allog/dallog/acceptance/fixtures/SubscriptionAcceptanceFixtures.java",
"chars": 1336,
"preview": "package com.allog.dallog.acceptance.fixtures;\n\nimport com.allog.dallog.subscription.dto.response.SubscriptionResponse;\ni"
},
{
"path": "backend/src/test/java/com/allog/dallog/auth/application/AuthServiceTest.java",
"chars": 4736,
"preview": "package com.allog.dallog.auth.application;\n\nimport static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_이메일;\nimpo"
},
{
"path": "backend/src/test/java/com/allog/dallog/auth/application/AuthTokenCreatorTest.java",
"chars": 1626,
"preview": "package com.allog.dallog.auth.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport com.allog."
},
{
"path": "backend/src/test/java/com/allog/dallog/auth/application/JwtTokenProviderTest.java",
"chars": 2847,
"preview": "package com.allog.dallog.auth.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org."
},
{
"path": "backend/src/test/java/com/allog/dallog/auth/application/StubTokenProvider.java",
"chars": 2280,
"preview": "package com.allog.dallog.auth.application;\n\nimport com.allog.dallog.auth.exception.InvalidTokenException;\nimport io.json"
},
{
"path": "backend/src/test/java/com/allog/dallog/auth/domain/AuthTokenTest.java",
"chars": 972,
"preview": "package com.allog.dallog.auth.domain;\n\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\nimport com.all"
},
{
"path": "backend/src/test/java/com/allog/dallog/auth/domain/InMemoryAuthTokenRepositoryTest.java",
"chars": 2424,
"preview": "package com.allog.dallog.auth.domain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.asser"
},
{
"path": "backend/src/test/java/com/allog/dallog/auth/domain/OAuthTokenRepositoryTest.java",
"chars": 2203,
"preview": "package com.allog.dallog.auth.domain;\n\nimport static com.allog.dallog.common.fixtures.MemberFixtures.매트;\nimport static c"
},
{
"path": "backend/src/test/java/com/allog/dallog/auth/domain/OAuthTokenTest.java",
"chars": 1174,
"preview": "package com.allog.dallog.auth.domain;\n\nimport static com.allog.dallog.common.fixtures.MemberFixtures.매트;\nimport static o"
},
{
"path": "backend/src/test/java/com/allog/dallog/auth/presentation/AuthControllerTest.java",
"chars": 9031,
"preview": "package com.allog.dallog.auth.presentation;\n\nimport static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER"
},
{
"path": "backend/src/test/java/com/allog/dallog/category/application/CategoryServiceTest.java",
"chars": 15009,
"preview": "package com.allog.dallog.category.application;\n\nimport static com.allog.dallog.category.domain.CategoryType.GOOGLE;\nimpo"
},
{
"path": "backend/src/test/java/com/allog/dallog/category/application/ExternalCategoryDetailServiceTest.java",
"chars": 1568,
"preview": "package com.allog.dallog.category.application;\n\nimport static com.allog.dallog.category.domain.CategoryType.GOOGLE;\nimpo"
},
{
"path": "backend/src/test/java/com/allog/dallog/category/domain/CategoryRepositoryTest.java",
"chars": 7097,
"preview": "package com.allog.dallog.category.domain;\n\nimport static com.allog.dallog.category.domain.CategoryType.NORMAL;\nimport st"
},
{
"path": "backend/src/test/java/com/allog/dallog/category/domain/CategoryTest.java",
"chars": 2956,
"preview": "package com.allog.dallog.category.domain;\n\nimport static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정;\nimport"
},
{
"path": "backend/src/test/java/com/allog/dallog/category/domain/CategoryTypeTest.java",
"chars": 1255,
"preview": "package com.allog.dallog.category.domain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.a"
},
{
"path": "backend/src/test/java/com/allog/dallog/category/domain/ExternalCategoryDetailRepositoryTest.java",
"chars": 3075,
"preview": "package com.allog.dallog.category.domain;\n\nimport static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정;\nimport"
},
{
"path": "backend/src/test/java/com/allog/dallog/category/presentation/CategoryControllerTest.java",
"chars": 30369,
"preview": "package com.allog.dallog.category.presentation;\n\nimport static com.allog.dallog.category.domain.CategoryType.NORMAL;\nimp"
},
{
"path": "backend/src/test/java/com/allog/dallog/categoryrole/application/CategoryRoleServiceTest.java",
"chars": 7380,
"preview": "package com.allog.dallog.categoryrole.application;\n\nimport static com.allog.dallog.category.domain.CategoryType.GOOGLE;\n"
},
{
"path": "backend/src/test/java/com/allog/dallog/categoryrole/domain/CategoryRoleRepositoryTest.java",
"chars": 2505,
"preview": "package com.allog.dallog.categoryrole.domain;\n\nimport static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정;\nim"
},
{
"path": "backend/src/test/java/com/allog/dallog/categoryrole/domain/CategoryRoleTest.java",
"chars": 1195,
"preview": "package com.allog.dallog.categoryrole.domain;\n\nimport static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정;\nim"
},
{
"path": "backend/src/test/java/com/allog/dallog/categoryrole/domain/CategoryRoleTypeTest.java",
"chars": 1316,
"preview": "package com.allog.dallog.categoryrole.domain;\n\nimport static com.allog.dallog.categoryrole.domain.CategoryAuthority.ADD_"
},
{
"path": "backend/src/test/java/com/allog/dallog/common/Constants.java",
"chars": 1272,
"preview": "package com.allog.dallog.common;\n\nimport java.time.LocalDateTime;\n\npublic class Constants {\n\n public static final Str"
}
]
// ... and 226 more files (download for full content)
About this extraction
This page contains the full source code of the woowacourse-teams/2022-dallog GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 426 files (811.5 KB), approximately 227.4k tokens, and a symbol index with 1462 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.