diff --git a/front/src/AppWrapper.tsx b/front/src/AppWrapper.tsx index 94076c583..d3784e6ab 100644 --- a/front/src/AppWrapper.tsx +++ b/front/src/AppWrapper.tsx @@ -3,6 +3,7 @@ import { BrowserRouter } from 'react-router-dom'; import { ApolloProvider } from './providers/apollo/ApolloProvider'; import { AppThemeProvider } from './providers/theme/AppThemeProvider'; +import { UserProvider } from './providers/user/UserProvider'; import { App } from './App'; export function AppWrapper() { @@ -11,7 +12,9 @@ export function AppWrapper() { - + + + diff --git a/front/src/modules/apollo/hooks/useApolloFactory.ts b/front/src/modules/apollo/hooks/useApolloFactory.ts new file mode 100644 index 000000000..793145ec4 --- /dev/null +++ b/front/src/modules/apollo/hooks/useApolloFactory.ts @@ -0,0 +1,57 @@ +import { useEffect, useMemo, useRef } from 'react'; +import { InMemoryCache, NormalizedCacheObject } from '@apollo/client'; +import { useRecoilState } from 'recoil'; + +import { tokenPairState } from '@/auth/states/tokenPairState'; +import { CommentThreadTarget } from '~/generated/graphql'; + +import { ApolloFactory } from '../services/apollo.factory'; + +export function useApolloFactory() { + const apolloRef = useRef | null>(null); + + const [tokenPair, setTokenPair] = useRecoilState(tokenPairState); + + const apolloClient = useMemo(() => { + apolloRef.current = new ApolloFactory({ + uri: `${process.env.REACT_APP_API_URL}`, + cache: new InMemoryCache({ + typePolicies: { + CommentThread: { + fields: { + commentThreadTargets: { + merge( + existing: CommentThreadTarget[] = [], + incoming: CommentThreadTarget[], + ) { + return [...incoming]; + }, + }, + }, + }, + }, + }), + defaultOptions: { + query: { + fetchPolicy: 'cache-first', + }, + }, + onTokenPairChange(tokenPair) { + setTokenPair(tokenPair); + }, + onUnauthenticatedError() { + setTokenPair(null); + }, + }); + + return apolloRef.current.getClient(); + }, [setTokenPair]); + + useEffect(() => { + if (apolloRef.current) { + apolloRef.current.updateTokenPair(tokenPair); + } + }, [tokenPair]); + + return apolloClient; +} diff --git a/front/src/modules/apollo/hooks/useApolloMocked.ts b/front/src/modules/apollo/hooks/useApolloMocked.ts new file mode 100644 index 000000000..91b706db4 --- /dev/null +++ b/front/src/modules/apollo/hooks/useApolloMocked.ts @@ -0,0 +1,44 @@ +import { useMemo } from 'react'; +import { + ApolloClient, + ApolloLink, + createHttpLink, + from, + InMemoryCache, +} from '@apollo/client'; + +import { mockedCompaniesData } from '~/testing/mock-data/companies'; +import { mockedUsersData } from '~/testing/mock-data/users'; + +export default function useApolloMocked() { + const mockedClient = useMemo(() => { + const apiLink = createHttpLink({ + uri: `${process.env.REACT_APP_API_URL}`, + }); + + const mockLink = new ApolloLink((operation, forward) => { + return forward(operation).map((response) => { + if (operation.operationName === 'GetCompanies') { + return { data: { companies: mockedCompaniesData } }; + } + if (operation.operationName === 'GetCurrentUser') { + return { data: { users: [mockedUsersData[0]] } }; + } + + return response; + }); + }); + + return new ApolloClient({ + link: from([mockLink, apiLink]), + cache: new InMemoryCache(), + defaultOptions: { + query: { + fetchPolicy: 'cache-first', + }, + }, + }); + }, []); + + return mockedClient; +} diff --git a/front/src/providers/apollo/interfaces/apollo-manager.interface.ts b/front/src/modules/apollo/interfaces/apolloManager.interface.ts similarity index 55% rename from front/src/providers/apollo/interfaces/apollo-manager.interface.ts rename to front/src/modules/apollo/interfaces/apolloManager.interface.ts index f996688e8..8878734e2 100644 --- a/front/src/providers/apollo/interfaces/apollo-manager.interface.ts +++ b/front/src/modules/apollo/interfaces/apolloManager.interface.ts @@ -1,5 +1,8 @@ import { ApolloClient } from '@apollo/client'; +import { AuthTokenPair } from '~/generated/graphql'; + export interface ApolloManager { getClient(): ApolloClient; + updateTokenPair(tokenPair: AuthTokenPair | null): void; } diff --git a/front/src/providers/apollo/apollo.factory.ts b/front/src/modules/apollo/services/apollo.factory.ts similarity index 85% rename from front/src/providers/apollo/apollo.factory.ts rename to front/src/modules/apollo/services/apollo.factory.ts index 0dcb0358a..f89543b56 100644 --- a/front/src/providers/apollo/apollo.factory.ts +++ b/front/src/modules/apollo/services/apollo.factory.ts @@ -14,13 +14,12 @@ import { RetryLink } from '@apollo/client/link/retry'; import { Observable } from '@apollo/client/utilities'; import { renewToken } from '@/auth/services/AuthService'; -import { tokenService } from '@/auth/services/TokenService'; +import { AuthTokenPair } from '~/generated/graphql'; -import { assertNotNull } from '../../modules/utils/assert'; -import { promiseToObservable } from '../../modules/utils/promise-to-observable'; - -import { ApolloManager } from './interfaces/apollo-manager.interface'; -import { loggerLink } from './logger'; +import { loggerLink } from '../../utils/apollo-logger'; +import { assertNotNull } from '../../utils/assert'; +import { promiseToObservable } from '../../utils/promise-to-observable'; +import { ApolloManager } from '../interfaces/apolloManager.interface'; const logger = loggerLink(() => 'Twenty'); @@ -35,17 +34,20 @@ const resolvePendingRequests = () => { export interface Options extends ApolloClientOptions { onError?: (err: GraphQLErrors | undefined) => void; onNetworkError?: (err: Error | ServerParseError | ServerError) => void; + onTokenPairChange?: (tokenPair: AuthTokenPair) => void; onUnauthenticatedError?: () => void; } export class ApolloFactory implements ApolloManager { private client: ApolloClient; + private tokenPair: AuthTokenPair | null = null; constructor(opts: Options) { const { uri, onError: onErrorCb, onNetworkError, + onTokenPairChange, onUnauthenticatedError, ...options } = opts; @@ -56,13 +58,11 @@ export class ApolloFactory implements ApolloManager { }); const authLink = setContext(async (_, { headers }) => { - const credentials = tokenService.getTokenPair(); - return { headers: { ...headers, - authorization: credentials?.accessToken - ? `Bearer ${credentials?.accessToken}` + authorization: this.tokenPair?.accessToken.token + ? `Bearer ${this.tokenPair?.accessToken.token}` : '', }, }; @@ -93,8 +93,9 @@ export class ApolloFactory implements ApolloManager { if (!isRefreshing) { isRefreshing = true; forward$ = promiseToObservable( - renewToken(uri) - .then(() => { + renewToken(uri, this.tokenPair) + .then((tokens) => { + onTokenPairChange?.(tokens); resolvePendingRequests(); return true; }) @@ -161,6 +162,10 @@ export class ApolloFactory implements ApolloManager { }); } + updateTokenPair(tokenPair: AuthTokenPair | null) { + this.tokenPair = tokenPair; + } + getClient() { return this.client; } diff --git a/front/src/modules/auth/hooks/useAuth.ts b/front/src/modules/auth/hooks/useAuth.ts index 26fbb3f97..3584a0f07 100644 --- a/front/src/modules/auth/hooks/useAuth.ts +++ b/front/src/modules/auth/hooks/useAuth.ts @@ -3,11 +3,12 @@ import { useRecoilState } from 'recoil'; import { useChallengeMutation, useVerifyMutation } from '~/generated/graphql'; -import { tokenService } from '../services/TokenService'; import { currentUserState } from '../states/currentUserState'; import { isAuthenticatingState } from '../states/isAuthenticatingState'; +import { tokenPairState } from '../states/tokenPairState'; export function useAuth() { + const [, setTokenPair] = useRecoilState(tokenPairState); const [, setCurrentUser] = useRecoilState(currentUserState); const [, setIsAuthenticating] = useRecoilState(isAuthenticatingState); @@ -50,14 +51,14 @@ export function useAuth() { throw new Error('No verify result'); } - tokenService.setTokenPair(verifyResult.data?.verify.tokens); + setTokenPair(verifyResult.data?.verify.tokens); setIsAuthenticating(false); setCurrentUser(verifyResult.data?.verify.user); return verifyResult.data?.verify; }, - [setCurrentUser, setIsAuthenticating, verify], + [setCurrentUser, setIsAuthenticating, setTokenPair, verify], ); const handleLogin = useCallback( @@ -70,8 +71,8 @@ export function useAuth() { ); const handleLogout = useCallback(() => { - tokenService.removeTokenPair(); - }, []); + setTokenPair(null); + }, [setTokenPair]); return { challenge: handleChallenge, diff --git a/front/src/modules/auth/hooks/useFetchCurrentUser.ts b/front/src/modules/auth/hooks/useFetchCurrentUser.ts new file mode 100644 index 000000000..e21997f5d --- /dev/null +++ b/front/src/modules/auth/hooks/useFetchCurrentUser.ts @@ -0,0 +1,26 @@ +import { useEffect } from 'react'; +import jwt from 'jwt-decode'; +import { useRecoilState } from 'recoil'; + +import { useGetCurrentUserQuery } from '~/generated/graphql'; + +import { currentUserState } from '../states/currentUserState'; +import { tokenPairState } from '../states/tokenPairState'; + +export function useFetchCurrentUser() { + const [, setCurrentUser] = useRecoilState(currentUserState); + const [tokenPair] = useRecoilState(tokenPairState); + const userId = tokenPair?.accessToken.token + ? jwt<{ sub: string }>(tokenPair.accessToken.token).sub + : null; + const { data } = useGetCurrentUserQuery({ + variables: { uuid: userId }, + }); + const user = data?.users?.[0]; + + useEffect(() => { + if (user) { + setCurrentUser(user); + } + }, [user, setCurrentUser]); +} diff --git a/front/src/modules/auth/hooks/useIsLogged.ts b/front/src/modules/auth/hooks/useIsLogged.ts index 16b05eb51..49bb0129c 100644 --- a/front/src/modules/auth/hooks/useIsLogged.ts +++ b/front/src/modules/auth/hooks/useIsLogged.ts @@ -1,21 +1,9 @@ -import { useEffect, useState } from 'react'; +import { useRecoilState } from 'recoil'; -import { cookieStorage } from '@/utils/cookie-storage'; +import { tokenPairState } from '../states/tokenPairState'; export function useIsLogged(): boolean { - const [value, setValue] = useState( - cookieStorage.getItem('accessToken'), - ); + const [tokenPair] = useRecoilState(tokenPairState); - useEffect(() => { - const updateValue = (newValue: string | undefined) => setValue(newValue); - - cookieStorage.addEventListener('accessToken', updateValue); - - return () => { - cookieStorage.removeEventListener('accessToken', updateValue); - }; - }, []); - - return !!value; + return !!tokenPair; } diff --git a/front/src/modules/auth/services/AuthService.ts b/front/src/modules/auth/services/AuthService.ts index 3d5bb9c4a..af32226a0 100644 --- a/front/src/modules/auth/services/AuthService.ts +++ b/front/src/modules/auth/services/AuthService.ts @@ -5,17 +5,14 @@ import { InMemoryCache, UriFunction, } from '@apollo/client'; -import jwt from 'jwt-decode'; -import { cookieStorage } from '@/utils/cookie-storage'; +import { loggerLink } from '@/utils/apollo-logger'; import { + AuthTokenPair, RenewTokenDocument, RenewTokenMutation, RenewTokenMutationVariables, } from '~/generated/graphql'; -import { loggerLink } from '~/providers/apollo/logger'; - -import { tokenService } from './TokenService'; const logger = loggerLink(() => 'Twenty-Refresh'); @@ -60,29 +57,15 @@ const renewTokenMutation = async ( * @param uri string | UriFunction | undefined * @returns TokenPair */ -export const renewToken = async (uri: string | UriFunction | undefined) => { - const tokenPair = tokenService.getTokenPair(); - +export const renewToken = async ( + uri: string | UriFunction | undefined, + tokenPair: AuthTokenPair | undefined | null, +) => { if (!tokenPair) { throw new Error('Refresh token is not defined'); } - const data = await renewTokenMutation(uri, tokenPair.refreshToken); + const data = await renewTokenMutation(uri, tokenPair.refreshToken.token); - tokenService.setTokenPair(data.renewToken.tokens); - - return data.renewToken; -}; - -export const getUserIdFromToken: () => string | null = () => { - const accessToken = cookieStorage.getItem('accessToken'); - if (!accessToken) { - return null; - } - - try { - return jwt<{ sub: string }>(accessToken).sub; - } catch (error) { - return null; - } + return data.renewToken.tokens; }; diff --git a/front/src/modules/auth/services/TokenService.ts b/front/src/modules/auth/services/TokenService.ts deleted file mode 100644 index 9834ec88e..000000000 --- a/front/src/modules/auth/services/TokenService.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { cookieStorage } from '@/utils/cookie-storage'; -import { AuthTokenPair } from '~/generated/graphql'; - -export class TokenService { - getTokenPair() { - const accessToken = cookieStorage.getItem('accessToken'); - const refreshToken = cookieStorage.getItem('refreshToken'); - - if (!accessToken || !refreshToken) { - return null; - } - - return { - accessToken, - refreshToken, - }; - } - - setTokenPair(tokens: AuthTokenPair) { - cookieStorage.setItem('accessToken', tokens.accessToken.token, { - secure: true, - }); - cookieStorage.setItem('refreshToken', tokens.refreshToken.token, { - secure: true, - }); - } - - removeTokenPair() { - cookieStorage.removeItem('accessToken'); - cookieStorage.removeItem('refreshToken'); - } -} - -export const tokenService = new TokenService(); diff --git a/front/src/modules/auth/services/__tests__/AuthService.test.tsx b/front/src/modules/auth/services/__tests__/AuthService.test.tsx deleted file mode 100644 index 1225cf744..000000000 --- a/front/src/modules/auth/services/__tests__/AuthService.test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { cookieStorage } from '@/utils/cookie-storage'; - -import { getUserIdFromToken } from '../AuthService'; - -it('getUserIdFromToken returns null when the token is not present', async () => { - const userId = getUserIdFromToken(); - expect(userId).toBeNull(); -}); - -it('getUserIdFromToken returns null when the token is not valid', async () => { - cookieStorage.setItem('accessToken', 'xxx-invalid-access'); - const userId = getUserIdFromToken(); - expect(userId).toBeNull(); -}); - -it('getUserIdFromToken returns the right userId when the token is valid', async () => { - cookieStorage.setItem( - 'accessToken', - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzNzRmZTNhNS1kZjFlLTQxMTktYWZlMC0yYTYyYTJiYTQ4MWUiLCJ3b3Jrc3BhY2VJZCI6InR3ZW50eS03ZWQ5ZDIxMi0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJpYXQiOjE2ODY5OTI0ODgsImV4cCI6MTY4Njk5Mjc4OH0.IO7U5G14IrrQriw3JjrKVxmZgd6XKL6yUIwuNe_R55E', - ); - const userId = getUserIdFromToken(); - expect(userId).toBe('374fe3a5-df1e-4119-afe0-2a62a2ba481e'); -}); - -afterEach(() => { - cookieStorage.clear(); -}); diff --git a/front/src/modules/auth/services/__tests__/TokenService.test.tsx b/front/src/modules/auth/services/__tests__/TokenService.test.tsx deleted file mode 100644 index 47c3496cb..000000000 --- a/front/src/modules/auth/services/__tests__/TokenService.test.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import Cookies from 'js-cookie'; - -import { tokenService } from '../TokenService'; - -const tokenPair = { - accessToken: { - token: - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzNzRmZTNhNS1kZjFlLTQxMTktYWZlMC0yYTYyYTJiYTQ4MWUiLCJ3b3Jrc3BhY2VJZCI6InR3ZW50eS03ZWQ5ZDIxMi0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJpYXQiOjE2ODY5OTMxODIsImV4cCI6MTY4Njk5MzQ4Mn0.F_FD6nJ5fssR_47v2XFhtzqjr-wrEQpqaWVq8iIlLJw', - expiresAt: '2023-06-17T09:18:02.942Z', - }, - refreshToken: { - token: - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzNzRmZTNhNS1kZjFlLTQxMTktYWZlMC0yYTYyYTJiYTQ4MWUiLCJpYXQiOjE2ODY5OTMxODIsImV4cCI6OTQ2Mjk5MzE4MiwianRpIjoiNzBmMWNhMjctOTYxYi00ZGZlLWEwOTUtMTY2OWEwOGViMTVjIn0.xEdX9dOGzrPHrPsivQYB9ipYGJH-mJ7GSIVPacmIzfY', - expiresAt: '2023-09-15T09:13:02.952Z', - }, -}; - -it('getTokenPair is fullfiled when token is present', () => { - tokenService.setTokenPair(tokenPair); - - // Otherwise the test will fail because Cookies-js seems to be async but functions aren't promises - setTimeout(() => { - expect(tokenService.getTokenPair()).toBe({ - accessToken: tokenPair.accessToken, - refreshToken: tokenPair.refreshToken, - }); - }, 10); -}); - -it('getTokenPair is null when token is not set', () => { - expect(tokenService.getTokenPair()).toBeNull(); -}); - -it('removeTokenPair clean cookie storage', () => { - tokenService.setTokenPair(tokenPair); - tokenService.removeTokenPair(); - expect(tokenService.getTokenPair()).toBeNull(); -}); - -afterEach(() => { - Cookies.remove('accessToken'); - Cookies.remove('refreshToken'); -}); diff --git a/front/src/modules/auth/states/tokenPairState.ts b/front/src/modules/auth/states/tokenPairState.ts new file mode 100644 index 000000000..24bc61a91 --- /dev/null +++ b/front/src/modules/auth/states/tokenPairState.ts @@ -0,0 +1,29 @@ +import { atom, AtomEffect } from 'recoil'; + +import { cookieStorage } from '@/utils/cookie-storage'; +import { AuthTokenPair } from '~/generated/graphql'; + +const cookieStorageEffect = + (key: string): AtomEffect => + ({ setSelf, onSet }) => { + const savedValue = cookieStorage.getItem(key); + if (savedValue != null) { + setSelf(JSON.parse(savedValue)); + } + + onSet((newValue, _, isReset) => { + if (!newValue) { + cookieStorage.removeItem(key); + return; + } + isReset + ? cookieStorage.removeItem(key) + : cookieStorage.setItem(key, JSON.stringify(newValue)); + }); + }; + +export const tokenPairState = atom({ + key: 'tokenPairState', + default: null, + effects: [cookieStorageEffect('tokenPair')], +}); diff --git a/front/src/providers/apollo/logger/format-title.ts b/front/src/modules/utils/apollo-logger/format-title.ts similarity index 100% rename from front/src/providers/apollo/logger/format-title.ts rename to front/src/modules/utils/apollo-logger/format-title.ts diff --git a/front/src/providers/apollo/logger/index.ts b/front/src/modules/utils/apollo-logger/index.ts similarity index 100% rename from front/src/providers/apollo/logger/index.ts rename to front/src/modules/utils/apollo-logger/index.ts diff --git a/front/src/providers/apollo/logger/operation-type.ts b/front/src/modules/utils/apollo-logger/operation-type.ts similarity index 100% rename from front/src/providers/apollo/logger/operation-type.ts rename to front/src/modules/utils/apollo-logger/operation-type.ts diff --git a/front/src/modules/utils/cookie-storage.ts b/front/src/modules/utils/cookie-storage.ts index ab98fa162..0fde168a0 100644 --- a/front/src/modules/utils/cookie-storage.ts +++ b/front/src/modules/utils/cookie-storage.ts @@ -1,12 +1,6 @@ import Cookies, { CookieAttributes } from 'js-cookie'; -type Listener = ( - newValue: string | undefined, - oldValue: string | undefined, -) => void; - class CookieStorage { - private listeners: Record = {}; private keys: Set = new Set(); getItem(key: string): string | undefined { @@ -14,49 +8,18 @@ class CookieStorage { } setItem(key: string, value: string, attributes?: CookieAttributes): void { - const oldValue = this.getItem(key); - this.keys.add(key); Cookies.set(key, value, attributes); - this.dispatch(key, value, oldValue); } removeItem(key: string): void { - const oldValue = this.getItem(key); - this.keys.delete(key); Cookies.remove(key); - this.dispatch(key, undefined, oldValue); } clear(): void { this.keys.forEach((key) => this.removeItem(key)); } - - private dispatch( - key: string, - newValue: string | undefined, - oldValue: string | undefined, - ): void { - if (this.listeners[key]) { - this.listeners[key].forEach((callback) => callback(newValue, oldValue)); - } - } - - addEventListener(key: string, callback: Listener): void { - if (!this.listeners[key]) { - this.listeners[key] = []; - } - this.listeners[key].push(callback); - } - - removeEventListener(key: string, callback: Listener): void { - if (this.listeners[key]) { - this.listeners[key] = this.listeners[key].filter( - (listener) => listener !== callback, - ); - } - } } export const cookieStorage = new CookieStorage(); diff --git a/front/src/providers/apollo/ApolloProvider.tsx b/front/src/providers/apollo/ApolloProvider.tsx index baceed4b8..8c72dca7b 100644 --- a/front/src/providers/apollo/ApolloProvider.tsx +++ b/front/src/providers/apollo/ApolloProvider.tsx @@ -1,18 +1,20 @@ import { ApolloProvider as ApolloProviderBase } from '@apollo/client'; import { useRecoilState } from 'recoil'; +import { useApolloFactory } from '@/apollo/hooks/useApolloFactory'; +import useApolloMocked from '@/apollo/hooks/useApolloMocked'; import { isMockModeState } from '@/auth/states/isMockModeState'; -import { apolloClient } from './apollo-client'; -import { mockClient } from './mock-client'; - export const ApolloProvider: React.FC = ({ children, }) => { + const apolloClient = useApolloFactory(); + const mockedClient = useApolloMocked(); + const [isMockMode] = useRecoilState(isMockModeState); return ( - + {children} ); diff --git a/front/src/providers/apollo/apollo-client.ts b/front/src/providers/apollo/apollo-client.ts deleted file mode 100644 index ec415b977..000000000 --- a/front/src/providers/apollo/apollo-client.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { InMemoryCache } from '@apollo/client'; - -import { tokenService } from '@/auth/services/TokenService'; -import { CommentThreadTarget } from '~/generated/graphql'; - -import { ApolloFactory } from './apollo.factory'; - -const apollo = new ApolloFactory({ - uri: `${process.env.REACT_APP_API_URL}`, - cache: new InMemoryCache({ - typePolicies: { - CommentThread: { - fields: { - commentThreadTargets: { - merge( - existing: CommentThreadTarget[] = [], - incoming: CommentThreadTarget[], - ) { - return [...incoming]; - }, - }, - }, - }, - }, - }), - defaultOptions: { - query: { - fetchPolicy: 'cache-first', - }, - }, - onUnauthenticatedError() { - tokenService.removeTokenPair(); - }, -}); - -export const apolloClient = apollo.getClient(); diff --git a/front/src/providers/apollo/mock-client.ts b/front/src/providers/apollo/mock-client.ts deleted file mode 100644 index adbf3756c..000000000 --- a/front/src/providers/apollo/mock-client.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { - ApolloClient, - ApolloLink, - createHttpLink, - from, - InMemoryCache, -} from '@apollo/client'; - -import { mockedCompaniesData } from '~/testing/mock-data/companies'; -import { mockedUsersData } from '~/testing/mock-data/users'; - -const apiLink = createHttpLink({ - uri: `${process.env.REACT_APP_API_URL}`, -}); - -const mockLink = new ApolloLink((operation, forward) => { - return forward(operation).map((response) => { - if (operation.operationName === 'GetCompanies') { - return { data: { companies: mockedCompaniesData } }; - } - if (operation.operationName === 'Verify') { - return { data: { user: [mockedUsersData[0]], tokens: {} } }; - } - return response; - }); -}); - -export const mockClient = new ApolloClient({ - link: from([mockLink, apiLink]), - cache: new InMemoryCache(), - defaultOptions: { - query: { - fetchPolicy: 'cache-first', - }, - }, -}); diff --git a/front/src/providers/user/UserProvider.tsx b/front/src/providers/user/UserProvider.tsx new file mode 100644 index 000000000..88faf52f4 --- /dev/null +++ b/front/src/providers/user/UserProvider.tsx @@ -0,0 +1,9 @@ +import { useFetchCurrentUser } from '@/auth/hooks/useFetchCurrentUser'; + +export const UserProvider: React.FC = ({ + children, +}) => { + useFetchCurrentUser(); + + return <>{children}; +}; diff --git a/front/src/testing/graphqlMocks.ts b/front/src/testing/graphqlMocks.ts index e31ad2ace..476f55283 100644 --- a/front/src/testing/graphqlMocks.ts +++ b/front/src/testing/graphqlMocks.ts @@ -11,7 +11,6 @@ import { GET_CURRENT_USER } from '@/users/services'; import { GetCompaniesQuery, GetPeopleQuery, - GetUsersQuery, SearchCompanyQuery, SearchUserQuery, } from '~/generated/graphql'; @@ -73,19 +72,9 @@ export const graphqlMocks = [ ); }), graphql.query(getOperationName(GET_CURRENT_USER) ?? '', (req, res, ctx) => { - const customWhere = { - ...req.variables.where, - id: { - equals: req.variables.uuid, - }, - }; - - const returnedMockedData = filterAndSortData< - GetUsersQuery['findManyUser'][0] - >(mockedUsersData, customWhere, req.variables.orderBy, req.variables.limit); return res( ctx.data({ - users: returnedMockedData, + users: [mockedUsersData[0]], }), ); }), diff --git a/front/src/testing/renderWrappers.tsx b/front/src/testing/renderWrappers.tsx index d95a19d81..c41f9e977 100644 --- a/front/src/testing/renderWrappers.tsx +++ b/front/src/testing/renderWrappers.tsx @@ -4,6 +4,7 @@ import { ApolloProvider } from '@apollo/client'; import { RecoilRoot } from 'recoil'; import { DefaultLayout } from '@/ui/layout/DefaultLayout'; +import { UserProvider } from '~/providers/user/UserProvider'; import { ComponentStorybookLayout } from './ComponentStorybookLayout'; import { FullHeightStorybookLayout } from './FullHeightStorybookLayout'; @@ -17,11 +18,13 @@ export function getRenderWrapperForPage( return ( - - - {children} - - + + + + {children} + + + );