Skip to content

Commit

Permalink
feat: handle reconnection internally
Browse files Browse the repository at this point in the history
  • Loading branch information
yashim-deriv committed Aug 19, 2024
1 parent e42c52c commit f964676
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 43 deletions.
24 changes: 19 additions & 5 deletions src/client-library/deriv-api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
TSocketSubscribeResponseData,
} from '../types/api.types';

export type DerivAPIClientOptions = {
export interface DerivAPIClientOptions {
onOpen?: (e: Event) => void;
onClose?: (e: CloseEvent) => void;
};
}

type DataHandler<T extends TSocketEndpointNames> = (data: TSocketResponseData<T>) => void;

Expand Down Expand Up @@ -57,6 +57,8 @@ export type UnsubscribeHandlerArgs = {
};

export class DerivAPIClient {
options?: DerivAPIClientOptions;
endpoint: string;
websocket: WebSocket;
requestHandler: RequestMap;
subscribeHandler: SubscriptionMap;
Expand All @@ -70,20 +72,25 @@ export class DerivAPIClient {
keepAliveIntervalId: NodeJS.Timeout | null = null;

constructor(endpoint: string, options?: DerivAPIClientOptions) {
this.websocket = new WebSocket(endpoint);
this.options = options;
this.endpoint = endpoint;
this.websocket = new WebSocket(this.endpoint);
this.req_id = 0;
this.requestHandler = new Map();
this.subscribeHandler = new Map();
this.waitForWebSocketOpen = PromiseUtils.createPromise();
this.init();
}

async init() {
this.websocket.addEventListener('open', e => {
if (typeof options?.onOpen === 'function') options.onOpen(e);
if (typeof this.options?.onOpen === 'function') this.options.onOpen(e);
const { resolve } = this.waitForWebSocketOpen;
resolve({});
});

this.websocket.addEventListener('close', e => {
if (typeof options?.onClose === 'function') options.onClose(e);
if (typeof this.options?.onClose === 'function') this.options.onClose(e);
});

this.websocket.addEventListener('message', async response => {
Expand Down Expand Up @@ -258,6 +265,13 @@ export class DerivAPIClient {
}
}

async reconnect() {
this.websocket = new WebSocket(this.endpoint);
this.waitForWebSocketOpen = PromiseUtils.createPromise();
this.init();
this.reinitializeSubscriptions(this.subscribeHandler, this.authorizePayload);
}

isSocketClosingOrClosed() {
return ![2, 3].includes(this.websocket.readyState);
}
Expand Down
28 changes: 16 additions & 12 deletions src/client-library/deriv-api-manager.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import { DerivAPIClient, DerivAPIClientOptions } from './deriv-api-client';

export interface DerivAPIManagerOptions extends DerivAPIClientOptions {
reconnect?: boolean;
}

export class DerivAPIManager {
options?: DerivAPIClientOptions;
activeClient: DerivAPIClient;
clientList: Map<string, DerivAPIClient> = new Map();

constructor(endpoint: string, options?: DerivAPIClientOptions) {
const client = new DerivAPIClient(endpoint, options);
constructor(endpoint: string, options?: DerivAPIClientOptions & DerivAPIManagerOptions) {
const { reconnect = true, ...api_options } = options || {};

const client = new DerivAPIClient(endpoint, {
onClose: () => {
api_options?.onClose;
if (reconnect) this.handleReconnect();
},
onOpen: api_options?.onOpen,
});
this.clientList.set(endpoint, client);
this.activeClient = client;
this.options = options;
Expand All @@ -16,16 +28,8 @@ export class DerivAPIManager {
return this.activeClient;
}

async reconnect() {
const current_url = this.activeClient.websocket.url;
const current_handler = this.activeClient.subscribeHandler;
const current_authorize_payload = this.activeClient.authorizePayload;

this.clientList.delete(current_url);
const reinitialized_instance = new DerivAPIClient(current_url, this.options);
await reinitialized_instance.reinitializeSubscriptions(current_handler, current_authorize_payload);
this.clientList.set(current_url, reinitialized_instance);
this.activeClient = reinitialized_instance;
async handleReconnect() {
await this.activeClient.reconnect();
}

/**
Expand Down
29 changes: 3 additions & 26 deletions src/context/api-context.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext, PropsWithChildren, useEffect, useRef, useState } from 'react';
import { createContext, PropsWithChildren } from 'react';
import { URLUtils } from '@deriv-com/utils';
import { DerivAPIManager } from '../client-library/deriv-api-manager';

Expand All @@ -8,7 +8,7 @@ type APIData = {

export const APIDataContext = createContext<APIData | null>(null);

type ConnectionState = 'idle' | 'active' | 'reconnecting' | 'disconnected';
const derivApiManager = new DerivAPIManager(URLUtils.getWebsocketURL());

/**
* Provides a React Query client and API data context to its child components.
Expand All @@ -20,28 +20,5 @@ type ConnectionState = 'idle' | 'active' | 'reconnecting' | 'disconnected';
* @returns {JSX.Element} The provider component wrapping its children with API data context and React Query client.
*/
export const APIProvider = ({ children }: PropsWithChildren) => {
const [connection_state, setConnectionState] = useState<ConnectionState>('idle');
const derivApiManager = useRef(
new DerivAPIManager(URLUtils.getWebsocketURL(), {
onOpen: () => {
setConnectionState('active');
},
onClose: () => {
setConnectionState('disconnected');
},
})
);

useEffect(() => {
if (connection_state === 'disconnected') {
setConnectionState('reconnecting');
derivApiManager.current.reconnect();
}
}, [connection_state]);

return (
<APIDataContext.Provider value={{ derivAPIClient: derivApiManager.current }}>
{children}
</APIDataContext.Provider>
);
return <APIDataContext.Provider value={{ derivAPIClient: derivApiManager }}>{children}</APIDataContext.Provider>;
};

0 comments on commit f964676

Please sign in to comment.