From 437187084210f12c5be881d8a18884a7fe5c3388 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Fri, 1 Dec 2023 23:17:46 -0800 Subject: [PATCH] chore: Use error filter in rn event source (#322) Pass `errorFilter` to rn eventSource to specify custom retry logic and error handling. --- .../react-native/src/ReactNativeLDClient.ts | 10 +++++++- packages/sdk/react-native/src/platform.ts | 6 +++-- .../src/react-native-sse/EventSource.ts | 25 ++++++++++++++++--- .../src/react-native-sse/types.ts | 1 + 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/sdk/react-native/src/ReactNativeLDClient.ts b/packages/sdk/react-native/src/ReactNativeLDClient.ts index 330f02fb4..4a23619dd 100644 --- a/packages/sdk/react-native/src/ReactNativeLDClient.ts +++ b/packages/sdk/react-native/src/ReactNativeLDClient.ts @@ -1,5 +1,6 @@ import { base64UrlEncode, + BasicLogger, LDClientImpl, type LDContext, type LDOptions, @@ -9,7 +10,14 @@ import platform from './platform'; export default class ReactNativeLDClient extends LDClientImpl { constructor(sdkKey: string, options: LDOptions = {}) { - super(sdkKey, platform, options); + const logger = + options.logger ?? + new BasicLogger({ + level: 'info', + // eslint-disable-next-line no-console + destination: console.log, + }); + super(sdkKey, platform, { ...options, logger }); } override createStreamUriPath(context: LDContext) { diff --git a/packages/sdk/react-native/src/platform.ts b/packages/sdk/react-native/src/platform.ts index f04b74024..f209ce2dd 100644 --- a/packages/sdk/react-native/src/platform.ts +++ b/packages/sdk/react-native/src/platform.ts @@ -22,8 +22,10 @@ import RNEventSource from './react-native-sse'; class PlatformRequests implements Requests { createEventSource(url: string, eventSourceInitDict: EventSourceInitDict): EventSource { - // TODO: add retry logic - return new RNEventSource(url, eventSourceInitDict); + return new RNEventSource(url, { + headers: eventSourceInitDict.headers, + retryAndHandleError: eventSourceInitDict.errorFilter, + }); } fetch(url: string, options?: Options): Promise { diff --git a/packages/sdk/react-native/src/react-native-sse/EventSource.ts b/packages/sdk/react-native/src/react-native-sse/EventSource.ts index 58a4c4409..92ba6944a 100644 --- a/packages/sdk/react-native/src/react-native-sse/EventSource.ts +++ b/packages/sdk/react-native/src/react-native-sse/EventSource.ts @@ -17,8 +17,9 @@ const defaultOptions: EventSourceOptions = { method: 'GET', pollingInterval: 5000, timeout: 0, - timeoutBeforeConnection: 500, + timeoutBeforeConnection: 0, withCredentials: false, + retryAndHandleError: undefined, }; export default class EventSource { @@ -49,6 +50,7 @@ export default class EventSource { private xhr: XMLHttpRequest = new XMLHttpRequest(); private pollTimer: any; private pollingInterval: number; + private retryAndHandleError?: (err: any) => boolean; constructor(url: string, options?: EventSourceOptions) { const opts = { @@ -65,6 +67,7 @@ export default class EventSource { this.body = opts.body; this.debug = opts.debug!; this.pollingInterval = opts.pollingInterval!; + this.retryAndHandleError = opts.retryAndHandleError; this.pollAgain(this.timeoutBeforeConnection, true); } @@ -147,7 +150,21 @@ export default class EventSource { if (this.xhr.readyState === XMLHttpRequest.DONE) { this.logDebug('[EventSource][onreadystatechange][ERROR] Response status error.'); - this.pollAgain(this.pollingInterval, false); + + if (!this.retryAndHandleError) { + // default implementation + this.pollAgain(this.pollingInterval, false); + } else { + // custom retry logic + const shouldRetry = this.retryAndHandleError({ + status: this.xhr.status, + message: this.xhr.responseText, + }); + + if (shouldRetry) { + this.pollAgain(this.pollingInterval, true); + } + } } } }; @@ -290,7 +307,7 @@ export default class EventSource { this.onerror(data); break; case 'retry': - this.onretrying(); + this.onretrying({ delayMillis: this.pollingInterval }); break; default: break; @@ -310,5 +327,5 @@ export default class EventSource { onopen() {} onclose() {} onerror(_err: any) {} - onretrying() {} + onretrying(_e: any) {} } diff --git a/packages/sdk/react-native/src/react-native-sse/types.ts b/packages/sdk/react-native/src/react-native-sse/types.ts index 961547b23..1a417a7db 100644 --- a/packages/sdk/react-native/src/react-native-sse/types.ts +++ b/packages/sdk/react-native/src/react-native-sse/types.ts @@ -53,6 +53,7 @@ export interface EventSourceOptions { body?: any; debug?: boolean; pollingInterval?: number; + retryAndHandleError?: (err: any) => boolean; } type BuiltInEventMap = {