Skip to content

Commit

Permalink
[WALL-3905] wojtek/do not subscribe for not authorized websocket (#15047
Browse files Browse the repository at this point in the history
)

* feat: make sure subscrpitions are not called when websocket is not authorized or not opened

* feat: typo

* feat: fixed test

* feat: removed unnecessary file

* feat: fixed loader handling
  • Loading branch information
wojciech-deriv committed Jun 7, 2024
1 parent 3cae2e0 commit f6f87e2
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 13 deletions.
58 changes: 53 additions & 5 deletions packages/api-v2/src/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { useAPIContext } from './APIProvider';

import { getAccountsFromLocalStorage, getActiveLoginIDFromLocalStorage, getToken } from '@deriv/utils';
import useMutation from './useMutation';
import { TSocketResponseData } from '../types';
import { TSocketSubscribableEndpointNames, TSocketResponseData, TSocketRequestPayload } from '../types';
import useAPI from './useAPI';

// Define the type for the context state
type AuthContextType = {
Expand All @@ -19,6 +20,17 @@ type AuthContextType = {
error: unknown;
isSwitching: boolean;
isInitializing: boolean;
subscribe: <T extends TSocketSubscribableEndpointNames>(
name: T,
payload?: TSocketRequestPayload<T>
) =>
| {
subscribe: (
onData: (response: any) => void,
onError: (response: any) => void
) => { unsubscribe?: VoidFunction };
}
| undefined;
};

type LoginToken = {
Expand Down Expand Up @@ -97,17 +109,43 @@ const AuthProvider = ({ loginIDKey, children, cookieTimeout, selectDefaultAccoun

const { mutateAsync } = useMutation('authorize');

const { queryClient, setOnReconnected, setOnConnected } = useAPIContext();
const { queryClient, setOnReconnected, setOnConnected, derivAPI } = useAPIContext();

const [isLoading, setIsLoading] = useState(true);
const [isSwitching, setIsSwitching] = useState(false);
const [isInitializing, setIsInitializing] = useState(true);
const [isSuccess, setIsSuccess] = useState(false);
const [isError, setIsError] = useState(false);
const [isFetching, setIsFetching] = useState(false);
const [isAuthorized, setIsAuthorized] = useState(false);

const [data, setData] = useState<TSocketResponseData<'authorize'> | null>();

const { subscribe: _subscribe } = useAPI();

const subscribe = useCallback(
<T extends TSocketSubscribableEndpointNames>(
name: T,
payload?: TSocketRequestPayload<T>
):
| {
subscribe: (
// The type will be handled by the `useSubscription` hook.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onData: (response: any) => void,
// The type will be handled by the `useSubscription` hook.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onError: (response: any) => void
) => { unsubscribe?: VoidFunction };
}
| undefined => {
if (isAuthorized && derivAPI.connection.readyState == 1) {
return derivAPI?.subscribe({ [name]: 1, subscribe: 1, ...(payload || {}) });
}
},
[derivAPI, isAuthorized]
);

const processAuthorizeResponse = useCallback(
(authorizeResponse: TSocketResponseData<'authorize'>) => {
setData(authorizeResponse);
Expand Down Expand Up @@ -135,8 +173,10 @@ const AuthProvider = ({ loginIDKey, children, cookieTimeout, selectDefaultAccoun
}, []);

useEffect(() => {
setOnReconnected(() => {
mutateAsync({ payload: { authorize: getToken(loginid || '') ?? '' } });
setOnReconnected(async () => {
setIsAuthorized(false);
await mutateAsync({ payload: { authorize: getToken(loginid || '') ?? '' } });
setIsAuthorized(true);
});
}, [loginid]);

Expand All @@ -154,15 +194,18 @@ const AuthProvider = ({ loginIDKey, children, cookieTimeout, selectDefaultAccoun
setIsLoading(true);
setIsInitializing(true);
setIsFetching(true);
setIsAuthorized(false);
await mutateAsync({ payload: { authorize: token || '' } })
.then(res => {
setIsAuthorized(true);
processAuthorizeResponse(res);
setIsLoading(false);
setIsInitializing(false);
setIsSuccess(true);
setLoginid(res?.authorize?.loginid ?? '');
})
.catch(() => {
setIsAuthorized(false);
setIsLoading(false);
setIsInitializing(false);
setIsError(true);
Expand All @@ -175,6 +218,7 @@ const AuthProvider = ({ loginIDKey, children, cookieTimeout, selectDefaultAccoun
})
.catch(() => {
if (isMounted) {
setIsAuthorized(false);
setIsLoading(false);
setIsInitializing(false);
setIsError(true);
Expand All @@ -197,7 +241,9 @@ const AuthProvider = ({ loginIDKey, children, cookieTimeout, selectDefaultAccoun
setIsLoading(true);
setIsSwitching(true);

setIsAuthorized(false);
const authorizeResponse = await mutateAsync({ payload: { authorize: getToken(newLoginId) ?? '' } });
setIsAuthorized(true);
setLoginid(newLoginId);
processAuthorizeResponse(authorizeResponse);

Expand All @@ -224,8 +270,9 @@ const AuthProvider = ({ loginIDKey, children, cookieTimeout, selectDefaultAccoun
loginid,
isSwitching,
isInitializing,
subscribe,
};
}, [data, switchAccount, refetch, isLoading, isError, isFetching, isSuccess, loginid]);
}, [data, switchAccount, refetch, isLoading, isError, isFetching, isSuccess, loginid, subscribe]);

return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
Expand All @@ -234,6 +281,7 @@ export default AuthProvider;

export const useAuthContext = () => {
const context = useContext(AuthContext);

if (!context) {
throw new Error('useAuthContext must be used within APIProvider');
}
Expand Down
6 changes: 4 additions & 2 deletions packages/api-v2/src/__tests__/useSubscription.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import APIProvider from '../APIProvider';
import AuthProvider from '../AuthProvider';
import useSubscription from '../useSubscription';

jest.mock('./../useAPI', () => ({
jest.mock('../AuthProvider', () => ({
__esModule: true,
default() {
...jest.requireActual('../AuthProvider'),

useAuthContext: () => {
return {
subscribe() {
return {
Expand Down
4 changes: 3 additions & 1 deletion packages/api-v2/src/useAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ const useAPI = () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onError: (response: any) => void
) => { unsubscribe?: VoidFunction };
} => derivAPI?.subscribe({ [name]: 1, subscribe: 1, ...(payload || {}) }),
} => {
return derivAPI?.subscribe({ [name]: 1, subscribe: 1, ...(payload || {}) });
},
[derivAPI]
);

Expand Down
6 changes: 3 additions & 3 deletions packages/api-v2/src/useSubscription.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import useAPI from './useAPI';
import { useAuthContext } from './AuthProvider';
import type {
TSocketAcceptableProps,
TSocketError,
Expand All @@ -16,7 +16,7 @@ const useSubscription = <T extends TSocketSubscribableEndpointNames>(name: T, id
const [data, setData] = useState<TSocketResponseData<T>>();
const subscriber = useRef<{ unsubscribe?: VoidFunction }>();
const idle_timeout = useRef<NodeJS.Timeout>();
const { subscribe: _subscribe } = useAPI();
const { subscribe: _subscribe } = useAuthContext();

const subscribe = useCallback(
(...props: TSocketAcceptableProps<T>) => {
Expand All @@ -33,7 +33,7 @@ const useSubscription = <T extends TSocketSubscribableEndpointNames>(name: T, id
}, idle_time);

try {
subscriber.current = _subscribe(name, payload).subscribe(
subscriber.current = _subscribe(name, payload)?.subscribe(
response => {
setData(response);
setIsLoading(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ const TransactionStatus: React.FC<TTransactionStatus> = ({ transactionType }) =>
</div>
<Divider color='#d6dadb' /> {/* --color-grey-5 */}
<div className='wallets-transaction-status__body'>
{!isError && isLoading && (
{isLoading && (
<div className='wallets-transaction-status__loader'>
<Loader />
</div>
)}
{isError && <TransactionStatusError refresh={refresh} />}
{isError && !isLoading && <TransactionStatusError refresh={refresh} />}
{isTransactionStatusSuccessVisible && (
<TransactionStatusSuccess
transactionType={transactionType}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ describe('TransactionStatus component', () => {
it('should render error state correctly for useActiveWalletAccount', async () => {
const mockError = new Error('Test error');

(useCryptoTransactions as jest.Mock).mockImplementation(() => ({
data: [],
error: null,
isLoading: false,
resetData: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
}));

(useActiveWalletAccount as jest.Mock).mockImplementation(() => ({
data: null,
error: mockError,
Expand Down

0 comments on commit f6f87e2

Please sign in to comment.