diff --git a/package-lock.json b/package-lock.json index 8b1d516..94e5469 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ulms/api-clients", - "version": "7.15.0", + "version": "7.16.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ulms/api-clients", - "version": "7.15.0", + "version": "7.16.0", "license": "MIT", "dependencies": { "axios": "1.6.2", diff --git a/package.json b/package.json index 3fd3f4d..d58792b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ulms/api-clients", - "version": "7.15.0", + "version": "7.16.0", "description": "JavaScript API clients for ULMS platform", "keywords": [], "homepage": "https://github.com/foxford/ulms-api-clients-js#readme", diff --git a/src/common.js b/src/common.js index 981ac33..9b3b45c 100644 --- a/src/common.js +++ b/src/common.js @@ -165,3 +165,29 @@ export const timeout = async (delay) => { return promise } + +export function mergeSignals(signals) { + if (AbortSignal && AbortSignal.any) { + return AbortSignal.any(signals) + } + + const controller = new AbortController() + + for (const signal of signals) { + if (signal.aborted) { + controller.abort(signal.reason) + + return controller.signal + } + + signal.addEventListener( + 'abort', + () => { + controller.abort(signal.reason) + }, + { once: true }, + ) + } + + return controller.signal +} diff --git a/src/http-client.js b/src/http-client.js index b05c25a..a417ef9 100644 --- a/src/http-client.js +++ b/src/http-client.js @@ -1,7 +1,12 @@ /* eslint-disable class-methods-use-this */ +import { mergeSignals } from './common' import { NetworkError } from './error' function createTimeoutSignal(timeout) { + if (AbortSignal && AbortSignal.timeout) { + return { signal: AbortSignal.timeout(timeout) } + } + const controller = new AbortController() const { signal } = controller const id = setTimeout(() => controller.abort(), timeout) @@ -12,22 +17,32 @@ function createTimeoutSignal(timeout) { class FetchHttpClient { async request(url, config) { - const { timeout, ...requestConfig } = config + const { signal: appSignal, timeout, ...requestConfig } = config const requestOptions = { ...requestConfig, } + const signals = [] let onFinally let response + if (appSignal) { + signals.push(appSignal) + } + if (timeout !== undefined) { - const { cleanup, signal } = createTimeoutSignal(timeout) + const { cleanup, signal: timeoutSignal } = createTimeoutSignal(timeout) + + signals.push(timeoutSignal) - requestOptions.signal = signal onFinally = cleanup } + if (signals.length > 0) { + requestOptions.signal = mergeSignals(signals) + } + try { - response = fetch(url, requestOptions) + response = await fetch(url, requestOptions) } catch (error) { throw new NetworkError('network_error', { cause: error }) } finally { diff --git a/src/ulms.js b/src/ulms.js index e53aeea..4018335 100644 --- a/src/ulms.js +++ b/src/ulms.js @@ -93,6 +93,12 @@ async function handleResponse(response) { return data } +const DEFAULT_REQUEST_TIMEOUT_MS = 10_000 +const requestOptionsWithRetryAndTimeout = { + retry: true, + timeout: DEFAULT_REQUEST_TIMEOUT_MS, +} + class ULMS extends BasicClient { agentLabel @@ -282,6 +288,7 @@ class ULMS extends BasicClient { * @param {Object|Object[]} jsep * @param {String} intent * @param {String} label + * @param {Object} requestOptions * @returns {Promise} */ createSignal( @@ -289,6 +296,7 @@ class ULMS extends BasicClient { jsep, intent = ULMS.intents.INTENT_READ, // eslint-disable-line default-param-last label, + requestOptions = {}, ) { const payload = { agent_label: this.agentLabel, @@ -296,24 +304,33 @@ class ULMS extends BasicClient { jsep, label, } + const { signal } = requestOptions - return this.post(this.url(`/conference_rtcs/${rtcId}/signal`), payload) + return this.post(this.url(`/conference_rtcs/${rtcId}/signal`), payload, { + ...requestOptionsWithRetryAndTimeout, + signal, + }) } /** * Create trickle signal * @param {String} handleId * @param {Object|Object[]} candidates + * @param {Object} requestOptions * @returns {Promise} */ - createTrickleSignal(handleId, candidates) { + createTrickleSignal(handleId, candidates, requestOptions = {}) { const payload = { agent_label: this.agentLabel, candidates, handle_id: handleId, } + const { signal } = requestOptions - return this.post(this.url('/conference_streams/trickle'), payload) + return this.post(this.url('/conference_streams/trickle'), payload, { + ...requestOptionsWithRetryAndTimeout, + signal, + }) } /** @@ -498,7 +515,7 @@ class ULMS extends BasicClient { readScope(kind, audience, scope, options) { return this.get( this.url(`/audiences/${audience}/${kind}/${scope}`, options), - { timeout: 10_000, retry: true }, + requestOptionsWithRetryAndTimeout, ) } @@ -690,17 +707,11 @@ class ULMS extends BasicClient { * @returns {Promise} */ updatePosition(kind, classId, position) { - const controller = new AbortController() - const { signal } = controller - const timeoutId = setTimeout(() => controller.abort(), 10 * 1000) - return this.post( `${this.baseUrl}/${kind}/${classId}/timestamps`, { position }, - { signal }, - ).finally(() => { - clearTimeout(timeoutId) - }) + requestOptionsWithRetryAndTimeout, + ) } /**