Skip to content

Commit

Permalink
v 1.0.0 ๋ฐฐํฌ ๐Ÿน๐ŸŽ‰ (#313)
Browse files Browse the repository at this point in the history
* feat: Slack OAuth๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ ๊ตฌํ˜„ (#233)

* feat: ์Šฌ๋ž™ OAuth 2๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ ๋กœ์ง ๊ตฌํ˜„

* test: ์ธ์ฆ ์ธ์ˆ˜ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€

* chore: ์„œ๋ธŒ๋ชจ๋“ˆ jwt, ์Šฌ๋ž™ api ํ˜ธ์ถœ ์ •๋ณด ์—…๋ฐ์ดํŠธ

* test: Mockito ๋Œ€์‹  BDDMockito ์‚ฌ์šฉ

Co-authored-by: hyewoncc <[email protected]>
Co-authored-by: yeon-06 <[email protected]>

* feat: ๋‹ฌ๋ ฅ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ (#246)

* feat: Calendar component ๊ฐœ๋ฐœ ๋ฐ ์Šคํ† ๋ฆฌ๋ถ ์ž‘์„ฑ

* feat: useCalendar custom hook ๋ถ„๋ฆฌ

* refactor: WrapperButton component props ์ถ”๊ฐ€(isFuture)

* refactor: Props type ๋ณ€๊ฒฝ

* refactor: Calendar component ์ค‘๋ณต๋˜๋Š” ๋กœ์ง ์ œ๊ฑฐ

* feat: Calendar ์ปดํฌ๋„ŒํŠธ ์ ์šฉ (#251)

* feat: usePortal custom hook ๊ฐœ๋ฐœ

* refactor: ISOConverter util ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌ

- src/@utils ํด๋”๋กœ ํ•จ์ˆ˜ ์ด๋™
- ์˜ค๋Š˜, ์–ด์ œ๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ convert ํ•ด์ฃผ๋Š” ๋กœ์ง ์ถ”๊ฐ€

* refactor: ๊ธฐ์กด ๋กœ์ง usePortal custom hook์œผ๋กœ ๋ณ€๊ฒฝ

* feat: ๊ธฐ์กด๋กœ์ง์— Calendar component ์ ์šฉ

* refactor: utils ํŒŒ์ผ ์ƒ์ˆ˜์ฒ˜๋ฆฌ

* refactor: ์บ˜๋ฆฐ๋” ์˜คํ”ˆ๋˜์—ˆ์„ ๋•Œ scroll ๊ณ ์ • ์‹œํ‚ค๋Š” ๊ธฐ๋Šฅ ์ถ”๊ฐ€

* refactor: MemberInitializer์— ๋นˆ ์ฃผ์ž… ๋ฐ ํ…Œ์ŠคํŠธ์—์„œ ๋น„ํ™œ์„ฑํ™”   (#252)

* refactor: MemberInitializer์— ๋นˆ ์ฃผ์ž… ๋ฐ Profile ์„ค์ •

* refactor: ์Šฌ๋ž™ api ํ˜ธ์ถœ์„ ์œ„ํ•œ ๋ด‡ ํ† ํฐ์„ ๋นˆ ์ƒ์„ฑ ์‹œ ์„ค์ •

* refactor: ๋ฉ”์„œ๋“œ ๋ช… ๊ฐœ์„ 

* feat: ๋ถ๋งˆํฌ ์ƒ์„ฑ ๊ตฌํ˜„ (#249)

* feat: ๋ถ๋งˆํฌ ์ƒ์„ฑ ๊ตฌํ˜„

* style: ์ฝ”๋“œ ์ปจ๋ฒค์…˜ ์ ์šฉ

* feat: ์„ ํƒ๋œ ์ฑ„๋„์˜ ํ”ผ๋“œ๋งŒ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ (#260)

* refactor: path์— channelId ์ถ”๊ฐ€

* refactor: getMessages API fetcher์— channelId ๊ด€๋ จ ๋กœ์ง ์ถ”๊ฐ€ ๋ฐ Param type ๋„ค์ด๋ฐ ๋ณ€๊ฒฝ

* refactor: Calendar, Drawer, DropdownMenu Component Link tag to ํ”„๋กœํผํ‹ฐ์— channelId ์ถ”๊ฐ€

* refactor: Feed, SpecificDateFeed component์— channelId ์ถ”๊ฐ€

* chore: authorization sample token ๊ฐ’ ๋ณ€๊ฒฝ, style ์ˆ˜์ •

* refactor: DATE ๊ฐ์ฒด constants ํด๋”๋กœ ์ด๋™ ๋ฐ "์–ด์ œ", "์˜ค๋Š˜" ์ƒ์ˆ˜๋กœ ๋ณ€๊ฒฝ

* refactor: ๋ถˆํ•„์š”ํ•œ Dropdown component ์ œ๊ฑฐ ๋ฐ Feed Page component์— ์ ์šฉ

* refactor: ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ˜ธ์ถœํ•˜๋Š” useParams, Portal -> Page component๋กœ ์ด๋™ ํ›„ Props๋กœ ์ „๋‹ฌ

- Calendar, dropdownMenu ์— ์žˆ๋Š” useParams, Portal -> Feed, SpecificDateFeed๋กœ ์ด๋™

* feat: ๋ถ๋งˆํฌ API ๋ณ€๊ฒฝ๋œ ๋ช…์„ธ ์ ์šฉ (#268)

* feat: ๋ถ๋งˆํฌ ์กฐํšŒ ๊ธฐ๋Šฅ ๊ตฌํ˜„ (#257)

* feat: ๋ถ๋งˆํฌ ์กฐํšŒ ๊ตฌํ˜„

* test: ๋ถ๋งˆํฌ ์ƒ์„ฑ ์„œ๋น„์Šค ํ…Œ์ŠคํŠธ ๊ฒ€์ฆ ๋ฐฉ์‹ ๋ณ€๊ฒฝ

* refactor: ์กฐํšŒ ์ •๋ ฌ ์กฐ๊ฑด์„ ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž๊ฒŒ ์ •์ •

* refactor: BookmarkResponse์— Builder ํŒจํ„ด ์ ์šฉ

* style: ๊ฐœํ–‰ ์ถ”๊ฐ€

Co-authored-by: yeon-06 <[email protected]>
Co-authored-by: hyewoncc <[email protected]>

* feat: ์ธ์ฆ ์ธ๊ฐ€ ๋ฐ ๋กœ๊ทธ์•„์›ƒ ๊ธฐ๋Šฅ (#262)

* feat: ๋กœ๊ทธ์ธ ์ƒํƒœ ํ™•์ธ api ๋ชจํ‚น
- header ๋ฅผ ๋ชจ๋“  axios ์š”์ฒญ์— ๋„ฃ์–ด์ฃผ๋„๋ก ์ˆ˜์ •

* feat: ์ฟ ํ‚ค ์ €์žฅ/์‚ญ์ œ/์กฐํšŒ ํ•จ์ˆ˜ ์ž‘์„ฑ ๋ฐ ์ ์šฉ
- Header ์— ์ฟ ํ‚ค์— ์žˆ๋Š” ๊ฐ’ ๊ฐ€์ ธ์˜ค๋„๋ก ๊ตฌํ˜„
- Cookie Key ์ƒ์ˆ˜ํ™”

* refactor: ๋ถˆํ•„์š”ํ•œ import ์ œ๊ฑฐ

* feat: Private Router ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„

* refactor: auth ํ™•์ธ mock api ๋ฉ”์„œ๋“œ ์ˆ˜์ •
- post -> get

* feat: PrivateRouter ์ ์šฉ

* fix: PrivateRouter ์ˆ˜์ •
- useErrorBoundary ์˜ต์…˜ ์ถ”๊ฐ€
- early return ์ ์šฉ

* feat: PublicRouter ๊ตฌํ˜„ ๋ฐ ์ ์šฉ

* feat: ๋žœ๋”ฉํŽ˜์ด์ง€ NavBar ์ œ๊ฑฐ

* refactor: ์ฟผ๋ฆฌํ‚ค ๋ฐฐ์—ด->string ์œผ๋กœ ์ˆ˜์ •

* fix: token ์š”์ฒญ๋งˆ๋‹ค get ํ•˜๋„๋ก ์ˆ˜์ •
- ์ด์ „์—๋Š”, axios ๊ฐ์ฒด ์ƒ์„ฑ ์‹œ ์ตœ์ดˆ๋กœ cookie ์—์„œ ๊ฐ€์ ธ์™”๋Š”๋ฐ, ํ˜„์žฌ๋Š” ๋งค ์š”์ฒญ๋งˆ๋‹ค get ํ•˜์—ฌ ์ตœ์‹  ์ฟ ํ‚ค ์ƒํƒœ ๋ฐ˜์˜ํ•˜๋„๋ก ์ˆ˜์ •ํ•จ

* feat: useAuthentication hook ์ž‘์„ฑํ•˜์—ฌ ์ž„์‹œ ๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ ๊ตฌํ˜„

* feat: ๋กœ๊ทธ์ธ ์—ฌ๋ถ€ ํ™•์ธ ๋ชจํ‚น API ์—์„œ ํ† ํฐ ํ™•์ธํ•˜๋„๋ก ์ˆ˜์ •

* refactor: useSnackbar ํ›… ๋ฆฌํŒฉํ„ฐ๋ง
- optional chaning ์ถ”๊ฐ€ํ•˜์—ฌ ํƒ€์ž… ์—๋Ÿฌ ์บ์น˜
- ํ•จ์ˆ˜ ํ•˜๋‚˜๋งŒ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์ˆ˜์ •

* fix: ์„ธ๋ฏธ์ฝœ๋ก  ์ œ๊ฑฐ

* fix: ์ค‘๊ฐ„ ํ™”๋ฉด ๊นœ๋นก์ด๋Š” ๋ฒ„๊ทธ ํ•ด๊ฒฐ

* refactor: Cookie value ํŒŒ์‹ฑ ์ •๊ทœ์‹ ์ˆ˜์ •

* refactor: ์ •๊ทœ์‹ ์ฃผ์„ ์ฒจ๋ถ€

* feat: Snackbar ์„ฑ๊ณต/์‹คํŒจ status ๊ตฌ๋ถ„

* refactor: Snackbar status type ์ƒ์ˆ˜ํ™”

* feat: Jenkinsfile ์‚ญ์ œ (#272)

* refactor: usePortal ๋ฆฌํŒฉํ„ฐ๋ง (#271)

* chore: tsconfig outDir ์„ค์ • ์ถ”๊ฐ€

* refactor: useEffect ์Šคํฌ๋กค ์‚ฌ์ด๋“œ ์ดํŒฉํŠธ usePortal custom hook์œผ๋กœ ๋ถ„๋ฆฌ

* refactor: usePortal -> useModal ๋กœ ๋„ค์ด๋ฐ์œผ๋กœ ๋ณ€๊ฒฝ

* refactor: DateDropdown component ์ค‘๋ณต๋˜๋Š” ๋กœ์ง useModal custom hook์œผ๋กœ ๋ณ€๊ฒฝ

* refactor: ๋ณ€๊ฒฝ๋œ custom hook ์ ์šฉ

* refactor: ๋ฆฌ๋ Œ๋”๋ง์‹œ DateDropdownMenu ์‚ฌ๋ผ์ง€๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ

- fetch์‹œ onsettled ์ œ๊ฑฐ
- useMessageData custom hook์— useEffect ๋กœ์ง ์ถ”๊ฐ€

* chore: ๋ถˆํ•„์š”ํ•œ import๋ฌธ ์ œ๊ฑฐ

* feat: ๋ถ๋งˆํฌ ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ (#269)

* feat: ๋ถ๋งˆํฌ ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„

* refactor: ๋ถ๋งˆํฌ ์‚ญ์ œ ์‹คํŒจ ์˜ˆ์™ธ ์ด๋ฆ„ ๋ณ€๊ฒฝ

Co-authored-by: yeon-06 <[email protected]>
Co-authored-by: hyewoncc <[email protected]>

* feat: ์ฑ„๋„ ๊ตฌ๋… ์ˆœ์„œ ์˜ˆ์™ธ ์ผ€์ด์Šค ์ฒ˜๋ฆฌ (#275)

* feat: viewOrder๊ฐ€ ์ค‘๋ณต์œผ๋กœ ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ์ถ”๊ฐ€

* feat: ๊ตฌ๋… ์ฑ„๋„ ์•„์ด๋””๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋“ค์–ด์˜ค์ง€ ์•Š์€ ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ์ถ”๊ฐ€

* style: when & then ์ฃผ์„ ์ถ”๊ฐ€

* refactor: test code ํ•จ์ˆ˜ ์ถ”์ถœ

* fix: ํ…Œ์ŠคํŠธ์ฝ”๋“œ ์˜ค๋ฅ˜ ์ˆ˜์ •

* feat: ์ฑ„๋„ ๊ตฌ๋… ์ˆœ์„œ๊ฐ€ 1 ๋ฏธ๋งŒ์ธ ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ

* feat: ๋ฉ”์‹œ์ง€ ์กฐํšŒ API ์— channelId๊ฐ€ ์ „๋‹ฌ๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ๋กœ์ง ์ˆ˜์ • (#261)

* feat: ๊ตฌ๋…์ค‘์ธ ์ฑ„๋„์ด ์ „ํ˜€ ์—†์„ ๋•Œ์— ๋Œ€์‘ํ•  ์˜ˆ์™ธ ํด๋ž˜์Šค ์ƒ์„ฑ

Co-authored-by: Richard Jeon <[email protected]>
Co-authored-by: JangBomi <[email protected]>

* feat: ๋ฉ”์‹œ์ง€ ์กฐํšŒ ์‹œ ํ—ค๋”์—์„œ ์œ ์ € ์•„์ด๋”” ์ถ”์ถœ

Co-authored-by: Richard Jeon <[email protected]>
Co-authored-by: JangBomi <[email protected]>

* feat: ์ฑ„๋„ ๊ตฌ๋… ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋‚ด ์ฒซ๋ฒˆ์งธ ๊ตฌ๋… ์ฑ„๋„ ์กฐํšŒ ๋ฉ”์„œ๋“œ ๊ตฌํ˜„

Co-authored-by: Richard Jeon <[email protected]>
Co-authored-by: JangBomi <[email protected]>

* feat: channelId๊ฐ€ ์ „๋‹ฌ๋˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ, ํšŒ์› ์ •๋ณด ์กฐํšŒํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ฐœ์„ 

Co-authored-by: Richard Jeon <[email protected]>
Co-authored-by: JangBomi <[email protected]>

* test: MessageAcceptanceTest ์— ์ธ์ฆ ๊ณผ์ • ์ถ”๊ฐ€

Co-authored-by: Richard Jeon <[email protected]>
Co-authored-by: JangBomi <[email protected]>

* test: MessageControllerTest RestDocs ์—…๋ฐ์ดํŠธ

Co-authored-by: Richard Jeon <[email protected]>
Co-authored-by: JangBomi <[email protected]>

* test: ์ฑ„๋„ id๊ฐ€ ์ „๋‹ฌ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ service test ์ถ”๊ฐ€

Co-authored-by: Richard Jeon <[email protected]>
Co-authored-by: JangBomi <[email protected]>

* fix: ์ œ๊ฑฐํ–ˆ๋˜ ๋ฉ”์„œ๋“œ ๋ณต์›

Co-authored-by: Richard Jeon <[email protected]>
Co-authored-by: JangBomi <[email protected]>

Co-authored-by: JangBomi <[email protected]>

* feat: ๋ฉ”์‹œ์ง€ ์กฐํšŒ ์‹œ needPastMessage ํ•จ๊ป˜ ์‘๋‹ตํ•˜๋„๋ก ์ˆ˜์ • (#277)

* refactor: message api ์กฐํšŒ ์‹œ needPastMessage ์‘๋‹ตํ•˜๋„๋ก ์ˆ˜์ •

* test: ๊ธฐ์กด ํ…Œ์ŠคํŠธ ์ˆ˜์ •

* test: ํ…Œ์ŠคํŠธ ์ถฉ๋Œ ํ•ด๊ฒฐ

* feat: ์Šฌ๋ž™ OAuth ๊ธฐ๋Šฅ ๊ตฌํ˜„ (#281)

* feat: Certification ํŽ˜์ด์ง€ ๊ตฌํ˜„
- Slack OAuth ์ดํ›„ Redirect ํ•  ๋ผˆ๋Œ€ ํŽ˜์ด์ง€ ๊ตฌํ˜„

* feat: ์Šฌ๋ž™ ๋กœ๊ทธ์ธ api ์š”์ฒญ ํ•จ์ˆ˜ ์ž‘์„ฑ

* feat: ์ธ์ฆ ๊ด€๋ จ Mock API ์ˆ˜์ •
- ๋ฌด์กฐ๊ฑด ์„ฑ๊ณต ์ผ€์ด์Šค๋กœ ๋„์›Œ์ฃผ๋„๋ก ์ˆ˜์ •

* feat: Certification ํŽ˜์ด์ง€ ์„ฑ๊ณต/์‹คํŒจ ์ฒ˜๋ฆฌ ์ถ”๊ฐ€

* chore: ์„œ๋ธŒ๋ชจ๋“ˆ redirect-url ์ˆ˜์ • (#283)

* hotifx: axios ์š”์ฒญ์—์„œ header ๋ˆ„๋ฝ๋˜๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ  (#285)

* hotfix: axios header ๋ˆ„๋ฝ๋˜๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ

* fix: ์ค‘๋ณต header ์ œ๊ฑฐ

* hotfix: axios ์ธ์Šคํ„ด์Šค ์ˆ˜์ •ํ•˜์—ฌ ์š”์ฒญ headers ํ•„์š”์‹œ ๊ตฌ๋ถ„  (#286)

* fix: privateFetcher, publicFetcher ๊ตฌ๋ถ„

* fix: npm script ์ˆ˜์ •

* fix: npm script ์ˆ˜์ •

* refactor: DateDropdown ์ปดํฌ๋„ŒํŠธ ๋ฆฌํŒฉํ„ฐ๋ง (#282)

* refactor: DateDropdown component ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ถ„๋ฆฌ

- DAY ๊ฐ์ฒด constants ํŒŒ์ผ๋กœ ์ด๋™
- getDateInformation, getMessagesDate ํ•จ์ˆ˜ utils ํŒŒ์ผ๋กœ ์ด๋™

* refactor: component ๋„ค์ด๋ฐ ๋ณ€๊ฒฝ

- DropdownToggle -> DateDropdownToggle
- DropdownMenu -> DateDropdownMenu

* refactor: renderDateOption -> DateDropdownOption component ๋กœ ๋ถ„๋ฆฌ

* feat: ํ† ํฐ ์ธ์ฆ ๋ฐ ์ธ๊ฐ€ ๊ตฌํ˜„ (#276)

* feat: token์šฉ Argument Resolver ๊ตฌํ˜„

* refactor: ํ—ค๋” Authorization ๊ฐ’์„ ArgumentResolver๋ฅผ ํ†ตํ•ด ์ถ”์ถœํ•˜๋„๋ก ๋ณ€๊ฒฝ

* feat: ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๋กœ์ง ์ถ”๊ฐ€

* test: Mockito ๋Œ€์‹  BDDMockito ์‚ฌ์šฉ

* feat: ์œ ํšจํ•œ ํ† ํฐ์ธ์ง€ ๊ฒ€์‚ฌํ•˜๋Š” interceptor ๊ตฌํ˜„

* style: ๋ˆ„๋ฝ๋œ final ํ‚ค์›Œ๋“œ ์ถ”๊ฐ€

* style: JwtTokenProvider ๋ฉ”์„œ๋“œ ์ˆœ์„œ ๋ณ€๊ฒฝ

Co-authored-by: hyewoncc <[email protected]>
Co-authored-by: yeon-06 <[email protected]>

* feat: ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ด€๋ จ API ์—ฐ๋™ ๋ฐ ์‹ค์ œ ํ† ํฐ ์‚ฝ์ž… (#289)

* feat: Headers์— ์‹ค์ œ token ์‚ฝ์ž…

* feat: ๋ฐฐํฌ๋œ ์‚ฌ์šฉ์ž ์ธ์ฆ API ์—ฐ๋™

* feat: Mocking API url ์ˆ˜์ •

* feat: ํ† ํฐ ๊ฒ€์ฆ api ๊ตฌํ˜„ (#287)

* feat: ํ† ํฐ ๊ฒ€์ฆ api ๊ตฌํ˜„

* test: ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋ช… ๋ณ€๊ฒฝ

Co-authored-by: yeon-06 <[email protected]>
Co-authored-by: hyewoncc <[email protected]>

* hotfix: ์Šฌ๋ž™ ๋กœ๊ทธ์ธ ํ›„ ํ† ํฐ ์ฟ ํ‚ค์— ์ €์žฅํ•˜๋Š” ๋กœ์ง ์ถ”๊ฐ€ (#290)

* fix: ์Šฌ๋ž™ ๋กœ๊ทธ์ธ ํ›„ ํ† ํฐ ์ฟ ํ‚ค์— ์ €์žฅํ•˜๋Š” ๋กœ์ง ์ถ”๊ฐ€

* fix: api ์‘๋‹ต ๋ฐ์ดํ„ฐ ๋ฆฌํ„ด ๋ฐฉ์‹ ์ˆ˜์ •

* fix: ์ฟ ํ‚ค์— ํ† ํฐ ์ €์žฅํ•˜๋Š” ๋กœ์ง api ํ•จ์ˆ˜๋กœ ์ด๋™

* feat: logback maxHistory ์ˆ˜์ •. 5 -> 2 (#293)

* chore: ์„œ๋ธŒ๋ชจ๋“ˆ ์—…๋ฐ์ดํŠธ - redirect-url ์˜คํƒ€ ์ˆ˜์ • (#294)

* test: util ํ•จ์ˆ˜ test ์ฝ”๋“œ ์ž‘์„ฑ (#292)

* refactor: utils ํŒŒ์ผ์— ์žˆ๋˜ ์ƒ์ˆ˜ constants ํŒŒ์ผ๋กœ ์ด๋™

* chore: @babel/preset-typescript ์„ค์น˜ ๋ฐ babel preset ์ถ”๊ฐ€ ์„ค์ •

* chore: test ์ ˆ๋Œ€๊ฒฝ๋กœ ์„ค์ •

- jset.config.js moduleNameMapper ์„ค์ • ์ถ”๊ฐ€

* chore: ๋ถˆํ•„์š”ํ•œ console.log, type ์ œ๊ฑฐ

* feat: test ์ฝ”๋“œ ์ž‘์„ฑ์‹œ ํ•„์š”ํ•œ mocking data ์ƒ์„ฑ

* feat: utils ํ•จ์ˆ˜ test ์ฝ”๋“œ ์ž‘์„ฑ

- 24์‹œ๊ฐ„์ œ์˜ ์‹œ๊ฐ„์ด ์ž…๋ ฅ๋˜๋ฉด ์˜ค์ „/์˜คํ›„ prefix๋ฅผ ๋ถ™์—ฌ ๋ณ€ํ™˜๋œ ์‹œ๊ฐ„์„ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
- ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ ISO ํ˜•์‹์˜ date ๊ฐ’์„ '์˜ค์ „/์˜คํ›„ 00:00' ํ˜•์‹์˜ ์‹œ๊ฐ„์œผ๋กœ ๋ณ€ํ™˜๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
- ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ ๋ฉ”์ธ ํ”ผ๋“œ ๋ฐ์ดํ„ฐ์—์„œ ํ•„์š”ํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ์ž˜ ์ถ”์ถœํ•ด ์ƒˆ๋กœ์šด ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด ์ฃผ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
- ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ ๋ถ๋งˆํฌ ํ”ผ๋“œ ๋ฐ์ดํ„ฐ์—์„œ ํ•„์š”ํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ์ž˜ ์ถ”์ถœํ•ด ์ƒˆ๋กœ์šด ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด ์ฃผ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
- date๋กœ ๋“ค์–ด์˜ค๋Š” '์–ด์ œ', '์˜ค๋Š˜', 'ํŠน์ •๋‚ ์งœ(2022-8-12)'๋ฅผ ISO ํ˜•์‹์˜ date ํƒ€์ž…์œผ๋กœ ๋ณ€๊ฒฝ ํ•ด์ฃผ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

* chore: package.json scripts ์ˆ˜์ •

- test, dev-test ๋‚˜๋ˆ ์„œ ์ ์šฉ

* refactor: ๋ถˆํ•„์š”ํ•œ sampleTest ์ฝ”๋“œ ์ œ๊ฑฐ

* fix: api retry ์š”์ฒญ ํšŸ์ˆ˜ ์ˆ˜์ • ๋ฐ ๋””ํดํŠธ QueryParam ์ˆ˜์ • (#295)

* hotfix: ๋กœ๊ทธ์ธ ํ›„ ์ฟผ๋ฆฌ ๋ฌดํšจํ™” ์ถ”๊ฐ€ (#296)

* feat: ํŒŒ์ผ ๊ณต์œ  ๋ฉ”์‹œ์ง€ ์ €์žฅ ๊ตฌํ˜„ (#225)

* feat: ํŒŒ์ผ ๊ณต์œ  ๋ฉ”์‹œ์ง€ ์ €์žฅ ๊ตฌํ˜„

Co-authored-by: JangBomi <[email protected]>
Co-authored-by: Richard Jeon <[email protected]>

* refactor: ํ”ผ๋“œ๋ฐฑ ๋ฐ˜์˜

Co-authored-by: Richard Jeon <[email protected]>
Co-authored-by: JangBomi <[email protected]>

Co-authored-by: Richard Jeon <[email protected]>

* Revert "feat: ํŒŒ์ผ ๊ณต์œ  ๋ฉ”์‹œ์ง€ ์ €์žฅ ๊ตฌํ˜„ (#225)" (#298)

This reverts commit 2e92b7f.

* hotfix : ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ ๋ฒ„๊ทธ (#302)

* fix: ๋กœ๊ทธ์ธ ์ดํ›„, ํ”ผ๋“œ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋„๋ก ์ˆ˜์ •

* feat: ๋žœ๋”ฉํŽ˜์ด์ง€ ๋ฒ„ํŠผ ๋ˆ„๋ฅผ ์‹œ, ์Šฌ๋ž™ ๋กœ๊ทธ์ธ์œผ๋กœ ๋„˜์–ด๊ฐ€๋„๋ก ์ˆ˜์ •

* fix: ๋ˆ„๋ฝ๋œ PrivateRouter ์ถ”๊ฐ€

* fix: ๋งค ์š”์ฒญ๋งˆ๋‹ค ํ† ํฐ ์ฟ ํ‚ค๋กœ๋ถ€ํ„ฐ ์ƒˆ๋กœ ๊ฐ€์ ธ์˜ค๋„๋ก ์ˆ˜์ •

* feat: ๋กœ๊ทธ์ธ ์‹œ ์ตœ์ดˆ ๋กœ๊ทธ์ธ ์—ฌ๋ถ€ ์‘๋‹ต (#303)

* fix: ๋ˆ„๋ฝ๋œ headers ์ถ”๊ฐ€ (#304)

* feat: ์ตœ์ดˆ ์‚ฌ์šฉ์ž ์ฑ„๋„ ๊ตฌ๋… ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋„๋ก ๊ตฌํ˜„ (#306)

* feat: ๋ฉ”์‹œ์ง€ ์‘๋‹ต ํ•„๋“œ์— isBookmarked ์ถ”๊ฐ€ (#305)

* hotfix: Calendar modal ๋‹ซํžˆ์ง€ ์•Š๋Š” ๋ฒ„๊ทธ ์ˆ˜์ • (#307)

* feat: ๋ฉ”์ธํŽ˜์ด์ง€์—์„œ ๋ถ๋งˆํฌ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ (#308)

* hotfix: ์ฑ„๋„ ์„ ํƒ์‹œ ๋ฆฌ๋ Œ๋”๋ง ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ (#310)

* hotfix: ์„ ํƒ๋œ ์ฑ„๋„ ๋‹ค์‹œ ์„ ํƒํ–ˆ์„ ๊ฒฝ์šฐ drawer ๋‹ซํžˆ์ง€ ์•Š๋Š” ๋ฒ„๊ทธ ์ˆ˜์ • (#311)

* hotfix: ๋ฉ”์ธํ”ผ๋“œ์—์„œ ๋‚ ์งœ ์ด๋™ ์‹œ, channelId ๋ˆ„๋ฝ๋˜๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ (#312)

Co-authored-by: yeonLog <[email protected]>
Co-authored-by: hyewoncc <[email protected]>
Co-authored-by: yeon-06 <[email protected]>
Co-authored-by: Jaejeung Ko <[email protected]>
Co-authored-by: hyewoncc <[email protected]>
Co-authored-by: ๋ด„ <[email protected]>
Co-authored-by: Richard JEON <[email protected]>
Co-authored-by: JangBomi <[email protected]>
Co-authored-by: yeon-06 <[email protected]>
  • Loading branch information
10 people authored Aug 4, 2022
1 parent 556dddf commit 87b5f1b
Show file tree
Hide file tree
Showing 122 changed files with 4,102 additions and 442 deletions.
14 changes: 11 additions & 3 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,22 @@ dependencies {
implementation 'com.slack.api:slack-api-client:1.23.0'
implementation 'com.querydsl:querydsl-jpa:5.0.0'
implementation 'com.querydsl:querydsl-apt:5.0.0'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'

compileOnly 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'

annotationProcessor 'org.projectlombok:lombok'
implementation 'com.h2database:h2'

runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
testImplementation 'io.rest-assured:rest-assured:4.4.0'

asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}

ext {
Expand Down
2 changes: 1 addition & 1 deletion backend/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ operation::channel-subscription-controller-test/update-order-of-subscribed-chann

์ž์„ธํ•œ ์˜ˆ์‹œ๋Š” https://github.com/woowacourse-teams/2022-pickpick/wiki/๋ฉ”์‹œ์ง€-์กฐํšŒ-API-์‚ฌ์šฉ๋ฒ•์—์„œ ํ™•์ธ

operation::message-controller-test/find-all-message-with-condition[snippets='http-request,request-parameters,http-response,response-fields']
operation::message-controller-test/find-all-message-with-condition[snippets='http-request,request-headers,request-parameters,http-response,response-fields']
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.pickpick.auth.application;

import com.pickpick.auth.support.JwtTokenProvider;
import com.pickpick.auth.ui.dto.LoginResponse;
import com.pickpick.exception.MemberNotFoundException;
import com.pickpick.exception.SlackClientException;
import com.pickpick.member.domain.Member;
import com.pickpick.member.domain.MemberRepository;
import com.slack.api.methods.MethodsClient;
import com.slack.api.methods.SlackApiException;
import com.slack.api.methods.request.oauth.OAuthV2AccessRequest;
import com.slack.api.methods.request.users.UsersIdentityRequest;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class AuthService {

private final String clientId;
private final String clientSecret;
private final String redirectUrl;

private final MemberRepository members;
private final MethodsClient slackClient;
private final JwtTokenProvider jwtTokenProvider;

public AuthService(@Value("${slack.client-id}") final String clientId,
@Value("${slack.client-secret}") final String clientSecret,
@Value("${slack.redirect-url}") final String redirectUrl,
final MemberRepository members,
final MethodsClient slackClient,
final JwtTokenProvider jwtTokenProvider) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.redirectUrl = redirectUrl;
this.members = members;
this.slackClient = slackClient;
this.jwtTokenProvider = jwtTokenProvider;
}

public LoginResponse login(final String code) {
try {
String token = requestSlackToken(code);
String memberSlackId = requestMemberSlackId(token);

Member member = members.findBySlackId(memberSlackId)
.orElseThrow(() -> new MemberNotFoundException(memberSlackId));

boolean isFirstLogin = member.isFirstLogin();
member.markLoggedIn();

return LoginResponse.builder()
.token(jwtTokenProvider.createToken(String.valueOf(member.getId())))
.firstLogin(isFirstLogin)
.build();
} catch (IOException | SlackApiException e) {
throw new SlackClientException(e);
}
}

private String requestSlackToken(final String code) throws IOException, SlackApiException {
OAuthV2AccessRequest request = OAuthV2AccessRequest.builder()
.clientId(clientId)
.clientSecret(clientSecret)
.redirectUri(redirectUrl)
.code(code)
.build();

return slackClient.oauthV2Access(request)
.getAuthedUser()
.getAccessToken();
}

private String requestMemberSlackId(final String token) throws IOException, SlackApiException {
UsersIdentityRequest request = UsersIdentityRequest.builder()
.token(token)
.build();

return slackClient.usersIdentity(request)
.getUser()
.getId();
}

public void verifyToken(final String token) {
jwtTokenProvider.validateToken(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.pickpick.auth.support;

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 {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.pickpick.auth.support;

import com.pickpick.exception.InvalidTokenException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
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 {

private final SecretKey secretKey;
private final long validityInMilliseconds;

public JwtTokenProvider(@Value("${security.jwt.token.secret-key}") final String secretKey,
@Value("${security.jwt.token.expire-length}") final long validityInMilliseconds) {
this.secretKey = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
this.validityInMilliseconds = validityInMilliseconds;
}

public String createToken(final String payload) {
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);

return Jwts.builder()
.setSubject(payload)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}

public void validateToken(final String token) {
try {
parseClaims(token);
} catch (ExpiredJwtException e) {
throw new InvalidTokenException("๋งŒ๋ฃŒ๋œ ํ† ํฐ์ž…๋‹ˆ๋‹ค.");
} catch (Exception e) {
throw new InvalidTokenException("์œ ํšจํ•˜์ง€ ์•Š์€ ํ† ํฐ์ž…๋‹ˆ๋‹ค.");
}
}

public String getPayload(final String token) {
return parseClaims(token)
.getBody()
.getSubject();
}

private Jws<Claims> parseClaims(final String token) {
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token);
}
}
33 changes: 33 additions & 0 deletions backend/src/main/java/com/pickpick/auth/ui/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.pickpick.auth.ui;

import com.pickpick.auth.application.AuthService;
import com.pickpick.auth.ui.dto.LoginResponse;
import com.pickpick.utils.AuthorizationExtractor;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotEmpty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class AuthController {

private final AuthService authService;

public AuthController(final AuthService authService) {
this.authService = authService;
}

@GetMapping("/slack-login")
public LoginResponse login(@RequestParam @NotEmpty final String code) {
return authService.login(code);
}

@GetMapping("/certification")
public void verifyToken(final HttpServletRequest request) {
String token = AuthorizationExtractor.extract(request);
authService.verifyToken(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.pickpick.auth.ui;

import com.pickpick.auth.support.JwtTokenProvider;
import com.pickpick.utils.AuthorizationExtractor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.servlet.HandlerInterceptor;

public class AuthenticationInterceptor implements HandlerInterceptor {

private final JwtTokenProvider jwtTokenProvider;

public AuthenticationInterceptor(final JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}

@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response,
final Object handler) {
if (CorsUtils.isCorsRequest(request)) {
return true;
}

String token = AuthorizationExtractor.extract(request);
jwtTokenProvider.validateToken(token);
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.pickpick.auth.ui;

import com.pickpick.auth.support.AuthenticationPrincipal;
import com.pickpick.auth.support.JwtTokenProvider;
import com.pickpick.utils.AuthorizationExtractor;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
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;

public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {

private final JwtTokenProvider jwtTokenProvider;

public AuthenticationPrincipalArgumentResolver(final JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}

@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) {
String token = AuthorizationExtractor.extract(toHttpServletRequest(webRequest));
jwtTokenProvider.validateToken(token);
return Long.valueOf(jwtTokenProvider.getPayload(token));
}

private HttpServletRequest toHttpServletRequest(final NativeWebRequest webRequest) {
return webRequest.getNativeRequest(HttpServletRequest.class);
}
}

20 changes: 20 additions & 0 deletions backend/src/main/java/com/pickpick/auth/ui/dto/LoginResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.pickpick.auth.ui.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Getter;

@Getter
public class LoginResponse {

private String token;

@JsonProperty("isFirstLogin")
private boolean firstLogin;

@Builder
public LoginResponse(final String token, final boolean firstLogin) {
this.token = token;
this.firstLogin = firstLogin;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.pickpick.exception.MemberNotFoundException;
import com.pickpick.exception.SubscriptionDuplicateException;
import com.pickpick.exception.SubscriptionNotExistException;
import com.pickpick.exception.SubscriptionOrderDuplicateException;
import com.pickpick.member.domain.Member;
import com.pickpick.member.domain.MemberRepository;
import java.util.List;
Expand Down Expand Up @@ -92,13 +93,57 @@ private int getMaxViewOrder(final Long memberId) {
@Transactional
public void updateOrders(final List<ChannelOrderRequest> orderRequests, final Long memberId) {
List<ChannelSubscription> subscribedChannels = channelSubscriptions.findAllByMemberId(memberId);
validateRequest(subscribedChannels, orderRequests);
Map<Long, Integer> ordersByChannelId = getOrdersMap(orderRequests);

for (ChannelSubscription subscribedChannel : subscribedChannels) {
subscribedChannel.changeOrder(ordersByChannelId.get(subscribedChannel.getChannelId()));
}
}

private void validateRequest(final List<ChannelSubscription> subscribedChannels,
final List<ChannelOrderRequest> orderRequests) {
if (isDuplicatedViewOrder(orderRequests)) {
throw new SubscriptionOrderDuplicateException();
}

if (isUnsubscribedChannelOfMember(subscribedChannels, orderRequests)) {
throw new SubscriptionNotExistException("๋ฉค๋ฒ„๊ฐ€ ๊ตฌ๋…ํ•œ ์  ์—†๋Š” ์ฑ„๋„์˜ ์ˆœ์„œ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
}

if (isEverySubscribedChannelNotContain(subscribedChannels, orderRequests)) {
throw new SubscriptionNotExistException("๋ฉค๋ฒ„์˜ ๋ชจ๋“  ๊ตฌ๋… ์ฑ„๋„ ์•„์ด๋””๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.");
}
}

private boolean isDuplicatedViewOrder(final List<ChannelOrderRequest> orderRequests) {
return orderRequests.size() != orderRequests.stream()
.map(ChannelOrderRequest::getOrder)
.distinct()
.count();
}


private boolean isUnsubscribedChannelOfMember(final List<ChannelSubscription> subscribedChannels,
final List<ChannelOrderRequest> orderRequests) {
List<Long> subscribedChannelIds = subscribedChannels.stream()
.map(ChannelSubscription::getChannelId)
.collect(Collectors.toList());

return !orderRequests.stream()
.allMatch(it -> subscribedChannelIds.contains(it.getId()));
}

private boolean isEverySubscribedChannelNotContain(final List<ChannelSubscription> subscribedChannels,
final List<ChannelOrderRequest> orderRequests) {
List<Long> requestChannelId = orderRequests.stream()
.map(ChannelOrderRequest::getId)
.collect(Collectors.toList());

return !subscribedChannels.stream()
.allMatch(it -> requestChannelId.contains(it.getChannelId()));
}

private Map<Long, Integer> getOrdersMap(final List<ChannelOrderRequest> orderRequests) {
return orderRequests.stream()
.collect(Collectors.toMap(ChannelOrderRequest::getId, ChannelOrderRequest::getOrder));
Expand Down
Loading

0 comments on commit 87b5f1b

Please sign in to comment.