Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reverts - Adds Response Time out Error to RN SDKs #725

Merged
merged 1 commit into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Reverts - Adds Response Time out Error to RN SDKs
  • Loading branch information
Ariflo committed Mar 15, 2024
commit be42797970ed6fbf8ccb49f033b6a05f47a48dbc
7 changes: 0 additions & 7 deletions packages/@magic-sdk/provider/src/core/sdk-exceptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,6 @@ export function createModalNotReadyError() {
return new MagicSDKError(SDKErrorCode.ModalNotReady, 'Modal is not ready.');
}

export function createResponseTimeoutError(methodType: string, messageId: number) {
return new MagicSDKError(
SDKErrorCode.ResponseTimeout,
`Response timed out for method: ${methodType} with message id: ${messageId}`,
);
}

export function createMalformedResponseError() {
return new MagicSDKError(SDKErrorCode.MalformedResponse, 'Response from the Magic iframe is malformed.');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ test('Creates a `MODAL_NOT_READY` error', async () => {
errorAssertions(error, 'MODAL_NOT_READY', 'Modal is not ready.');
});

test('Creates a `RESPONSE_TIMEOUT` error', async () => {
const { createResponseTimeoutError } = require('../../../../src/core/sdk-exceptions');
const error = createResponseTimeoutError('test_method', 123);
errorAssertions(error, 'RESPONSE_TIMEOUT', 'Response timed out for method: test_method with message id: 123');
});

test('Creates a `MALFORMED_RESPONSE` error', async () => {
const { createMalformedResponseError } = require('../../../../src/core/sdk-exceptions');
const error = createMalformedResponseError();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@

import browserEnv from '@ikscodes/browser-env';
import { MagicIncomingWindowMessage, MagicOutgoingWindowMessage, JsonRpcRequestPayload } from '@magic-sdk/types';
import { create } from 'lodash';
import { createViewController, TestViewController } from '../../../factories';
import { JsonRpcResponse } from '../../../../src/core/json-rpc';
import * as storage from '../../../../src/util/storage';
import * as webCryptoUtils from '../../../../src/util/web-crypto';
import * as deviceShareWebCryptoUtils from '../../../../src/util/device-share-web-crypto';
import { SDKEnvironment } from '../../../../src/core/sdk-environment';
import { createModalNotReadyError, createResponseTimeoutError } from '../../../../src/core/sdk-exceptions';
import { createModalNotReadyError } from '../../../../src/core/sdk-exceptions';

/**
* Create a dummy request payload.
Expand Down Expand Up @@ -263,24 +262,6 @@ test('throws MODAL_NOT_READY error when not connected to the internet', async ()
}
});

test('throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => {
const eventWithRt = { data: { ...responseEvent().data } };
const { handlerSpy, onSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, eventWithRt],
]);

// @ts-ignore protected variable
viewController.responseTimeout = 1;

const payload = requestPayload();

try {
await viewController.post(MagicOutgoingWindowMessage.MAGIC_HANDLE_REQUEST, payload);
} catch (e) {
expect(e).toEqual(createResponseTimeoutError('eth_accounts', 1));
}
});

test('does not call web crypto api if platform is not web', async () => {
SDKEnvironment.platform = 'react-native';
const eventWithRt = { data: { ...responseEvent().data } };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useCallback, useMemo, useEffect } from 'react';
import { Linking, StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview';
import { SafeAreaView } from 'react-native-safe-area-context';
import { ViewController, createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider';
import { ViewController, createModalNotReadyError } from '@magic-sdk/provider';
import { MagicMessageEvent } from '@magic-sdk/types';
import { isTypedArray } from 'lodash';
import Global = NodeJS.Global;
Expand Down Expand Up @@ -62,7 +62,6 @@ export class ReactNativeWebViewController extends ViewController {
private webView!: WebView | null;
private container!: ViewWrapper | null;
private styles: any;
private messageTimeouts = new Map();

protected init() {
this.webView = null;
Expand Down Expand Up @@ -140,18 +139,6 @@ export class ReactNativeWebViewController extends ViewController {
}, [show]);

const handleWebViewMessage = useCallback((event: any) => {
const data = JSON.parse(event.nativeEvent.data);

if (data?.response?.id) {
const messageId = data.response.id;

// Clear timeout if message is received in time
if (this.messageTimeouts.has(messageId)) {
clearTimeout(this.messageTimeouts.get(messageId));
this.messageTimeouts.delete(messageId);
}
}
// Process the received message
this.handleReactNativeWebViewMessage(event);
}, []);

Expand Down Expand Up @@ -229,22 +216,7 @@ export class ReactNativeWebViewController extends ViewController {
}

protected async _post(data: any) {
// Safely access `method` and `id` from `payload`, defaulting to undefined if not present
const methodType = data.payload?.method;
const messageId = data.payload?.id;

if (this.webView && (this.webView as any).postMessage) {
// Setup timeout for message response
if (methodType && messageId) {
const timeout = setTimeout(() => {
this.messageTimeouts.delete(messageId);

throw createResponseTimeoutError(methodType, messageId);
}, 10000); // 10-second timeout

this.messageTimeouts.set(messageId, timeout);
}

(this.webView as any).postMessage(
JSON.stringify(data, (key, value) => {
// parse Typed Array to Stringify object
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import browserEnv from '@ikscodes/browser-env';
import { createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider';
import { SDKErrorCode } from '@magic-sdk/types';
import { createModalNotReadyError } from '@magic-sdk/provider';
import { createReactNativeWebViewController } from '../../factories';
import { reactNativeStyleSheetStub } from '../../mocks';

Expand Down Expand Up @@ -51,88 +50,3 @@ test('Process Typed Array in a Solana Request', async () => {
'http:https://example.com',
]);
});

test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => {
const overlay = createReactNativeWebViewController('http:https://example.com');

const postStub = jest.fn();
overlay.webView = { postMessage: postStub };

// Assume `_post` method returns a promise that rejects upon timeout
const payload = { method: 'testMethod', id: 123 };

try {
await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload });
} catch (error) {
expect(error.code).toBe(SDKErrorCode.ResponseTimeout);
}
});

test('Adds a timeout to messageTimeouts map on message send', async () => {
const overlay = createReactNativeWebViewController('http:https://example.com');

overlay.webView = { postMessage: jest.fn() };
const payload = { method: 'testMethod', id: 123 };

try {
await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload });
} catch (error) {
expect(error.code).toBe(SDKErrorCode.ResponseTimeout);
}

expect(overlay.messageTimeouts.has(123)).toBe(true);
});

test('Removes timeout from messageTimeouts map on response', async () => {
const overlay = createReactNativeWebViewController('http:https://example.com');

// Mock the WebView and its postMessage method
overlay.webView = { postMessage: jest.fn() };

const payload = { method: 'testMethod', id: 123 };
try {
await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload });
} catch (error) {
expect(error.code).toBe(SDKErrorCode.ResponseTimeout);
}

try {
// Simulate receiving a response by directly invoking the message handler
overlay.handleReactNativeWebViewMessage({
nativeEvent: {
data: JSON.stringify({ response: { id: 123 } }),
},
});
} catch (e) {
console.log(e);
}

expect(overlay.messageTimeouts.has(123)).toBe(true);
});

test('Removes timeout from messageTimeouts map on timeout', async () => {
expect.assertions(2); // Make sure both assertions are checked
const overlay = createReactNativeWebViewController('http:https://example.com');

overlay.webView = { postMessage: jest.fn() };

// Mock setTimeout to immediately invoke its callback to simulate a timeout
jest.spyOn(global, 'setTimeout').mockImplementationOnce((cb) => {
cb();
return 123; // Return a mock timer ID
});

const payload = { method: 'testMethod', id: 123 };

try {
await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload });
} catch (error) {
// Expect that the error was thrown due to a timeout
expect(error.code).toBe(SDKErrorCode.ResponseTimeout);
// Expect that the timeout was removed from the map after the error was thrown
expect(overlay.messageTimeouts.has(123)).toBe(false);
}

// Restore original setTimeout function
jest.restoreAllMocks();
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useCallback, useMemo, useEffect } from 'react';
import { Linking, StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview';
import { SafeAreaView } from 'react-native-safe-area-context';
import { ViewController, createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider';
import { ViewController, createModalNotReadyError } from '@magic-sdk/provider';
import { MagicMessageEvent } from '@magic-sdk/types';
import { isTypedArray } from 'lodash';
import Global = NodeJS.Global;
Expand Down Expand Up @@ -62,7 +62,6 @@ export class ReactNativeWebViewController extends ViewController {
private webView!: WebView | null;
private container!: ViewWrapper | null;
private styles: any;
private messageTimeouts = new Map();

protected init() {
this.webView = null;
Expand Down Expand Up @@ -140,18 +139,6 @@ export class ReactNativeWebViewController extends ViewController {
}, [show]);

const handleWebViewMessage = useCallback((event: any) => {
const data = JSON.parse(event.nativeEvent.data);

if (data?.response?.id) {
const messageId = data.response.id;

// Clear timeout if message is received in time
if (this.messageTimeouts.has(messageId)) {
clearTimeout(this.messageTimeouts.get(messageId));
this.messageTimeouts.delete(messageId);
}
}
// Process the received message
this.handleReactNativeWebViewMessage(event);
}, []);

Expand Down Expand Up @@ -229,21 +216,7 @@ export class ReactNativeWebViewController extends ViewController {
}

protected async _post(data: any) {
// Safely access `method` and `id` from `payload`, defaulting to undefined if not present
const methodType = data.payload?.method;
const messageId = data.payload?.id;

if (this.webView && (this.webView as any).postMessage) {
// Setup timeout for message response
if (methodType && messageId) {
const timeout = setTimeout(() => {
this.messageTimeouts.delete(messageId);
throw createResponseTimeoutError(methodType, messageId);
}, 10000); // 10-second timeout

this.messageTimeouts.set(messageId, timeout);
}

(this.webView as any).postMessage(
JSON.stringify(data, (key, value) => {
// parse Typed Array to Stringify object
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import browserEnv from '@ikscodes/browser-env';
import { createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider';
import { SDKErrorCode } from '@magic-sdk/types';
import { createModalNotReadyError } from '@magic-sdk/provider';
import { createReactNativeWebViewController } from '../../factories';
import { reactNativeStyleSheetStub } from '../../mocks';

Expand Down Expand Up @@ -51,87 +50,3 @@ test('Process Typed Array in a Solana Request', async () => {
'http:https://example.com',
]);
});

test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => {
const overlay = createReactNativeWebViewController('http:https://example.com');
const postStub = jest.fn();
overlay.webView = { postMessage: postStub };

// Assume `_post` method returns a promise that rejects upon timeout
const payload = { method: 'testMethod', id: 123 };

try {
await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload });
} catch (error) {
expect(error.code).toBe(SDKErrorCode.ResponseTimeout);
}
});

test('Adds a timeout to messageTimeouts map on message send', async () => {
const overlay = createReactNativeWebViewController('http:https://example.com');

overlay.webView = { postMessage: jest.fn() };
const payload = { method: 'testMethod', id: 123 };

try {
await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload });
} catch (error) {
expect(error.code).toBe(SDKErrorCode.ResponseTimeout);
}

expect(overlay.messageTimeouts.has(123)).toBe(true);
});

test('Removes timeout from messageTimeouts map on response', async () => {
const overlay = createReactNativeWebViewController('http:https://example.com');

// Mock the WebView and its postMessage method
overlay.webView = { postMessage: jest.fn() };

const payload = { method: 'testMethod', id: 123 };
try {
await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload });
} catch (error) {
expect(error.code).toBe(SDKErrorCode.ResponseTimeout);
}

try {
// Simulate receiving a response by directly invoking the message handler
overlay.handleReactNativeWebViewMessage({
nativeEvent: {
data: JSON.stringify({ response: { id: 123 } }),
},
});
} catch (e) {
console.log(e);
}

expect(overlay.messageTimeouts.has(123)).toBe(true);
});

test('Removes timeout from messageTimeouts map on timeout', async () => {
expect.assertions(2); // Make sure both assertions are checked
const overlay = createReactNativeWebViewController('http:https://example.com');

overlay.webView = { postMessage: jest.fn() };

// Mock setTimeout to immediately invoke its callback to simulate a timeout
jest.spyOn(global, 'setTimeout').mockImplementationOnce((cb) => {
cb();
return 123; // Return a mock timer ID
});

const payload = { method: 'testMethod', id: 123 };

try {
await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload });
} catch (error) {
// Expect that the error was thrown due to a timeout
expect(error.code).toBe(SDKErrorCode.ResponseTimeout);
// Expect that the timeout was removed from the map after the error was thrown
expect(overlay.messageTimeouts.has(123)).toBe(false);
}

// Restore original setTimeout function
jest.restoreAllMocks();
});
1 change: 0 additions & 1 deletion packages/@magic-sdk/types/src/core/exception-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export enum SDKErrorCode {
InvalidArgument = 'INVALID_ARGUMENT',
ExtensionNotInitialized = 'EXTENSION_NOT_INITIALIZED',
IncompatibleExtensions = 'INCOMPATIBLE_EXTENSIONS',
ResponseTimeout = 'RESPONSE_TIMEOUT',
}

export enum SDKWarningCode {
Expand Down
Loading