From 940f3b51c118266919d0335f44a7a452ac8d4ae2 Mon Sep 17 00:00:00 2001 From: yoonseo choi Date: Thu, 18 Jul 2024 18:02:45 +0900 Subject: [PATCH 1/5] =?UTF-8?q?ci=20=EC=B4=88=EA=B8=B0=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80=20(issue#47)=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: ci 초기 설정 추가 * fix: gradle 빌드 명령어 수정 * fix: gradle 빌드 명령어 수정 * test: ci 정상 작동 여부 확인을 위한 실패 테스트 작성 * chore: ci 실패시 슬랙 메시지 전송 * chore: 슬랙 메시지 전송 시 멘션하도록 설정 * fix: 슬랙 전송 시 멘션 기능 * fix: 슬랙 전송 시 멘션 기능 * fix: 슬랙 멘션 테스트 * fix: 슬랙 메시지 멘션 기능 * fix: 슬랙 메시지 멘션 기능 * fix: 슬랙 멘션 기능 테스트 * fix: 슬랙 메시지 멘션 기능 테스트 * fix: 슬랙 멘션 메시지 테스트 * fix: 스크립트 문법 오류 수정 * fix: 스크립트 문법 오류 수정 * fix: 스크립트 문법 오류 수정 * fix: 스크립트 문법 오류 수정 * fix: 스크립트 문법 오류 수정 * test: 실패하는 테스트 삭제 * chore: 팀원 목록 추가 * fix: 메인 브랜치가 아니라, PR의 코드로 체크아웃 하도록 수정 * feat: 백엔드 작업이 아닌 경우 CI 가 돌아가지 않도록 하는 기능 추가 * fix: 의존성 버전 수정 * fix: 의존성 제거 * fix: 백엔드가 아닐 경우 early return 하도록 수정 * fix: 백엔드가 아닐 경우 early return 하도록 수정 * fix: early return 순서 변경 * fix: early return 조건 변경 * fix: early return 조건 변경 * fix: early return 방식 변경 * test: 백엔드 PR일 경우 CI 스킵하지 않는지 테스트 * test: 백엔드 PR일 경우 CI 스킵하지 않는지 테스트 * fix: early 리턴 방식 변경 * fix: early 리턴 문법 오류 수정 * fix: early 리턴 문법 오류 수정 --------- Co-authored-by: robinjoon --- .github/workflows/ci.yml | 82 +++++++++++++++++++++++++++++++ .github/workflows/teamMember.json | 10 ++++ 2 files changed, 92 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/teamMember.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..988de01a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,82 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle + +name: Gradle Package + +on: + pull_request: + types: [ opened, reopened, synchronize ] + branches: [ 'main' ] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + actions: write + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + - name: Early exit + if: ${{ !contains(github.event.pull_request.labels.*.name, '백엔드') }} + run: | + gh run cancel ${{ github.run_id }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/setup-java@v4 + with: + distribution: 'corretto' + java-version: '21' + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }} # location for the settings.xml file + + - name: Setup Gradle + run: chmod +x ./backend/gradlew + + - name: Build with Gradle + continue-on-error: true + id: gradle_build + run: | + cd backend + ./gradlew build + + - name: Get teamMember List + id: teamMembers + uses: actions/github-script@v6 + with: + script: | + const fs = require('fs'); + const workers = JSON.parse(fs.readFileSync('.github/workflows/teamMember.json')); + const mention = context.payload.pull_request.assignees.map((user) => { + const login = user.login; + const mappedValue = workers[login]; + return mappedValue ? `<@${mappedValue}>` : `No mapping found for ${login}`; + }) + return mention.join(', '); + + - name: slack mention + uses: slackapi/slack-github-action@v1.24.0 + with: + channel-id: ${{ secrets.ISSUE_CHANNEL }} + payload: | + { + "text": "pr 테스트 결과", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "pr 테스트 ${{ steps.gradle_build.outcome}} \n • 링크: <${{ github.event.pull_request.html_url }}|${{ github.event.pull_request.title }}> \n • pr 담당자: \${{ steps.teamMembers.outputs.result }} + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.BOT_TOKEN }} diff --git a/.github/workflows/teamMember.json b/.github/workflows/teamMember.json new file mode 100644 index 00000000..3b1e4e0f --- /dev/null +++ b/.github/workflows/teamMember.json @@ -0,0 +1,10 @@ +{ + "lilychoibb": "U07AQJWU8S3", + "robinjoon": "U07BU02FQFJ", + "brgndyy" : "U07B53DM02W", + "chosim-dvlpr": "U07BHP5UTLH", + "Minjoo522": "U07B4V80WLT", + "alstn113": "U07AQK2KBLP", + "le2sky": "U07B26581CM", + "Parkhanyoung": "U07BTSGKCC8" +} From f4a27c013e8bd19c8f4fcac7dd48d7e4b0eee951 Mon Sep 17 00:00:00 2001 From: JEON TAEHEON Date: Thu, 18 Jul 2024 22:10:32 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=A9=EB=B2=95=20?= =?UTF-8?q?=EC=95=88=EB=82=B4=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(issue#52)=20(#56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 린트에러 수정 * feat: 미션 가이드 페이지 구현 * refactor: 리뷰 수정 사항 반영 * refactor: 컴포넌트명 수정 --- frontend/src/apis/authAPI.ts | 2 +- frontend/src/components/guide/HowToFork.tsx | 142 ++++++++++++++++++ frontend/src/components/tab/Tab.styled.ts | 64 ++++++++ .../src/components/tab/TabCurrentContent.tsx | 8 + frontend/src/components/tab/TabHeader.tsx | 16 ++ .../src/components/tab/TabListWrapper.tsx | 6 + frontend/src/components/tab/Tabs.tsx | 30 ++++ .../userProfile/UserProfileDescription.tsx | 5 +- .../userProfile/UserProfileEmail.tsx | 2 +- .../userProfile/UserProfileImage.tsx | 2 +- .../userProfile/UserProfileName.tsx | 2 +- frontend/src/constants/messages.ts | 3 + frontend/src/constants/routes.ts | 1 + frontend/src/contexts/TabsContext.ts | 10 ++ frontend/src/hooks/useTabs.ts | 12 ++ frontend/src/hooks/useUserInfo.ts | 2 +- frontend/src/index.tsx | 9 ++ frontend/src/pages/GuidePage.tsx | 35 +++++ frontend/src/types/index.ts | 7 + 19 files changed, 351 insertions(+), 7 deletions(-) create mode 100644 frontend/src/components/guide/HowToFork.tsx create mode 100644 frontend/src/components/tab/Tab.styled.ts create mode 100644 frontend/src/components/tab/TabCurrentContent.tsx create mode 100644 frontend/src/components/tab/TabHeader.tsx create mode 100644 frontend/src/components/tab/TabListWrapper.tsx create mode 100644 frontend/src/components/tab/Tabs.tsx create mode 100644 frontend/src/constants/messages.ts create mode 100644 frontend/src/contexts/TabsContext.ts create mode 100644 frontend/src/hooks/useTabs.ts create mode 100644 frontend/src/pages/GuidePage.tsx diff --git a/frontend/src/apis/authAPI.ts b/frontend/src/apis/authAPI.ts index 1fb371f9..e6fcdf0c 100644 --- a/frontend/src/apis/authAPI.ts +++ b/frontend/src/apis/authAPI.ts @@ -1,4 +1,4 @@ -import { UserInfo } from '@/types'; +import type { UserInfo } from '@/types'; //TODO 일단 관심사 별로 API.ts 로 나누어 놓은 것인데, // 이 또한 추후에 논의해보아야 할듯 해요 ~ @버건디 diff --git a/frontend/src/components/guide/HowToFork.tsx b/frontend/src/components/guide/HowToFork.tsx new file mode 100644 index 00000000..9b80b2dd --- /dev/null +++ b/frontend/src/components/guide/HowToFork.tsx @@ -0,0 +1,142 @@ +// TODO 안에 컨텐츠가 어떻게 들어갈지 몰라서 일단 목파일 넣어놓습니다. @버건디 + +const HowToFork = () => { + return ( +
+ + +

Lorem Ipsum

+

+ "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci + velit..." +

+
+ "There is no one who loves pain itself, who seeks after it and wants to have it, simply + because it is pain..." +
+ +
+ +
+
+
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut convallis enim elementum + nulla posuere eleifend. Vestibulum sit amet venenatis libero. Suspendisse porttitor + sit amet ligula et placerat. Pellentesque habitant morbi tristique senectus et netus + et malesuada fames ac turpis egestas. Cras dignissim non urna in fermentum. Vivamus + egestas odio et rutrum ultricies. Aliquam gravida, lectus non tristique commodo, + ligula eros ultrices turpis, quis malesuada quam lectus nec metus. Pellentesque + habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. +

+

+ Nullam viverra eu augue id aliquet. Nulla sit amet lobortis ipsum. Suspendisse + suscipit viverra ante vitae semper. Aliquam enim felis, iaculis a posuere pharetra, + lacinia eu tortor. Aliquam posuere suscipit cursus. Vivamus semper volutpat urna, eu + dignissim lectus ullamcorper eget. Duis placerat molestie sapien, nec commodo felis + vulputate at. +

+

+ Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos + himenaeos. In vehicula, est id efficitur porta, orci velit ultrices enim, a dignissim + augue ligula at felis. Phasellus elementum nibh felis, ac viverra nunc elementum at. + Phasellus eu pellentesque libero. In convallis erat diam, vitae hendrerit ante posuere + a. In hac habitasse platea dictumst. Pellentesque at lectus eu nulla ullamcorper + ornare ut et urna. +

+

+ Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia + curae; Nullam vulputate et nisi et iaculis. Donec iaculis ut nibh venenatis efficitur. + Nunc lorem justo, sagittis vitae lacus eu, tincidunt placerat justo. Aenean semper + nibh at sodales convallis. Duis ut iaculis arcu. Proin commodo rutrum nisl eget + venenatis. Quisque sodales magna sit amet turpis tristique faucibus. Aenean a porta + ex, at feugiat risus. Nulla tincidunt consectetur pellentesque. Integer semper, diam + quis porttitor porttitor, elit nunc ullamcorper dui, quis consectetur metus enim ac + nunc. Nulla egestas justo at auctor fringilla. +

+

+ Mauris pharetra ut eros quis euismod. Sed efficitur erat id dui mollis venenatis. Duis + blandit, nisl vel venenatis fermentum, justo purus consectetur nibh, quis rutrum mi + eros id est. Sed mattis commodo leo. Donec lacinia mi sed laoreet imperdiet. Mauris ac + ex congue, consequat arcu eu, egestas metus. Duis imperdiet diam quis tellus pretium + ultricies. Quisque sagittis sit amet nisi id vulputate. Interdum et malesuada fames ac + ante ipsum primis in faucibus. Maecenas sagittis enim id porta fringilla. Nunc vel + faucibus massa. Nam rhoncus ex quis augue viverra, non aliquet mauris imperdiet. Donec + tincidunt enim sed justo posuere tempus. Suspendisse justo velit, vehicula id tellus + eu, condimentum posuere nibh. +

+
+
+ Generated 5 paragraphs, 397 words, 2706 bytes of{' '} + + Lorem Ipsum + +
+
+
+ +
+ +
+ + help@lipsum.com + +
+ + Privacy Policy + + · +
+
+ ); +}; + +export default HowToFork; diff --git a/frontend/src/components/tab/Tab.styled.ts b/frontend/src/components/tab/Tab.styled.ts new file mode 100644 index 00000000..57f4a5a5 --- /dev/null +++ b/frontend/src/components/tab/Tab.styled.ts @@ -0,0 +1,64 @@ +import { keyframes, styled } from 'styled-components'; + +export const TabPageContainer = styled.div` + width: 70rem; + height: 100%; + margin: 0 auto; + padding-top: 3rem; +`; + +export const TabListContainer = styled.div` + display: flex; + justify-content: center; +`; + +const fadeIn = keyframes` + from { + opacity: 0; + } + to { + opacity: 1; + } +`; + +export const CurrentContentContainer = styled.div` + word-break: break-all; + margin-top: 2rem; + animation: ${fadeIn} 0.3s ease-out; +`; + +export const TabContainer = styled.div<{ isSelected: boolean }>` + padding: 10px 20px; + cursor: pointer; + background: ${({ isSelected }) => (isSelected ? 'var(--primary-700)' : 'var(--grey-700)')}; + font-size: 1.5rem; + color: white; + border-radius: 1rem 1rem 0 0; + margin: 0 5px; + transition: + background 0.3s, + box-shadow 0.3s; + + ${({ isSelected }) => + isSelected && + ` + box-shadow: 0 0.4rem 0.8rem rgba(0, 0, 0, 0.2); + animation: tabSelected 0.3s ease-out; + `} + + &:hover { + background: ${({ isSelected }) => (isSelected ? 'var(--primary-500)' : 'var(--grey-500)')}; + } + + @keyframes tabSelected { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.1); + } + 100% { + transform: scale(1); + } + } +`; diff --git a/frontend/src/components/tab/TabCurrentContent.tsx b/frontend/src/components/tab/TabCurrentContent.tsx new file mode 100644 index 00000000..e2b7fd08 --- /dev/null +++ b/frontend/src/components/tab/TabCurrentContent.tsx @@ -0,0 +1,8 @@ +import { useTabs } from '@/hooks/useTabs'; +import * as S from './Tab.styled'; + +export default function TabCurrentContent() { + const { selectedIndex, tabList } = useTabs(); + + return {tabList[selectedIndex].content}; +} diff --git a/frontend/src/components/tab/TabHeader.tsx b/frontend/src/components/tab/TabHeader.tsx new file mode 100644 index 00000000..421b92cd --- /dev/null +++ b/frontend/src/components/tab/TabHeader.tsx @@ -0,0 +1,16 @@ +import { useTabs } from '@/hooks/useTabs'; +import type { PropsWithChildren } from 'react'; +import * as S from './Tab.styled'; + +interface TabProps extends PropsWithChildren { + index: number; +} + +export default function TabHeader({ index, children }: TabProps) { + const { selectedIndex, handleSelectedIndex } = useTabs(); + return ( + handleSelectedIndex(index)}> + {children} + + ); +} diff --git a/frontend/src/components/tab/TabListWrapper.tsx b/frontend/src/components/tab/TabListWrapper.tsx new file mode 100644 index 00000000..4be339e6 --- /dev/null +++ b/frontend/src/components/tab/TabListWrapper.tsx @@ -0,0 +1,6 @@ +import type { PropsWithChildren } from 'react'; +import * as S from './Tab.styled'; + +export default function TabListWrapper({ children }: PropsWithChildren) { + return {children}; +} diff --git a/frontend/src/components/tab/Tabs.tsx b/frontend/src/components/tab/Tabs.tsx new file mode 100644 index 00000000..e7794435 --- /dev/null +++ b/frontend/src/components/tab/Tabs.tsx @@ -0,0 +1,30 @@ +import { TabsContext } from '@/contexts/TabsContext'; +import { useState } from 'react'; +import type { PropsWithChildren } from 'react'; +import TabHeader from './TabHeader'; +import TabListWrapper from './TabListWrapper'; +import TabCurrentContent from './TabCurrentContent'; +import * as S from './Tab.styled'; +import type { TabInfo } from '@/types'; + +interface TabsProps extends PropsWithChildren { + tabList: TabInfo[]; +} + +export default function Tabs({ children, tabList }: TabsProps) { + const [selectedIndex, setSelectedIndex] = useState(0); + + const handleSelectedIndex = (index: number) => { + setSelectedIndex(index); + }; + + return ( + + {children} + + ); +} + +Tabs.ListWrapper = TabListWrapper; +Tabs.TabHeader = TabHeader; +Tabs.CurrentContent = TabCurrentContent; diff --git a/frontend/src/components/userProfile/UserProfileDescription.tsx b/frontend/src/components/userProfile/UserProfileDescription.tsx index c58780ff..d406dcf2 100644 --- a/frontend/src/components/userProfile/UserProfileDescription.tsx +++ b/frontend/src/components/userProfile/UserProfileDescription.tsx @@ -1,5 +1,6 @@ -import { ChangeEvent, useState } from 'react'; -import { UserInfo } from '@/types'; +import { useState } from 'react'; +import type { ChangeEvent } from 'react'; +import type { UserInfo } from '@/types'; import * as S from './UserProfile.styled'; type UserProfileDescription = Pick; diff --git a/frontend/src/components/userProfile/UserProfileEmail.tsx b/frontend/src/components/userProfile/UserProfileEmail.tsx index ca15c5e1..72277b03 100644 --- a/frontend/src/components/userProfile/UserProfileEmail.tsx +++ b/frontend/src/components/userProfile/UserProfileEmail.tsx @@ -1,4 +1,4 @@ -import { UserInfo } from '@/types'; +import type { UserInfo } from '@/types'; import * as S from './UserProfile.styled'; type UserProfileEmailProps = Pick; diff --git a/frontend/src/components/userProfile/UserProfileImage.tsx b/frontend/src/components/userProfile/UserProfileImage.tsx index bd1b7f59..dbfa4863 100644 --- a/frontend/src/components/userProfile/UserProfileImage.tsx +++ b/frontend/src/components/userProfile/UserProfileImage.tsx @@ -1,5 +1,5 @@ import * as S from './UserProfile.styled'; -import { UserInfo } from '@/types'; +import type { UserInfo } from '@/types'; //TODO UserInfo 타입에서 재사용할 수 있을것 같아 Pick으로 선언해놓습니다. // 인터페이스로 추후에 변경 해도 상관 없어요! @버건디 diff --git a/frontend/src/components/userProfile/UserProfileName.tsx b/frontend/src/components/userProfile/UserProfileName.tsx index 8caab759..de5303d9 100644 --- a/frontend/src/components/userProfile/UserProfileName.tsx +++ b/frontend/src/components/userProfile/UserProfileName.tsx @@ -1,4 +1,4 @@ -import { UserInfo } from '@/types'; +import type { UserInfo } from '@/types'; import * as S from './UserProfile.styled'; type UserProfileNameProps = Pick; diff --git a/frontend/src/constants/messages.ts b/frontend/src/constants/messages.ts new file mode 100644 index 00000000..3c009f04 --- /dev/null +++ b/frontend/src/constants/messages.ts @@ -0,0 +1,3 @@ +export const ERROR_MESSAGE = { + not_defined_context: '컨텍스트가 정의 되지 않았어요!', +} as const; diff --git a/frontend/src/constants/routes.ts b/frontend/src/constants/routes.ts index 4604c0b8..f907d806 100644 --- a/frontend/src/constants/routes.ts +++ b/frontend/src/constants/routes.ts @@ -3,4 +3,5 @@ export const ROUTES = { submit: '/submit/:id', missionDetail: '/missions/:id', profile: '/profile', + guide: '/guide', } as const; diff --git a/frontend/src/contexts/TabsContext.ts b/frontend/src/contexts/TabsContext.ts new file mode 100644 index 00000000..27088384 --- /dev/null +++ b/frontend/src/contexts/TabsContext.ts @@ -0,0 +1,10 @@ +import type { TabInfo } from '@/types'; +import { createContext } from 'react'; + +export interface TabsContextProps { + selectedIndex: number; + handleSelectedIndex: (index: number) => void; + tabList: TabInfo[]; +} + +export const TabsContext = createContext(undefined); diff --git a/frontend/src/hooks/useTabs.ts b/frontend/src/hooks/useTabs.ts new file mode 100644 index 00000000..3c3b7656 --- /dev/null +++ b/frontend/src/hooks/useTabs.ts @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { ERROR_MESSAGE } from '@/constants/messages'; +import { TabsContext } from '@/contexts/TabsContext'; +import type { TabsContextProps } from '@/contexts/TabsContext'; + +export const useTabs = (): TabsContextProps => { + const context = useContext(TabsContext); + if (!context) { + throw new Error(ERROR_MESSAGE.not_defined_context); + } + return context; +}; diff --git a/frontend/src/hooks/useUserInfo.ts b/frontend/src/hooks/useUserInfo.ts index 0087b056..1b999beb 100644 --- a/frontend/src/hooks/useUserInfo.ts +++ b/frontend/src/hooks/useUserInfo.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; import { getUserInfo } from '@/apis/authAPI'; -import { UserInfo } from '@/types'; +import type { UserInfo } from '@/types'; const useUserInfo = () => { //TODO 아직 토큰 로직에 관한 부분이 미정이라서 diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 79fa8457..a2b3c9b4 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -9,6 +9,7 @@ import MissionDetailPage from './pages/MissionDetailPage'; import MissionListPage from './pages/MissionListPage'; import MissionSubmitPage from './pages/MissionSubmitPage'; import UserProfilePage from './pages/UserProfilePage'; +import GuidePage from './pages/GuidePage'; const queryClient = new QueryClient(); @@ -48,6 +49,14 @@ const routes = [ ), }, + { + path: ROUTES.guide, + element: ( + + + + ), + }, ]; const router = createBrowserRouter(routes, { diff --git a/frontend/src/pages/GuidePage.tsx b/frontend/src/pages/GuidePage.tsx new file mode 100644 index 00000000..cfb3865e --- /dev/null +++ b/frontend/src/pages/GuidePage.tsx @@ -0,0 +1,35 @@ +import Tabs from '@/components/tab/Tabs'; +import HowToFork from '@/components/guide/HowToFork'; +import type { TabInfo } from '@/types'; + +//TODO 여기서만 쓰일 데이터 같아서 일단 위에 선언해놓습니다. @버건디 + +const TAB_LIST: TabInfo[] = [ + { + name: 'step1(포크 클론 방법)', + content: , + }, + { + name: 'step2(PR 보내기 / 제출)', + content:
step2(PR 보내기 / 제출)
, + }, + { + name: 'step3(리뷰 방법)', + content:
step3(리뷰방법)
, + }, +]; + +export default function GuidePage() { + return ( + + + {TAB_LIST.map((tab, index) => ( + + {tab.name} + + ))} + + + + ); +} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 0e2a4270..3fe7beed 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -1,6 +1,8 @@ //TODO 임시로 유저 정보에 대한 타입을 정의합니다. // 추후에 깃허브 로그인 기능과 연동했을때 수정 됩니다 @버건디 +import type { ReactNode } from 'react'; + export interface UserInfo { id: number; login: string; @@ -9,3 +11,8 @@ export interface UserInfo { name: string; description?: string; } + +export interface TabInfo { + name: string; + content: ReactNode; +} From a9a54cb7252968ca4c029ea8bf86a672e260285e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=88=98=EB=B9=88?= Date: Fri, 19 Jul 2024 10:22:56 +0900 Subject: [PATCH 3/5] =?UTF-8?q?CD=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(Issue#68)=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: self hosted runner 적용 * feat: CD 스크립트 작성 및 Self Hosted Runner 적용 * feat: 프론트 CI 적용 * fix: 프론트 CI 스크립트 오류 수정 * fix: 프론트 CI 스크립트 실패 시 스크립트 중지되지 않도록 수정 * test: 스크립트 테스트 * fix: 프론트 CI 결과 슬랙 메세지가 여러번 발송되는 오류 수정 --- .github/workflows/cd.yml | 67 +++++++++++++++++++++++++ .github/workflows/front_ci.yml | 91 ++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 .github/workflows/cd.yml create mode 100644 .github/workflows/front_ci.yml diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 00000000..6071fbf1 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,67 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle + +name: Gradle Package + +on: + pull_request: + types: [ closed ] + branches: [ 'main' ] + +jobs: + build: + runs-on: self-hosted + permissions: + contents: read + packages: write + actions: write + + steps: + - uses: actions/checkout@v4 + - name: Early exit + if: ${{ !contains(github.event.pull_request.labels.*.name, '백엔드') }} + run: | + gh run cancel ${{ github.run_id }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/setup-java@v4 + with: + distribution: 'corretto' + java-version: '21' + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }} # location for the settings.xml file + + - name: Setup Gradle + run: chmod +x ./backend/gradlew + + - name: Build with Gradle + continue-on-error: true + id: gradle_build + run: | + cd backend + ./gradlew build + + - name: slack mention + uses: slackapi/slack-github-action@v1.24.0 + with: + channel-id: ${{ secrets.ISSUE_CHANNEL }} + payload: | + { + "text": "pr 머지", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "<@channel> pr이 main에 머지되었습니다. \n • PR 링크: <${{ github.event.pull_request.html_url }}|${{ github.event.pull_request.title }}>" + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.BOT_TOKEN }} + diff --git a/.github/workflows/front_ci.yml b/.github/workflows/front_ci.yml new file mode 100644 index 00000000..4a846e40 --- /dev/null +++ b/.github/workflows/front_ci.yml @@ -0,0 +1,91 @@ +name: CI + +on: + pull_request: + types: [ opened, reopened, synchronize ] + branches: [ 'main' ] + +jobs: + build-and-test: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + actions: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Early exit + if: ${{ contains(github.event.pull_request.labels.*.name, '백엔드') }} + run: | + gh run cancel ${{ github.run_id }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.15.1' + + - name: Install dependencies + run: | + cd frontend + npm install + + - name: Run lint + continue-on-error: true + id: npm_run_lint + run: | + cd frontend + npm run lint + + - name: Run build + continue-on-error: true + id: npm_run_build + run: | + pwd + cd frontend + npm run build + + - name: Run test + continue-on-error: true + id: npm_run_test + run: | + cd frontend + npm run test + + - name: Get teamMember List + id: teamMembers + uses: actions/github-script@v6 + with: + script: | + const fs = require('fs'); + const workers = JSON.parse(fs.readFileSync('.github/workflows/teamMember.json')); + const mention = context.payload.pull_request.assignees.map((user) => { + const login = user.login; + const mappedValue = workers[login]; + return mappedValue ? `<@${mappedValue}>` : `No mapping found for ${login}`; + }) + return mention.join(', '); + + - name: slack mention + uses: slackapi/slack-github-action@v1.24.0 + with: + channel-id: ${{ secrets.ISSUE_CHANNEL }} + payload: | + { + "text": "pr 테스트 결과", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "pr 테스트 \n lint : ${{ steps.npm_run_lint.outcome}} \n build : ${{ steps.npm_run_build.outcome}} \n test : ${{ steps.npm_run_test.outcome}} \n • 링크: <${{ github.event.pull_request.html_url }}|${{ github.event.pull_request.title }}> \n • pr 담당자: \${{ steps.teamMembers.outputs.result }} + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.BOT_TOKEN }} From b40bf92ab3f0f27617a83be04a128a8f6af08fdc Mon Sep 17 00:00:00 2001 From: JEON TAEHEON Date: Fri, 19 Jul 2024 10:34:30 +0900 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20=ED=94=84=EB=A1=A0=ED=8A=B8=20ci?= =?UTF-8?q?=20=ED=99=98=EA=B2=BD=20=EA=B5=AC=EC=B6=95=20(#72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/guide/HowToFork.tsx | 1 + frontend/src/utils/__tests__/add.test.ts | 10 ++++++++++ frontend/src/utils/add.ts | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 frontend/src/utils/__tests__/add.test.ts create mode 100644 frontend/src/utils/add.ts diff --git a/frontend/src/components/guide/HowToFork.tsx b/frontend/src/components/guide/HowToFork.tsx index 9b80b2dd..71b42817 100644 --- a/frontend/src/components/guide/HowToFork.tsx +++ b/frontend/src/components/guide/HowToFork.tsx @@ -1,5 +1,6 @@ // TODO 안에 컨텐츠가 어떻게 들어갈지 몰라서 일단 목파일 넣어놓습니다. @버건디 +/* eslint-disable react/no-unescaped-entities */ const HowToFork = () => { return (
diff --git a/frontend/src/utils/__tests__/add.test.ts b/frontend/src/utils/__tests__/add.test.ts new file mode 100644 index 00000000..e42c8976 --- /dev/null +++ b/frontend/src/utils/__tests__/add.test.ts @@ -0,0 +1,10 @@ +import add from '../add'; + +// 프론트 ci 환경 구축을 위한 임시 테스트 코드입니다. +// 후에 도메인 로직에 대한 테스트 코드 작성이 된다면 지워도 괜찮습니다. +describe('ci 구축을 위한 테스트 코드 작성 ', () => { + it('ci 환경 구축을 위한 작성한다.', () => { + const result = add(2, 3); + expect(result).toBe(5); + }); +}); diff --git a/frontend/src/utils/add.ts b/frontend/src/utils/add.ts new file mode 100644 index 00000000..cba3fdd9 --- /dev/null +++ b/frontend/src/utils/add.ts @@ -0,0 +1,5 @@ +const add = (a: number, b: number) => { + return a + b; +}; + +export default add; From d9b81aceb4c3c3fc4ffc6164a801230fd32a4253 Mon Sep 17 00:00:00 2001 From: Haneul Lee Date: Fri, 19 Jul 2024 11:56:28 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=A7=A4=EC=B9=AD=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84(issue=20#33)=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: pair 패키지 위치 변경 * feat: 아직 매칭안된 제출 조회 기능 구현 * feat: 매칭 가능 여부 판단 기능 구현 * fix: submission 필드명 변경 * fix: 매칭안된 제출 조회 쿼리 수정 * feat: 제출 주인 동일 여부 판단 기능 구현 * feat: 페어 매칭 기능 구현 * refactor: pair status enum으로 개선 * fix: 테스트에서 멤버 id 사용하지 않도록 변경 * feat: 같은 제출로 페어 생성 검증 기능 구현 * fix: import 문 변경 * style: 메서드 선언 순서 변경 * fix: 불필요한 getter 제거 * refactor: no arg 생성자 접근자 변경 * feat: 상태 description 작성 * feat: 멤버 주 생성자 생성 * fix: 실패하는 테스트 성공하도록 변경 - 스키마 변경 사항을 반영했다. - 엔티티 생성자 변경 사항을 반영했다. - 데이터 초기화 코드를 적용했다. * fix: mymission 부생성자 제거 * feat: 제출 시점에 매칭 기능을 연동 * test: 누락된 hasMessage 추가 * fix: 누락된 트랜잭션 어노테이션 추가 * refactor: 테스트용 객체 생성 메서드 분리 * refactor: 매칭 시도 코드 메서드 추출 * refactor: inner 클래스 내부 멤버 생성 메서드 분리 * style: 불필요한 개행 제거 --- .../src/main/java/develup/member/Member.java | 23 +++ backend/src/main/java/develup/pair/Pair.java | 52 ------ .../java/develup/submission/MyMission.java | 10 +- .../develup/submission/MyMissionResponse.java | 2 +- .../main/java/develup/submission/Pair.java | 52 ++++++ .../{pair => submission}/PairRepository.java | 14 +- .../java/develup/submission/PairService.java | 65 +++++++ .../java/develup/submission/PairStatus.java | 19 ++ .../java/develup/submission/Submission.java | 27 +++ .../develup/submission/SubmissionApi.java | 5 +- .../submission/SubmissionRepository.java | 17 ++ .../develup/submission/SubmissionService.java | 13 +- backend/src/main/resources/data.sql | 24 +-- .../test/java/develup/ApplicationTests.java | 13 -- .../develup/mission/MissionServiceTest.java | 1 - .../java/develup/pair/PairRepositoryTest.java | 3 +- .../develup/submission/PairServiceTest.java | 173 ++++++++++++++++++ .../java/develup/submission/PairTest.java | 19 ++ .../submission/SubmissionRepositoryTest.java | 76 ++++++++ .../submission/SubmissionServiceTest.java | 21 ++- .../develup/submission/SubmissionTest.java | 52 ++++++ backend/src/test/resources/clean_data.sql | 6 - backend/src/test/resources/mymissions.sql | 42 +++-- 23 files changed, 605 insertions(+), 124 deletions(-) delete mode 100644 backend/src/main/java/develup/pair/Pair.java create mode 100644 backend/src/main/java/develup/submission/Pair.java rename backend/src/main/java/develup/{pair => submission}/PairRepository.java (61%) create mode 100644 backend/src/main/java/develup/submission/PairService.java create mode 100644 backend/src/main/java/develup/submission/PairStatus.java delete mode 100644 backend/src/test/java/develup/ApplicationTests.java create mode 100644 backend/src/test/java/develup/submission/PairServiceTest.java create mode 100644 backend/src/test/java/develup/submission/PairTest.java create mode 100644 backend/src/test/java/develup/submission/SubmissionRepositoryTest.java create mode 100644 backend/src/test/java/develup/submission/SubmissionTest.java diff --git a/backend/src/main/java/develup/member/Member.java b/backend/src/main/java/develup/member/Member.java index 53598105..147976b1 100644 --- a/backend/src/main/java/develup/member/Member.java +++ b/backend/src/main/java/develup/member/Member.java @@ -1,5 +1,6 @@ package develup.member; +import java.util.Objects; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -35,6 +36,11 @@ protected Member() { } public Member(String email, Provider provider, Long socialId, String name, String imageUrl) { + this(null, email, provider, socialId, name, imageUrl); + } + + public Member(Long id, String email, Provider provider, Long socialId, String name, String imageUrl) { + this.id = id; this.email = email; this.provider = provider; this.socialId = socialId; @@ -65,4 +71,21 @@ public String getName() { public String getImageUrl() { return imageUrl; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Member member)) { + return false; + } + + return this.getId() != null && Objects.equals(getId(), member.getId()); + } + + @Override + public int hashCode() { + return Objects.hash(getId()); + } } diff --git a/backend/src/main/java/develup/pair/Pair.java b/backend/src/main/java/develup/pair/Pair.java deleted file mode 100644 index 20e10c7c..00000000 --- a/backend/src/main/java/develup/pair/Pair.java +++ /dev/null @@ -1,52 +0,0 @@ -package develup.pair; - -import develup.submission.Submission; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToOne; - -@Entity -public class Pair { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @OneToOne - @JoinColumn(nullable = false) - private Submission submission1; - - @OneToOne - @JoinColumn(nullable = false) - private Submission submission2; - - @Column(nullable = false) - private String status; - - protected Pair() { - } - - public Long getId() { - return id; - } - - public Submission getSubmission1() { - return submission1; - } - - public Submission getSubmission2() { - return submission2; - } - - public String getStatus() { - return status; - } - - public String getOtherUrl() { - return submission2.getUrl(); - } -} diff --git a/backend/src/main/java/develup/submission/MyMission.java b/backend/src/main/java/develup/submission/MyMission.java index 5fa8be94..81da7ea1 100644 --- a/backend/src/main/java/develup/submission/MyMission.java +++ b/backend/src/main/java/develup/submission/MyMission.java @@ -8,9 +8,9 @@ public class MyMission { private final Mission mission; private final String myPrLink; private final String pairPrLink; - private final String status; + private final PairStatus status; - public MyMission(Long id, Mission mission, String myPrLink, String pairPrLink, String status) { + public MyMission(Long id, Mission mission, String myPrLink, String pairPrLink, PairStatus status) { this.id = id; this.mission = mission; this.myPrLink = myPrLink; @@ -19,7 +19,7 @@ public MyMission(Long id, Mission mission, String myPrLink, String pairPrLink, S } public static MyMission waitPairMatching(Submission submission) { - return new MyMission(submission.getId(), submission.getMission(), submission.getUrl(), null, "매칭 대기"); + return new MyMission(submission.getId(), submission.getMission(), submission.getUrl(), null, PairStatus.WAITING); } public Long getId() { @@ -38,7 +38,7 @@ public String getPairPrLink() { return pairPrLink; } - public String getStatus() { - return status; + public String getStatusDescription() { + return status.getDescription(); } } diff --git a/backend/src/main/java/develup/submission/MyMissionResponse.java b/backend/src/main/java/develup/submission/MyMissionResponse.java index 26e55723..d6f6a64b 100644 --- a/backend/src/main/java/develup/submission/MyMissionResponse.java +++ b/backend/src/main/java/develup/submission/MyMissionResponse.java @@ -16,7 +16,7 @@ public static MyMissionResponse from(MyMission myMission) { MissionResponse.from(myMission.getMission()), myMission.getMyPrLink(), myMission.getPairPrLink(), - myMission.getStatus() + myMission.getStatusDescription() ); } } diff --git a/backend/src/main/java/develup/submission/Pair.java b/backend/src/main/java/develup/submission/Pair.java new file mode 100644 index 00000000..b806988f --- /dev/null +++ b/backend/src/main/java/develup/submission/Pair.java @@ -0,0 +1,52 @@ +package develup.submission; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; + +@Entity +public class Pair { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne + @JoinColumn(name = "main_submission", nullable = false) + private Submission main; + + @OneToOne + @JoinColumn(name = "pair_submission", nullable = false) + private Submission other; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private PairStatus status; + + protected Pair() { + } + + public Pair(Submission main, Submission other, PairStatus status) { + if (main.equals(other)) { + throw new IllegalArgumentException("같은 제출끼리 페어가 될 수 없습니다."); + } + + this.main = main; + this.other = other; + this.status = status; + } + + public Long getId() { + return id; + } + + public Submission getMain() { + return main; + } +} diff --git a/backend/src/main/java/develup/pair/PairRepository.java b/backend/src/main/java/develup/submission/PairRepository.java similarity index 61% rename from backend/src/main/java/develup/pair/PairRepository.java rename to backend/src/main/java/develup/submission/PairRepository.java index c4896e8e..83c0cfe5 100644 --- a/backend/src/main/java/develup/pair/PairRepository.java +++ b/backend/src/main/java/develup/submission/PairRepository.java @@ -1,8 +1,6 @@ -package develup.pair; +package develup.submission; import java.util.Optional; -import develup.submission.MyMission; -import develup.submission.Submission; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -10,15 +8,15 @@ public interface PairRepository extends JpaRepository { @Query(""" SELECT new develup.submission.MyMission( - p.submission1.id, - p.submission1.mission, - p.submission1.url, - p.submission2.url, + p.main.id, + p.main.mission, + p.main.url, + p.other.url, p.status ) FROM Pair p WHERE - p.submission1 = :submission + p.main = :submission """) Optional findMyMissionBySubmission(Submission submission); } diff --git a/backend/src/main/java/develup/submission/PairService.java b/backend/src/main/java/develup/submission/PairService.java new file mode 100644 index 00000000..4ca625af --- /dev/null +++ b/backend/src/main/java/develup/submission/PairService.java @@ -0,0 +1,65 @@ +package develup.submission; + +import java.util.List; +import java.util.Optional; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +class PairService { + + private final SubmissionRepository submissionRepository; + private final PairRepository pairRepository; + + public PairService(SubmissionRepository submissionRepository, PairRepository pairRepository) { + this.submissionRepository = submissionRepository; + this.pairRepository = pairRepository; + } + + public boolean canMatch(Submission submission) { + return findCanMatchSubmission(submission).isPresent(); + } + + public void match(Submission submission) { + validateSubmitted(submission); + validateAlreadyMatched(submission); + matchWithOtherSubmission(submission); + } + + private void validateSubmitted(Submission submission) { + boolean isSubmitted = submissionRepository.existsById(submission.getId()); + + if (!isSubmitted) { + throw new IllegalArgumentException("아직 제출되지 않아 매칭이 불가능합니다."); + } + } + + private void validateAlreadyMatched(Submission submission) { + List nonMatchedSubmissions = submissionRepository + .findNonMatchedSubmissions(submission.getMission()); + + if (!nonMatchedSubmissions.contains(submission)) { + throw new IllegalStateException("이미 매칭된 제출입니다."); + } + } + + private void matchWithOtherSubmission(Submission submission) { + Submission other = findCanMatchSubmission(submission) + .orElseThrow(() -> new IllegalStateException("매칭할 제출이 존재하지 않습니다.")); + Pair mainPair = new Pair(submission, other, PairStatus.IN_PROGRESS); + Pair otherPair = new Pair(other, submission, PairStatus.IN_PROGRESS); + + pairRepository.save(mainPair); + pairRepository.save(otherPair); + } + + private Optional findCanMatchSubmission(Submission submission) { + List nonMatchedSubmissions = submissionRepository + .findNonMatchedSubmissions(submission.getMission()); + + return nonMatchedSubmissions.stream() + .filter(it -> it.isNotSameOwner(submission)) + .findFirst(); + } +} diff --git a/backend/src/main/java/develup/submission/PairStatus.java b/backend/src/main/java/develup/submission/PairStatus.java new file mode 100644 index 00000000..ca69c70a --- /dev/null +++ b/backend/src/main/java/develup/submission/PairStatus.java @@ -0,0 +1,19 @@ +package develup.submission; + +public enum PairStatus { + + WAITING("매칭 대기"), + IN_PROGRESS("진행 중"), + REVIEW_FINISHED("리뷰 완료"), + ALL_FINISHED("완료"); + + private final String description; + + PairStatus(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/backend/src/main/java/develup/submission/Submission.java b/backend/src/main/java/develup/submission/Submission.java index d946f83b..9d892f44 100644 --- a/backend/src/main/java/develup/submission/Submission.java +++ b/backend/src/main/java/develup/submission/Submission.java @@ -1,5 +1,6 @@ package develup.submission; +import java.util.Objects; import develup.member.Member; import develup.mission.Mission; import jakarta.persistence.Column; @@ -34,12 +35,21 @@ protected Submission() { } public Submission(String url, String comment, Member member, Mission mission) { + this(null, url, comment, member, mission); + } + + public Submission(Long id, String url, String comment, Member member, Mission mission) { + this.id = id; this.url = url; this.comment = comment; this.member = member; this.mission = mission; } + public boolean isNotSameOwner(Submission other) { + return !this.member.equals(other.member); + } + public Long getId() { return id; } @@ -67,4 +77,21 @@ public Long getMemberId() { public Long getMissionId() { return mission.getId(); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Submission submission)) { + return false; + } + + return this.getId() != null && Objects.equals(getId(), submission.getId()); + } + + @Override + public int hashCode() { + return Objects.hash(getId()); + } } diff --git a/backend/src/main/java/develup/submission/SubmissionApi.java b/backend/src/main/java/develup/submission/SubmissionApi.java index 8e56dbd2..a33a634e 100644 --- a/backend/src/main/java/develup/submission/SubmissionApi.java +++ b/backend/src/main/java/develup/submission/SubmissionApi.java @@ -3,6 +3,7 @@ import java.net.URI; import java.util.List; import develup.member.Member; +import develup.member.Provider; import develup.support.ApiResponse; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -21,14 +22,14 @@ public SubmissionApi(SubmissionService submissionService) { @GetMapping("/submissions") public ResponseEntity>> getMyMissions() { - Member member = new Member(1L); + Member member = new Member(1L, "email", Provider.GITHUB, 1234L, "name", "image"); return ResponseEntity.ok(new ApiResponse<>(submissionService.getMyMissions(member))); } @PostMapping("/submissions") public ResponseEntity> postSubmission(@RequestBody CreateSubmissionRequest request) { - Member member = new Member(1L); + Member member = new Member(1L, "email", Provider.GITHUB, 1234L, "name", "image"); SubmissionResponse response = submissionService.submit(member, request); return ResponseEntity.created(URI.create("/submissions/" + response.id())) diff --git a/backend/src/main/java/develup/submission/SubmissionRepository.java b/backend/src/main/java/develup/submission/SubmissionRepository.java index 9039ad68..f852157b 100644 --- a/backend/src/main/java/develup/submission/SubmissionRepository.java +++ b/backend/src/main/java/develup/submission/SubmissionRepository.java @@ -1,9 +1,26 @@ package develup.submission; import java.util.List; +import develup.mission.Mission; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; public interface SubmissionRepository extends JpaRepository { List findAllByMember_IdOrderByIdDesc(Long id); + + @Query(""" + SELECT s + FROM Submission s + WHERE + s.mission = :mission AND + NOT EXISTS ( + SELECT 1 + FROM Pair p + WHERE + p.main = s OR + p.other = s + ) + """) + List findNonMatchedSubmissions(Mission mission); } diff --git a/backend/src/main/java/develup/submission/SubmissionService.java b/backend/src/main/java/develup/submission/SubmissionService.java index c02b60d4..8d9449ba 100644 --- a/backend/src/main/java/develup/submission/SubmissionService.java +++ b/backend/src/main/java/develup/submission/SubmissionService.java @@ -4,7 +4,6 @@ import develup.member.Member; import develup.mission.Mission; import develup.mission.MissionRepository; -import develup.pair.PairRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,25 +14,35 @@ class SubmissionService { private final MissionRepository missionRepository; private final SubmissionRepository submissionRepository; private final PairRepository pairRepository; + private final PairService pairService; public SubmissionService( MissionRepository missionRepository, SubmissionRepository submissionRepository, - PairRepository pairRepository + PairRepository pairRepository, + PairService pairService ) { this.missionRepository = missionRepository; this.submissionRepository = submissionRepository; this.pairRepository = pairRepository; + this.pairService = pairService; } public SubmissionResponse submit(Member member, CreateSubmissionRequest request) { Mission mission = missionRepository.findById(request.missionId()) .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 미션입니다.")); Submission newSubmission = submissionRepository.save(request.toSubmission(member, mission)); + tryMatch(newSubmission); return SubmissionResponse.from(newSubmission); } + private void tryMatch(Submission newSubmission) { + if (pairService.canMatch(newSubmission)) { + pairService.match(newSubmission); + } + } + public List getMyMissions(Member member) { List submissions = submissionRepository.findAllByMember_IdOrderByIdDesc(member.getId()); diff --git a/backend/src/main/resources/data.sql b/backend/src/main/resources/data.sql index cf8d2572..77218ff3 100644 --- a/backend/src/main/resources/data.sql +++ b/backend/src/main/resources/data.sql @@ -13,11 +13,11 @@ VALUES ('숫자 추리 게임', 'JAVA', 'https://raw.githubusercontent.com/devel 'https://private-user-images.githubusercontent.com/80797824/348618796-753cae7d-779e-4847-b1b5-31ab9ff2d1cb.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjEwNDA3NTIsIm5iZiI6MTcyMTA0MDQ1MiwicGF0aCI6Ii84MDc5NzgyNC8zNDg2MTg3OTYtNzUzY2FlN2QtNzc5ZS00ODQ3LWIxYjUtMzFhYjlmZjJkMWNiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MTUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzE1VDEwNDczMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTE2ODM0ZjI2YWMxMGEzNGUzNDk2MjVlMTdlMWRkNjI4NjkyMWUxMTQzZDdhZjRlYjM2NTgzNjE0MTQ0YTZlZmQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.yk09dRTA7LefjF0uTyYNkCgBRJcLizoKSJ8Otq7J1Gs', 'https://github.com/develup-mission/java-guessing-number'); -INSERT INTO member (id) -VALUES (1); +INSERT INTO member (id, email, provider, social_id, name, image_url) +VALUES (1, 'test1@gmail.com', 'GITHUB', '1234', '구름', 'www.naver.com'); -INSERT INTO member (id) -VALUES (2); +INSERT INTO member (id, email, provider, social_id, name, image_url) +VALUES (2, 'test1@gmail.com', 'GITHUB', '1234', '리브', 'www.naver.com'); INSERT INTO submission (member_id, mission_id, url, comment) VALUES (1, 1, 'https://github.com/develup-mission/java-smoking/pull/1', '코멘트 1'); @@ -34,14 +34,14 @@ VALUES (2, 2, 'https://github.com/develup-mission/java-word-puzzle/pull/2', '코 INSERT INTO submission (member_id, mission_id, url, comment) VALUES (1, 3, 'https://github.com/develup-mission/java-guessing-number/pull/1', '코멘트 5'); -INSERT INTO pair (submission1_id, submission2_id, status) -VALUES (1, 2, '완료'); +INSERT INTO pair (main_submission, pair_submission, status) +VALUES (1, 2, 'ALL_FINISHED'); -INSERT INTO pair (submission1_id, submission2_id, status) -VALUES (2, 1, '완료'); +INSERT INTO pair (main_submission, pair_submission, status) +VALUES (2, 1, 'ALL_FINISHED'); -INSERT INTO pair (submission1_id, submission2_id, status) -VALUES (3, 4, '리뷰 완료'); +INSERT INTO pair (main_submission, pair_submission, status) +VALUES (3, 4, 'REVIEW_FINISHED'); -INSERT INTO pair (submission1_id, submission2_id, status) -VALUES (4, 3, '진행 중'); +INSERT INTO pair (main_submission, pair_submission, status) +VALUES (4, 3, 'IN_PROGRESS'); diff --git a/backend/src/test/java/develup/ApplicationTests.java b/backend/src/test/java/develup/ApplicationTests.java deleted file mode 100644 index 5226ec59..00000000 --- a/backend/src/test/java/develup/ApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package develup; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/backend/src/test/java/develup/mission/MissionServiceTest.java b/backend/src/test/java/develup/mission/MissionServiceTest.java index de91e5a5..ac32d58e 100644 --- a/backend/src/test/java/develup/mission/MissionServiceTest.java +++ b/backend/src/test/java/develup/mission/MissionServiceTest.java @@ -4,7 +4,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/backend/src/test/java/develup/pair/PairRepositoryTest.java b/backend/src/test/java/develup/pair/PairRepositoryTest.java index 8683252d..7d509932 100644 --- a/backend/src/test/java/develup/pair/PairRepositoryTest.java +++ b/backend/src/test/java/develup/pair/PairRepositoryTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import develup.submission.MyMission; +import develup.submission.PairRepository; import develup.submission.Submission; import develup.submission.SubmissionRepository; import org.junit.jupiter.api.DisplayName; @@ -32,7 +33,7 @@ void findMyMissionBySubmission() { assertAll( () -> assertThat(myMission.getId()).isEqualTo(1L), - () -> assertThat(myMission.getStatus()).isEqualTo("완료") + () -> assertThat(myMission.getStatusDescription()).isEqualTo("완료") ); } } diff --git a/backend/src/test/java/develup/submission/PairServiceTest.java b/backend/src/test/java/develup/submission/PairServiceTest.java new file mode 100644 index 00000000..2240ca60 --- /dev/null +++ b/backend/src/test/java/develup/submission/PairServiceTest.java @@ -0,0 +1,173 @@ +package develup.submission; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import develup.member.Member; +import develup.member.MemberRepository; +import develup.member.Provider; +import develup.mission.Language; +import develup.mission.Mission; +import develup.mission.MissionRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.jdbc.Sql; + +@SpringBootTest +@Sql(value = {"classpath:clean_data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) +class PairServiceTest { + + @Autowired + private PairService pairService; + + @Autowired + private MissionRepository missionRepository; + + @Autowired + private SubmissionRepository submissionRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private PairRepository pairRepository; + + @Test + @DisplayName("매칭 가능한 페어가 존재하는지 확인한다.") + void canMatch() { + Mission mission = createMission(); + createSubmission(mission, createMember()); + Submission submission = createSubmission(mission, createMember()); + + boolean result = pairService.canMatch(submission); + + assertThat(result).isTrue(); + } + + @Test + @DisplayName("매칭 가능한 페어가 존재하지 않은지 확인한다.") + void canNotMatch() { + Mission mission = createMission(); + Submission submission = createSubmission(mission, createMember()); + + boolean result = pairService.canMatch(submission); + + assertThat(result).isFalse(); + } + + @Test + @DisplayName("자신을 제외한 매칭 가능한 페어가 존재하지 않은지 확인한다.") + void canNotMatchWithMember() { + Mission mission = createMission(); + Member sameMember = createMember(); + Submission submission = createSubmission(mission, sameMember); + createSubmission(mission, sameMember); + + boolean result = pairService.canMatch(submission); + + assertThat(result).isFalse(); + } + + @Test + @DisplayName("페어를 매칭한다.") + void match() { + Mission mission = createMission(); + Submission mySubmission = createSubmission(mission, createMember()); + Submission otherSubmission = createSubmission(mission, createMember()); + + pairService.match(mySubmission); + + List pairs = pairRepository.findAll(); + assertThat(pairs) + .extracting(Pair::getMain) + .contains(mySubmission, otherSubmission); + } + + @Test + @DisplayName("이미 매칭된 제출로 새로운 매칭을 할 수 없다.") + void alreadyMatched() { + Mission mission = createMission(); + Submission mySubmission = createSubmission(mission, createMember()); + createSubmission(mission, createMember()); + pairService.match(mySubmission); + + assertThatThrownBy(() -> pairService.match(mySubmission)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("이미 매칭된 제출입니다."); + } + + @Test + @DisplayName("매칭할 제출이 존재하지 않으면 예외를 발생한다.") + void cannotMatch() { + Mission mission = createMission(); + Submission mySubmission = createSubmission(mission, createMember()); + + assertThatThrownBy(() -> pairService.match(mySubmission)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("매칭할 제출이 존재하지 않습니다."); + } + + @Test + @DisplayName("자신의 제출과 매칭될 수 없다.") + void cannotMatchWithMe() { + Mission mission = createMission(); + Member sameMember = createMember(); + createSubmission(mission, sameMember); + Submission mySubmission = createSubmission(mission, sameMember); + + assertThatThrownBy(() -> pairService.match(mySubmission)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("매칭할 제출이 존재하지 않습니다."); + } + + @Test + @DisplayName("존재하지 않는 제출은 매칭될 수 없다.") + void notfoundSubmission() { + Mission mission = createMission(); + Member member = createMember(); + Submission submission = new Submission( + -1L, + "sample", + "comment", + member, + mission + ); + + assertThatThrownBy(() -> pairService.match(submission)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("아직 제출되지 않아 매칭이 불가능합니다."); + } + + private Mission createMission() { + Mission mission = new Mission( + "sample", + Language.JAVA, + "description", + "thumbnail", + "url" + ); + missionRepository.save(mission); + + return mission; + } + + private Submission createSubmission(Mission mission, Member member) { + Submission submission = new Submission( + "sample", + "comment", + member, + mission + ); + + return submissionRepository.save(submission); + } + + private Member createMember() { + Member member = new Member("email", Provider.GITHUB, 1234L, "name", "image"); + + return memberRepository.save(member); + } +} diff --git a/backend/src/test/java/develup/submission/PairTest.java b/backend/src/test/java/develup/submission/PairTest.java new file mode 100644 index 00000000..e280da86 --- /dev/null +++ b/backend/src/test/java/develup/submission/PairTest.java @@ -0,0 +1,19 @@ +package develup.submission; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PairTest { + + @Test + @DisplayName("같은 제출로 페어를 맺을 수 없다.") + void createWithSameSubmission() { + Submission submission = new Submission(1L, "url", "comment", null, null); + + assertThatThrownBy(() -> new Pair(submission, submission, PairStatus.IN_PROGRESS)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("같은 제출끼리 페어가 될 수 없습니다."); + } +} diff --git a/backend/src/test/java/develup/submission/SubmissionRepositoryTest.java b/backend/src/test/java/develup/submission/SubmissionRepositoryTest.java new file mode 100644 index 00000000..333c5e4c --- /dev/null +++ b/backend/src/test/java/develup/submission/SubmissionRepositoryTest.java @@ -0,0 +1,76 @@ +package develup.submission; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import develup.member.Member; +import develup.member.MemberRepository; +import develup.member.Provider; +import develup.mission.Language; +import develup.mission.Mission; +import develup.mission.MissionRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.jdbc.Sql; + +@SpringBootTest +@Sql(value = {"classpath:clean_data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) +class SubmissionRepositoryTest { + + @Autowired + private SubmissionRepository submissionRepository; + + @Autowired + private MissionRepository missionRepository; + + @Autowired + private MemberRepository memberRepository; + + @Test + @DisplayName("아직 매칭되지 않은 제출을 불러온다.") + void findNonMatchedSubmission() { + Mission mission = createMission(); + Submission submission1 = createSubmission(mission); + Submission submission2 = createSubmission(mission); + + List result = submissionRepository.findNonMatchedSubmissions(mission); + + assertThat(result) + .extracting(Submission::getId) + .contains(submission1.getId(), submission2.getId()); + } + + private Mission createMission() { + Mission mission = new Mission( + "sample", + Language.JAVA, + "description", + "thumbnail", + "url" + ); + missionRepository.save(mission); + + return mission; + } + + private Submission createSubmission(Mission mission) { + Member member = createMember(); + + Submission submission = new Submission( + "sample", + "comment", + member, + mission + ); + + return submissionRepository.save(submission); + } + + private Member createMember() { + Member member = new Member("email", Provider.GITHUB, 1234L, "name", "image"); + + return memberRepository.save(member); + } +} diff --git a/backend/src/test/java/develup/submission/SubmissionServiceTest.java b/backend/src/test/java/develup/submission/SubmissionServiceTest.java index 66d08345..8d75e4a4 100644 --- a/backend/src/test/java/develup/submission/SubmissionServiceTest.java +++ b/backend/src/test/java/develup/submission/SubmissionServiceTest.java @@ -4,6 +4,8 @@ import java.util.List; import develup.member.Member; +import develup.member.MemberRepository; +import develup.member.Provider; import develup.mission.Language; import develup.mission.Mission; import develup.mission.MissionRepository; @@ -27,10 +29,13 @@ class SubmissionServiceTest { @Autowired private MissionRepository missionRepository; + @Autowired + private MemberRepository memberRepository; + @Test @DisplayName("미션을 제출한다.") void createSubmission() { - Member member = new Member(1L); + Member member = createMember(); Mission mission = missionRepository.save(new Mission("미션 1", Language.JAVA, "미션 설명", "미션 썸네일", "미션 url")); CreateSubmissionRequest request = new CreateSubmissionRequest(mission.getId(), "pr url", "코멘트"); @@ -39,6 +44,12 @@ void createSubmission() { assertThat(submissionRepository.findAll()).hasSize(1); } + private Member createMember() { + Member member = new Member("email", Provider.GITHUB, 1234L, "name", "image"); + + return memberRepository.save(member); + } + @Nested @Sql(value = {"classpath:mymissions.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @DisplayName("특정 유저 미션 현황 서비스 테스트") @@ -47,7 +58,7 @@ class MyMission { @Test @DisplayName("참여한 모든 미션을 조회한다.") void getMyMissions() { - Member member = new Member(1L); + Member member = createMember(); List myMissions = submissionService.getMyMissions(member); @@ -57,11 +68,15 @@ void getMyMissions() { @Test @DisplayName("매칭된 제출이 없는 경우 `매칭 대기` 상태로 설정된다.") void getMyMissionsWhenNoPair() { - Member member = new Member(1L); + Member member = createMember(); List myMissions = submissionService.getMyMissions(member); assertThat(myMissions.getFirst().status()).isEqualTo("매칭 대기"); } + + private Member createMember() { + return new Member(1L, "email", Provider.GITHUB, 1234L, "name", "image"); + } } } diff --git a/backend/src/test/java/develup/submission/SubmissionTest.java b/backend/src/test/java/develup/submission/SubmissionTest.java new file mode 100644 index 00000000..2355c81d --- /dev/null +++ b/backend/src/test/java/develup/submission/SubmissionTest.java @@ -0,0 +1,52 @@ +package develup.submission; + +import static org.assertj.core.api.Assertions.assertThat; + +import develup.member.Member; +import develup.member.Provider; +import develup.mission.Language; +import develup.mission.Mission; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SubmissionTest { + + @Test + @DisplayName("같은 사용자의 제출이 아닌지 확인할 수 있다.") + void isNotSameOwner() { + Member member1 = createMember(1L); + Member member2 = createMember(2L); + Mission mission = createMission(); + Submission submission1 = createSubmission(member1, mission); + Submission submission2 = createSubmission(member2, mission); + + boolean result = submission1.isNotSameOwner(submission2); + + assertThat(result).isTrue(); + } + + @Test + @DisplayName("같은 사용자의 제출인지 확인할 수 있다.") + void isSameOwner() { + Member member = createMember(1L); + Mission mission = createMission(); + Submission submission2 = createSubmission(member, mission); + Submission submission1 = createSubmission(member, mission); + + boolean result = submission1.isNotSameOwner(submission2); + + assertThat(result).isFalse(); + } + + private Member createMember(Long id) { + return new Member(id, "email", Provider.GITHUB, 1234L, "name", "image"); + } + + private Mission createMission() { + return new Mission("title", Language.JAVA, "description", "thumbnail", "url"); + } + + private Submission createSubmission(Member member1, Mission mission) { + return new Submission("url", "comment", member1, mission); + } +} diff --git a/backend/src/test/resources/clean_data.sql b/backend/src/test/resources/clean_data.sql index adabbda4..60c9435d 100644 --- a/backend/src/test/resources/clean_data.sql +++ b/backend/src/test/resources/clean_data.sql @@ -8,9 +8,3 @@ ALTER TABLE mission ALTER COLUMN id RESTART; TRUNCATE TABLE member; ALTER TABLE member ALTER COLUMN id RESTART; SET REFERENTIAL_INTEGRITY TRUE; - -INSERT INTO member (id) -VALUES (1); - -INSERT INTO member (id) -VALUES (2); diff --git a/backend/src/test/resources/mymissions.sql b/backend/src/test/resources/mymissions.sql index 57586a13..8ba4c8cb 100644 --- a/backend/src/test/resources/mymissions.sql +++ b/backend/src/test/resources/mymissions.sql @@ -1,13 +1,19 @@ -SET REFERENTIAL_INTEGRITY FALSE; +SET +REFERENTIAL_INTEGRITY FALSE; TRUNCATE TABLE pair; -ALTER TABLE pair ALTER COLUMN id RESTART; +ALTER TABLE pair + ALTER COLUMN id RESTART; TRUNCATE TABLE submission; -ALTER TABLE submission ALTER COLUMN id RESTART; +ALTER TABLE submission + ALTER COLUMN id RESTART; TRUNCATE TABLE mission; -ALTER TABLE mission ALTER COLUMN id RESTART; +ALTER TABLE mission + ALTER COLUMN id RESTART; TRUNCATE TABLE member; -ALTER TABLE member ALTER COLUMN id RESTART; -SET REFERENTIAL_INTEGRITY TRUE; +ALTER TABLE member + ALTER COLUMN id RESTART; +SET +REFERENTIAL_INTEGRITY TRUE; INSERT INTO mission (title, language, description, thumbnail, url) VALUES ('루터회관 흡연 단속', 'JAVA', 'https://raw.githubusercontent.com/develup-mission/java-smoking/main/README.md', @@ -24,11 +30,11 @@ VALUES ('숫자 추리 게임', 'JAVA', 'https://raw.githubusercontent.com/devel 'https://private-user-images.githubusercontent.com/80797824/348618796-753cae7d-779e-4847-b1b5-31ab9ff2d1cb.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjEwNDA3NTIsIm5iZiI6MTcyMTA0MDQ1MiwicGF0aCI6Ii84MDc5NzgyNC8zNDg2MTg3OTYtNzUzY2FlN2QtNzc5ZS00ODQ3LWIxYjUtMzFhYjlmZjJkMWNiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MTUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzE1VDEwNDczMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTE2ODM0ZjI2YWMxMGEzNGUzNDk2MjVlMTdlMWRkNjI4NjkyMWUxMTQzZDdhZjRlYjM2NTgzNjE0MTQ0YTZlZmQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.yk09dRTA7LefjF0uTyYNkCgBRJcLizoKSJ8Otq7J1Gs', 'https://github.com/develup-mission/java-guessing-number'); -INSERT INTO member (id) -VALUES (1); +INSERT INTO member (id, email, provider, social_id, name, image_url) +VALUES (1, 'test1@gmail.com', 'GITHUB', '1234', '구름', 'www.naver.com'); -INSERT INTO member (id) -VALUES (2); +INSERT INTO member (id, email, provider, social_id, name, image_url) +VALUES (2, 'test1@gmail.com', 'GITHUB', '1234', '리브', 'www.naver.com'); INSERT INTO submission (member_id, mission_id, url, comment) VALUES (1, 1, 'https://github.com/develup-mission/java-smoking/pull/1', '코멘트 1'); @@ -45,14 +51,14 @@ VALUES (2, 2, 'https://github.com/develup-mission/java-word-puzzle/pull/2', '코 INSERT INTO submission (member_id, mission_id, url, comment) VALUES (1, 3, 'https://github.com/develup-mission/java-guessing-number/pull/1', '코멘트 5'); -INSERT INTO pair (submission1_id, submission2_id, status) -VALUES (1, 2, '완료'); +INSERT INTO pair (main_submission, pair_submission, status) +VALUES (1, 2, 'ALL_FINISHED'); -INSERT INTO pair (submission1_id, submission2_id, status) -VALUES (2, 1, '완료'); +INSERT INTO pair (main_submission, pair_submission, status) +VALUES (2, 1, 'ALL_FINISHED'); -INSERT INTO pair (submission1_id, submission2_id, status) -VALUES (3, 4, '리뷰 완료'); +INSERT INTO pair (main_submission, pair_submission, status) +VALUES (3, 4, 'REVIEW_FINISHED'); -INSERT INTO pair (submission1_id, submission2_id, status) -VALUES (4, 3, '진행 중'); +INSERT INTO pair (main_submission, pair_submission, status) +VALUES (4, 3, 'IN_PROGRESS');