Skip to content

Commit

Permalink
userRepository 마이그레이션
Browse files Browse the repository at this point in the history
  • Loading branch information
woohm402 committed May 22, 2024
1 parent 0244b15 commit f3968b2
Show file tree
Hide file tree
Showing 17 changed files with 283 additions and 122 deletions.
16 changes: 5 additions & 11 deletions apps/snutt-webclient/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ import { implFeedbackSnuttApiRepository } from '@/infrastructures/implFeedbackSn
import { getNotificationRepository } from '@/infrastructures/implNotificationSnuttApiRepository';
import { implSearchSnuttApiRepository } from '@/infrastructures/implSearchSnuttApiRepository';
import { implSemesterSnuttApiRepository } from '@/infrastructures/implSemesterSnuttApiRepository';
import { implUserSnuttApiRepository } from '@/infrastructures/implUserSnuttApiRepository';
import { ErrorPage } from '@/pages/error';
import { Main } from '@/pages/main';
import { MyPage } from '@/pages/mypage';
import { getTimetableRepository } from '@/repositories/timetableRepository';
import { getUserRepository } from '@/repositories/userRepository';
import { getAuthService } from '@/usecases/authService';
import { getColorService } from '@/usecases/colorService';
import { getErrorService } from '@/usecases/errorService';
Expand Down Expand Up @@ -166,17 +166,11 @@ const GlobalStyles = createGlobalStyle`
`;

const getUnauthorizedServices = (ENV: { API_BASE_URL: string; API_KEY: string }) => {
const httpClient = createFetchClient({
baseURL: ENV.API_BASE_URL,
headers: { 'x-access-apikey': ENV.API_KEY },
});

const snuttApi = getSnuttApi(ENV);

const authRepository = implAuthSnuttApiRepository({ snuttApi });
const feedbackRepository = implFeedbackSnuttApiRepository({ snuttApi });
const userRepository = getUserRepository({ httpClient });
const authService = getAuthService({ authRepository, userRepository });
const authService = getAuthService({ authRepository });
const feedbackService = getFeedbackService({ feedbackRepository });

return { authService, feedbackService };
Expand All @@ -198,15 +192,15 @@ const getAuthorizedServices = (

const snuttApi = getSnuttApi(ENV);

const userRepository = getUserRepository({ httpClient });
const userRepository = implUserSnuttApiRepository({ snuttApi });
const authRepository = implAuthSnuttApiRepository({ snuttApi });
const timetableRepository = getTimetableRepository({ httpClient });
const semesterRepository = implSemesterSnuttApiRepository({ snuttApi });
const searchRepository = implSearchSnuttApiRepository({ snuttApi });
const notificationRepository = getNotificationRepository({ snuttApi });
const colorRepository = implColorSnuttApiRepository({ snuttApi });

const userService = getUserService({ repositories: [userRepository] });
const userService = getUserService({ userRepository });
const colorService = getColorService({ colorRepository });
const notificationService = getNotificationService({ notificationRepository });
const searchService = getSearchService({ searchRepository });
Expand All @@ -215,7 +209,7 @@ const getAuthorizedServices = (
const timeMaskService = getTimeMaskService();
const hourMinuteService = getHourMinuteService();
const hourMinutePickerService = getHourMinutePickerService({ services: [hourMinuteService] });
const authService = getAuthService({ authRepository, userRepository });
const authService = getAuthService({ authRepository });
const semesterService = getSemesterService({ semesterRepository });

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { Link } from 'react-router-dom';
import styled from 'styled-components';

import { serviceContext } from '@/contexts/ServiceContext';
import { useTokenAuthContext } from '@/contexts/TokenAuthContext';
import { useGuardContext } from '@/hooks/useGuardContext';
import { queryKey } from '@/utils/query-key-factory';

export const LayoutProfile = () => {
const { data: myInfo } = useMyInfo();

const isTempUser = !myInfo?.local_id && !myInfo?.fb_name;
const isTempUser = myInfo && myInfo.type === 'success' && !myInfo.data.localId && !myInfo.data.facebookName;
const isLoginButton = isTempUser;

return isLoginButton ? (
Expand All @@ -18,17 +18,18 @@ export const LayoutProfile = () => {
</ProfileText>
) : (
<ProfileText to="/mypage" data-testid="layout-my-info">
{myInfo?.local_id ?? myInfo?.fb_name}
{myInfo?.type === 'success' && `${myInfo.data.localId ?? myInfo.data.facebookName}님`}
</ProfileText>
);
};

const useMyInfo = () => {
const { userService } = useGuardContext(serviceContext);
const { token } = useTokenAuthContext();

return useQuery({
queryKey: queryKey('user/info'),
queryFn: () => userService.getUserInfo(),
queryKey: ['UserService', 'getUserInfo', { token }] as const,
queryFn: ({ queryKey }) => userService.getUserInfo(queryKey[2]),
});
};

Expand Down
6 changes: 3 additions & 3 deletions apps/snutt-webclient/src/entities/user.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export interface User {
email?: string;
fb_name: string | null;
email: string | null;
facebookName: string | null;
isAdmin: boolean;
local_id: string | null;
localId: string | null;
notificationCheckedAt: string;
regDate: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ export const implAuthSnuttApiRepository = ({
else return { type: 'error', errcode: data.errcode };
},
signInWithFacebook: async (body) => {
const { status, data } = await snuttApi['POST /auth/login_fb']({ body });
const { status, data } = await snuttApi['POST /auth/login_fb']({
body: {
fb_id: body.facebookId,
fb_token: body.facebookToken,
},
});
if (status === 200) return { type: 'success', data };
else return { type: 'error', errcode: data.errcode };
},
Expand All @@ -29,24 +34,45 @@ export const implAuthSnuttApiRepository = ({
else return { type: 'error', errcode: data.errcode };
},
passwordResetCheckEmail: async (body) => {
const { status, data } = await snuttApi['POST /v1/auth/password/reset/email/check']({ body });
const { status, data } = await snuttApi['POST /v1/auth/password/reset/email/check']({
body: { user_id: body.userId },
});
if (status === 200) return { type: 'success', data };
else return { type: 'error', errcode: data.errcode };
},
sendPasswordResetVerificationEmail: async (body) => {
const { status, data } = await snuttApi['POST /v1/auth/password/reset/email/send']({ body });
const { status, data } = await snuttApi['POST /v1/auth/password/reset/email/send']({
body: { user_email: body.userEmail },
});
if (status === 200) return { type: 'success', data };
else throw data;
},
verifyPasswordResetCode: async (body) => {
const { status, data } = await snuttApi['POST /v1/auth/password/reset/verification/code']({ body });
const { status, data } = await snuttApi['POST /v1/auth/password/reset/verification/code']({
body: { user_id: body.userId, code: body.code },
});
if (status === 200) return { type: 'success' };
else return { type: 'error', errcode: data.errcode };
},
resetPassword: async (body) => {
const { status, data } = await snuttApi['POST /v1/auth/password/reset']({ body });
const { status, data } = await snuttApi['POST /v1/auth/password/reset']({
body: { user_id: body.userId, password: body.password },
});
if (status === 200) return { type: 'success', data };
else throw data;
},
deleteUser: async ({ token }) => {
const { status, data } = await snuttApi['DELETE /v1/user/account']({ token });
if (status === 200) return { type: 'success' };
return { type: 'error', errcode: data.errcode };
},
changePassword: async ({ oldPassword, newPassword, token }) => {
const { status, data } = await snuttApi['PUT /v1/user/password']({
token,
body: { old_password: oldPassword, new_password: newPassword },
});
if (status === 200) return { type: 'success', data };
return { type: 'error', errcode: data.errcode };
},
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { type SnuttApi } from '@sf/snutt-api';

import { type getUserService } from '@/usecases/userService';

export const implUserSnuttApiRepository = ({
snuttApi,
}: {
snuttApi: SnuttApi;
}): Parameters<typeof getUserService>[0]['userRepository'] => {
return {
getUserInfo: async ({ token }) => {
const { status, data } = await snuttApi['GET /v1/user/info']({ token });
if (status === 200)
return {
type: 'success',
data: {
email: data.email ?? null,
facebookName: data.fb_name ?? null,
isAdmin: data.isAdmin,
localId: data.local_id ?? null,
notificationCheckedAt: data.notificationCheckedAt,
regDate: data.regDate,
},
};
return { type: 'error', errcode: data.errcode };
},
addIdPassword: async ({ id, password, token }) => {
const { status, data } = await snuttApi['POST /v1/user/password']({ body: { id, password }, token });
if (status === 200) return { type: 'success', data };
return { type: 'error', errcode: data.errcode };
},
attachFacebookAccount: async ({ facebookId, facebookToken, token }) => {
const { status, data } = await snuttApi['POST /v1/user/facebook']({
body: { fb_id: facebookId, fb_token: facebookToken },
token,
});
if (status === 200) return { type: 'success', data };
return { type: 'error', errcode: data.errcode };
},
detachFacebookAccount: async ({ token }) => {
const { status, data } = await snuttApi['DELETE /v1/user/facebook']({ token });
if (status === 200) return { type: 'success', data };
return { type: 'error', errcode: data.errcode };
},
};
};
3 changes: 1 addition & 2 deletions apps/snutt-webclient/src/mocks/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
} from '@/mocks/fixtures/timetable';
import { mockUsers } from '@/mocks/fixtures/user';
import type { TimetableRepository } from '@/repositories/timetableRepository';
import type { UserRepository } from '@/repositories/userRepository';

import { withValidateAccess } from '../utils/access';

Expand Down Expand Up @@ -78,7 +77,7 @@ export const handlers = [
),
),

http.get<never, never, Awaited<ReturnType<UserRepository['getUserInfo']> | CoreServerError>>(
http.get(
`*/v1/user/info`,
withValidateAccess(({ token }) => {
const user = mockUsers.find((u) => u.auth.token === token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ export const LandingLogin = ({ className, authService, onSignUp }: Props) => {
const handleFacebookSignIn = async (userInfo: ReactFacebookLoginInfo) => {
setErrorMessage('');

const res = await authService.signIn({ type: 'FACEBOOK', fb_id: userInfo.id, fb_token: userInfo.accessToken });
const res = await authService.signIn({
type: 'FACEBOOK',
facebookId: userInfo.id,
facebookToken: userInfo.accessToken,
});

if (res.type === 'success') saveToken(res.data.token, keepSignIn);
else setErrorMessage(res.message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const LoginResetPasswordDialog = ({ open, onClose, authService }: Props)
size="small"
onClick={() =>
checkEmailMutation.mutate(
{ user_id: id },
{ userId: id },
{ onSuccess: ({ type }) => type === 'success' && setStep(Step.EMAIL_CONFIRM) },
)
}
Expand All @@ -94,7 +94,7 @@ export const LoginResetPasswordDialog = ({ open, onClose, authService }: Props)
onClick={() => {
if (checkEmailMutation.data?.type !== 'success') return;
sendCodeEmailMutation.mutate(
{ user_email: checkEmailMutation.data.data.email },
{ userEmail: checkEmailMutation.data.data.email },
{ onSuccess: () => setStep(Step.CODE_INPUT) },
);
}}
Expand Down Expand Up @@ -122,7 +122,7 @@ export const LoginResetPasswordDialog = ({ open, onClose, authService }: Props)
size="small"
onClick={() =>
verifyCodeMutation.mutate(
{ user_id: id, code },
{ userId: id, code },
{ onSuccess: ({ type }) => type === 'success' && setStep(Step.RESET_PASSWORD) },
)
}
Expand All @@ -148,7 +148,7 @@ export const LoginResetPasswordDialog = ({ open, onClose, authService }: Props)
size="small"
disabled={!password}
onClick={() =>
resetPasswordMutation.mutate({ user_id: id, password }, { onSuccess: () => setStep(Step.DONE) })
resetPasswordMutation.mutate({ userId: id, password }, { onSuccess: () => setStep(Step.DONE) })
}
>
완료
Expand All @@ -174,23 +174,23 @@ export const LoginResetPasswordDialog = ({ open, onClose, authService }: Props)
};

const useCheckEmail = (authService: AuthService) => {
return useMutation({ mutationFn: (body: { user_id: string }) => authService.passwordResetCheckEmail(body) });
return useMutation({ mutationFn: (body: { userId: string }) => authService.passwordResetCheckEmail(body) });
};

const useSendCodeEmail = (authService: AuthService) => {
return useMutation({
mutationFn: (body: { user_email: string }) => authService.sendPasswordResetVerificationEmail(body),
mutationFn: (body: { userEmail: string }) => authService.sendPasswordResetVerificationEmail(body),
});
};

const useVerifyCode = (authService: AuthService) => {
return useMutation({
mutationFn: (body: { user_id: string; code: string }) => authService.verifyPasswordResetCode(body),
mutationFn: (body: { userId: string; code: string }) => authService.verifyPasswordResetCode(body),
});
};

const useResetPassword = (authService: AuthService) => {
return useMutation({ mutationFn: (body: { user_id: string; password: string }) => authService.resetPassword(body) });
return useMutation({ mutationFn: (body: { userId: string; password: string }) => authService.resetPassword(body) });
};

const Content = styled(Dialog.Content)`
Expand Down
33 changes: 19 additions & 14 deletions apps/snutt-webclient/src/pages/mypage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ import { Button } from '@/components/button';
import { Layout } from '@/components/layout';
import { envContext } from '@/contexts/EnvContext';
import { serviceContext } from '@/contexts/ServiceContext';
import { useTokenAuthContext } from '@/contexts/TokenAuthContext';
import { useTokenManageContext } from '@/contexts/TokenManageContext';
import { type CoreServerError, getErrorMessage } from '@/entities/error';
import { useGuardContext } from '@/hooks/useGuardContext';
import { queryKey } from '@/utils/query-key-factory';

import { MypageChangePassword } from './mypage-change-password';
import { MypageCloseAccountDialog } from './mypage-close-account-dialog';
Expand All @@ -30,7 +29,7 @@ export const MyPage = () => {
const { mutate: attach } = useAttachFacebook();
const { mutate: detach } = useDetachFacebook();

const isFbOnlyUser = myInfo && userService.isFbOnlyUser(myInfo);
const isFbOnlyUser = myInfo?.type === 'success' ? userService.isFbOnlyUser(myInfo.data) : undefined;

const logout = () => {
clearToken();
Expand Down Expand Up @@ -70,10 +69,10 @@ export const MyPage = () => {
)}
<br />
<br />
{myInfo?.local_id && (
{myInfo?.type === 'success' && myInfo.data.localId && (
<Row data-testid="facebook-row">
<RowLabel>페이스북</RowLabel>
{myInfo?.fb_name ? (
{myInfo.data.facebookName ? (
<Button variant="outlined" color="blue" data-testid="facebook-detach-button" onClick={() => detach()}>
페이스북 연동 해지하기
</Button>
Expand Down Expand Up @@ -114,35 +113,41 @@ export const MyPage = () => {

const useMyInfo = () => {
const { userService } = useGuardContext(serviceContext);
const { token } = useTokenAuthContext();

return useQuery({
queryKey: queryKey('user/info'),
queryFn: () => userService.getUserInfo(),
queryKey: ['UserService', 'getUserInfo', { token }] as const,
queryFn: ({ queryKey }) => userService.getUserInfo(queryKey[2]),
});
};

const useAttachFacebook = () => {
const { saveToken } = useTokenManageContext();
const { userService } = useGuardContext(serviceContext);
const { token } = useTokenAuthContext();

return useMutation({
mutationFn: (userInfo: ReactFacebookLoginInfo) => {
return userService.attachFacebookAccount({ fb_id: userInfo.id, fb_token: userInfo.accessToken });
return userService.attachFacebookAccount({ facebookId: userInfo.id, facebookToken: userInfo.accessToken, token });
},
onSuccess: (data) => {
if (data.type === 'success') saveToken(data.data.token, false);
else alert(data.message);
},
onSuccess: ({ token }) => saveToken(token, false),
onError: (error) => alert(getErrorMessage(error as unknown as CoreServerError)),
});
};

const useDetachFacebook = () => {
const { saveToken } = useTokenManageContext();

const { token } = useTokenAuthContext();
const { userService } = useGuardContext(serviceContext);

return useMutation({
mutationFn: () => userService.detachFacebookAccount(),
onSuccess: ({ token }) => saveToken(token, false),
onError: (error) => alert(getErrorMessage(error as unknown as CoreServerError)),
mutationFn: () => userService.detachFacebookAccount({ token }),
onSuccess: (data) => {
if (data.type === 'success') saveToken(data.data.token, false);
else alert(data.message);
},
});
};

Expand Down
Loading

0 comments on commit f3968b2

Please sign in to comment.