diff --git a/src/ui/libs/schematic-sdk/decorator.ts b/src/ui/libs/schematic-sdk/decorator.ts new file mode 100644 index 0000000000..5f41fa7f35 --- /dev/null +++ b/src/ui/libs/schematic-sdk/decorator.ts @@ -0,0 +1,66 @@ +import type {SdkConfig} from '@gravity-ui/sdk'; +import {CancellablePromise} from '@gravity-ui/sdk'; + +const CANCEL_REQUEST_EVENT = 'sdk_cancel_request'; + +type ConcurrentId = string | undefined; + +class SdkEvent extends EventTarget { + cancelRequest(concurrentId: ConcurrentId) { + this.dispatchEvent(new CustomEvent(CANCEL_REQUEST_EVENT, {detail: {concurrentId}})); + } +} +const instance = new SdkEvent(); + +function subscribeCancelRequest(cb: (concurrentId: ConcurrentId) => void) { + function subscribe(event: Event) { + const customEvent = event as CustomEvent<{concurrentId: ConcurrentId}>; + cb(customEvent.detail.concurrentId); + } + instance.addEventListener(CANCEL_REQUEST_EVENT, subscribe); + function unsubscribe() { + instance.removeEventListener(CANCEL_REQUEST_EVENT, subscribe); + } + return unsubscribe; +} + +export function emitCancelRequest(concurrentId: ConcurrentId) { + instance.cancelRequest(concurrentId); +} + +export function initBeforeRequestDecorator( + beforeRequest: () => Promise, +): SdkConfig['decorator'] { + return (sdkActionFunc) => + (...sdkActionFuncArgs) => { + const requestOptions = sdkActionFuncArgs[1]; + const concurrentId = requestOptions?.concurrentId; + let cancelled = false; + let actionPromise: ReturnType; + let unsubscribe: ReturnType; + if (concurrentId) { + unsubscribe = subscribeCancelRequest((emittedConcurrentId) => { + if (emittedConcurrentId === concurrentId) { + cancelled = true; + } + }); + } + return new CancellablePromise( + beforeRequest().then(() => { + unsubscribe?.(); + actionPromise = sdkActionFunc(...sdkActionFuncArgs); + if (cancelled) { + actionPromise.cancel(); + } + return actionPromise; + }), + () => { + unsubscribe?.(); + actionPromise?.cancel(); + cancelled = true; + }, + ).finally(() => { + unsubscribe?.(); + }); + }; +} diff --git a/src/ui/libs/schematic-sdk/index.ts b/src/ui/libs/schematic-sdk/index.ts index 469e3677f2..36eae1616a 100644 --- a/src/ui/libs/schematic-sdk/index.ts +++ b/src/ui/libs/schematic-sdk/index.ts @@ -19,6 +19,7 @@ import {DL} from '../../constants'; import {registry} from '../../registry'; import Utils from '../../utils'; +import {emitCancelRequest} from './decorator'; import type {OperationError, SdkError} from './parse-error'; import {handleRequestError, isOperationError, isSdkError} from './parse-error'; @@ -60,6 +61,7 @@ const sdkConfig: SdkConfig = { }, }; }, + // decorator: initBeforeRequestDecorator(...), }; export const initSdk = () => { @@ -97,8 +99,15 @@ export const initSdk = () => { return sdk; }; +// TODO: return object with sdk and cancelRequest export const getSdk = () => { return registry.libs.schematicSdk.get() as DatalensSdk<{ root: typeof schema; }>; }; + +// Use it instead of sdk.cancelRequest +export function cancelRequest(concurrentId: string) { + emitCancelRequest(concurrentId); + getSdk().cancelRequest(concurrentId); +}