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

feat: add fetch timeout [INS-3911] #7467

Merged
merged 4 commits into from
Jun 5, 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
3 changes: 3 additions & 0 deletions packages/insomnia/src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,6 @@ export const EXPORT_TYPE_ENVIRONMENT = 'environment';
export const EXPORT_TYPE_API_SPEC = 'api_spec';
export const EXPORT_TYPE_PROTO_FILE = 'proto_file';
export const EXPORT_TYPE_PROTO_DIRECTORY = 'proto_directory';

// (ms) curently server timeout is 30s
export const INSOMNIA_FETCH_TIME_OUT = 30_000;
45 changes: 17 additions & 28 deletions packages/insomnia/src/ui/insomniaFetch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

import { getApiBaseURL, getClientString, PLAYWRIGHT } from '../common/constants';
import { delay } from '../common/misc';
import { getApiBaseURL, getClientString, INSOMNIA_FETCH_TIME_OUT, PLAYWRIGHT } from '../common/constants';

interface FetchConfig {
method: 'POST' | 'PUT' | 'GET' | 'DELETE' | 'PATCH';
Expand All @@ -13,26 +12,6 @@ interface FetchConfig {
headers?: Record<string, string>;
}

const exponentialBackOff = async (url: string, init: RequestInit, retries = 0): Promise<Response> => {
try {
const response = await fetch(url, init);
if (response.status === 502 && retries < 5) {
retries++;
await delay(retries * 1000);
console.log(`Received 502 from ${url} retrying`);
return exponentialBackOff(url, init, retries);
}
if (!response.ok) {
// TODO: review error status code behaviour with backend, should we parse errors here and return response
// or should we rethrow an error with a response object inside? should we be exposing errors to the app UI?
console.log(`Response not OK: ${response.status} for ${url}`);
}
return response;
} catch (err) {
throw err;
}
};

// Adds headers, retries and opens deep links returned from the api
export async function insomniaFetch<T = void>({ method, path, data, sessionId, organizationId, origin, headers }: FetchConfig): Promise<T> {
const config: RequestInit = {
Expand All @@ -47,15 +26,25 @@ export async function insomniaFetch<T = void>({ method, path, data, sessionId, o
...(PLAYWRIGHT ? { 'X-Mockbin-Test': 'true' } : {}),
},
...(data ? { body: JSON.stringify(data) } : {}),
signal: AbortSignal.timeout(INSOMNIA_FETCH_TIME_OUT),
};
if (sessionId === undefined) {
throw new Error(`No session ID provided to ${method}:${path}`);
}
const response = await exponentialBackOff((origin || getApiBaseURL()) + path, config);
const uri = response.headers.get('x-insomnia-command');
if (uri) {
window.main.openDeepLink(uri);

try {
const response = await fetch((origin || getApiBaseURL()) + path, config);
const uri = response.headers.get('x-insomnia-command');
if (uri) {
window.main.openDeepLink(uri);
}
const isJson = response.headers.get('content-type')?.includes('application/json') || path.match(/\.json$/);
return isJson ? response.json() : response.text();
} catch (err) {
if (err.name === 'AbortError') {
throw new Error('insomniaFetch timed out');
} else {
throw err;
}
}
const isJson = response.headers.get('content-type')?.includes('application/json') || path.match(/\.json$/);
return isJson ? response.json() : response.text();
}
Loading