UNPKG

@itwin/presentation-common

Version:

Common pieces for iModel.js presentation packages

169 lines • 7.33 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /* eslint-disable @typescript-eslint/no-deprecated */ /** @packageDocumentation * @module RPC */ import { Guid, Logger } from "@itwin/core-bentley"; import { RpcManager, RpcRequest } from "@itwin/core-common"; import { PresentationCommonLoggerCategory } from "./CommonLoggerCategory.js"; import { PresentationError, PresentationStatus } from "./Error.js"; import { PresentationRpcInterface } from "./PresentationRpcInterface.js"; import { createCancellableTimeoutPromise } from "./Utils.js"; /** * Default timeout for how long we're going to wait for RPC request to be fulfilled before throwing * a timeout error. */ const DEFAULT_REQUEST_TIMEOUT = 10 * 60 * 1000; // 10 minutes /** * RPC requests handler that wraps [[PresentationRpcInterface]] and * adds handling for cases when backend needs to be synced with client * state. * * @internal */ export class RpcRequestsHandler { /** Timeout for how long the handler going to wait for RPC request to be fulfilled before throwing a timeout error. */ timeout; /** ID that identifies this handler as a client */ clientId; constructor(props) { this.clientId = props?.clientId ?? Guid.createValue(); this.timeout = props?.timeout ?? DEFAULT_REQUEST_TIMEOUT; } // eslint-disable-next-line @typescript-eslint/naming-convention get rpcClient() { return RpcManager.getClientForInterface(PresentationRpcInterface); } async requestWithTimeout(func, diagnosticsHandler) { const rpcResponsePromise = func(); const rpcRequest = RpcRequest.current(this.rpcClient); const timeout = createCancellableTimeoutPromise(this.timeout); return Promise.race([ (async () => { let diagnostics; try { const response = await rpcResponsePromise; diagnostics = response.diagnostics; switch (response.statusCode) { case PresentationStatus.Success: return response.result; default: throw new PresentationError(response.statusCode, response.errorMessage); } } finally { diagnosticsHandler && diagnostics && diagnosticsHandler(diagnostics); } })(), timeout.promise.then(() => { throw new Error(`Processing the request took longer than the configured limit of ${this.timeout} ms`); }), ]).finally(() => { rpcRequest?.cancel(); timeout.cancel(); }); } /** * Send the request to backend. We'll wait for the response for `this.timeout` ms, and if we don't get the response by * then, we'll throw an error. */ async request(func, options, ...additionalOptions) { const { imodel, diagnostics, ...optionsNoIModel } = options; const { handler: diagnosticsHandler, ...diagnosticsOptions } = diagnostics ?? {}; if (isOptionsWithRuleset(optionsNoIModel)) { optionsNoIModel.rulesetOrId = cleanupRuleset(optionsNoIModel.rulesetOrId); } const rpcOptions = { ...optionsNoIModel, clientId: this.clientId, }; if (diagnostics) { rpcOptions.diagnostics = diagnosticsOptions; } const doRequest = async () => func(imodel, rpcOptions, ...additionalOptions); const result = await this.requestWithTimeout(doRequest, diagnosticsHandler); return result; } async getNodesCount(options) { return this.request(this.rpcClient.getNodesCount.bind(this.rpcClient), options); } async getPagedNodes(options) { return this.request(this.rpcClient.getPagedNodes.bind(this.rpcClient), options); } async getNodesDescriptor(options) { const response = await this.request(this.rpcClient.getNodesDescriptor.bind(this.rpcClient), options); if (typeof response === "string") { return JSON.parse(response); } return response; } async getNodePaths(options) { return this.request(this.rpcClient.getNodePaths.bind(this.rpcClient), options); } async getFilteredNodePaths(options) { return this.request(this.rpcClient.getFilteredNodePaths.bind(this.rpcClient), options); } async getContentSources(options) { return this.request(this.rpcClient.getContentSources.bind(this.rpcClient), options); } async getContentDescriptor(options) { return this.request(this.rpcClient.getContentDescriptor.bind(this.rpcClient), options); } async getContentSetSize(options) { return this.request(this.rpcClient.getContentSetSize.bind(this.rpcClient), options); } async getPagedContent(options) { return this.request(this.rpcClient.getPagedContent.bind(this.rpcClient), options); } async getPagedContentSet(options) { return this.request(this.rpcClient.getPagedContentSet.bind(this.rpcClient), options); } async getPagedDistinctValues(options) { return this.request(this.rpcClient.getPagedDistinctValues.bind(this.rpcClient), options); } async getContentInstanceKeys(options) { return this.request(this.rpcClient.getContentInstanceKeys.bind(this.rpcClient), options); } async getDisplayLabelDefinition(options) { return this.request(this.rpcClient.getDisplayLabelDefinition.bind(this.rpcClient), options); } async getPagedDisplayLabelDefinitions(options) { return this.request(this.rpcClient.getPagedDisplayLabelDefinitions.bind(this.rpcClient), options); } /* eslint-disable @typescript-eslint/no-deprecated */ async getSelectionScopes(options) { return this.request(this.rpcClient.getSelectionScopes.bind(this.rpcClient), options); } async computeSelection(options) { return this.request(this.rpcClient.computeSelection.bind(this.rpcClient), options); } } function isOptionsWithRuleset(options) { return typeof options.rulesetOrId === "object"; } const RULESET_SUPPORTED_PROPERTIES_OBJ = { id: true, rules: true, version: true, requiredSchemas: true, supplementationInfo: true, vars: true, }; function cleanupRuleset(ruleset) { const cleanedUpRuleset = { ...ruleset }; for (const propertyKey of Object.keys(cleanedUpRuleset)) { if (!RULESET_SUPPORTED_PROPERTIES_OBJ.hasOwnProperty(propertyKey)) { if (propertyKey === "$schema") { delete cleanedUpRuleset[propertyKey]; } else { Logger.logWarning(PresentationCommonLoggerCategory.Package, `Provided ruleset contains unrecognized attribute '${propertyKey}'. It either doesn't exist or may be no longer supported.`); } } } return cleanedUpRuleset; } //# sourceMappingURL=RpcRequestsHandler.js.map