UNPKG

@itwin/core-frontend

Version:
136 lines • 7.97 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module IModelConnection */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CheckpointConnection = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const FrontendLoggerCategory_1 = require("./common/FrontendLoggerCategory"); const IModelApp_1 = require("./IModelApp"); const IModelConnection_1 = require("./IModelConnection"); const IModelRoutingContext_1 = require("./IModelRoutingContext"); const IpcApp_1 = require("./IpcApp"); const loggerCategory = FrontendLoggerCategory_1.FrontendLoggerCategory.IModelConnection; /** * An IModelConnection to a Checkpoint of an iModel. * @see [CheckpointConnection]($docs/learning/frontend/IModelConnection) * @public */ class CheckpointConnection extends IModelConnection_1.IModelConnection { _fromIpc; /** The Guid that identifies the iTwin that owns this iModel. */ get iTwinId() { return super.iTwinId; } /** The Guid that identifies this iModel. */ get iModelId() { return super.iModelId; } /** Returns `true` if [[close]] has already been called. */ get isClosed() { return this._isClosed ? true : false; } _isClosed; constructor(props, fromIpc) { super(props); this._fromIpc = fromIpc; } /** Type guard for instanceof [[CheckpointConnection]] */ isCheckpointConnection() { return true; } /** * Open a readonly IModelConnection to a Checkpoint of an iModel. */ static async openRemote(iTwinId, iModelId, version = core_common_1.IModelVersion.latest()) { if (undefined === IModelApp_1.IModelApp.hubAccess) throw new Error("Missing an implementation of IModelApp.hubAccess"); const accessToken = await IModelApp_1.IModelApp.getAccessToken(); const changeset = await IModelApp_1.IModelApp.hubAccess.getChangesetFromVersion({ accessToken, iModelId, version }); let connection; const iModelProps = { iTwinId, iModelId, changeset }; if (IpcApp_1.IpcApp.isValid) { connection = new this(await IpcApp_1.IpcApp.appFunctionIpc.openCheckpoint(iModelProps), true); } else { const routingContext = IModelRoutingContext_1.IModelRoutingContext.current || IModelRoutingContext_1.IModelRoutingContext.default; connection = new this(await this.callOpen(iModelProps, routingContext), false); core_common_1.RpcManager.setIModel(connection); connection.routingContext = routingContext; core_common_1.RpcRequest.notFoundHandlers.addListener(connection._reopenConnectionHandler); } IModelConnection_1.IModelConnection.onOpen.raiseEvent(connection); return connection; } static async callOpen(iModelToken, routingContext) { // Try opening the iModel repeatedly accommodating any pending responses from the backend. // Waits for an increasing amount of time (but within a range) before checking on the pending request again. const connectionRetryIntervalRange = { min: 100, max: 5000 }; // in milliseconds let connectionRetryInterval = Math.min(connectionRetryIntervalRange.min, IModelConnection_1.IModelConnection.connectionTimeout); const openForReadOperation = core_common_1.RpcOperation.lookup(core_common_1.IModelReadRpcInterface, "getConnectionProps"); if (!openForReadOperation) throw new core_common_1.IModelError(core_bentley_1.BentleyStatus.ERROR, "IModelReadRpcInterface.getConnectionProps() is not available"); openForReadOperation.policy.retryInterval = () => connectionRetryInterval; core_bentley_1.Logger.logTrace(loggerCategory, `IModelConnection.open`, iModelToken); const startTime = Date.now(); const removeListener = core_common_1.RpcRequest.events.addListener((type, request) => { if (type !== core_common_1.RpcRequestEvent.PendingUpdateReceived) // eslint-disable-line @typescript-eslint/no-deprecated return; if (!(openForReadOperation && request.operation === openForReadOperation)) return; core_bentley_1.Logger.logTrace(loggerCategory, "Received pending open notification in IModelConnection.open", iModelToken); const connectionTimeElapsed = Date.now() - startTime; if (connectionTimeElapsed > IModelConnection_1.IModelConnection.connectionTimeout) { core_bentley_1.Logger.logError(loggerCategory, `Timed out opening connection in IModelConnection.open (took longer than ${IModelConnection_1.IModelConnection.connectionTimeout} milliseconds)`, iModelToken); throw new core_common_1.IModelError(core_bentley_1.BentleyStatus.ERROR, "Opening a connection was timed out"); // NEEDS_WORK: More specific error status } connectionRetryInterval = Math.min(connectionRetryIntervalRange.max, connectionRetryInterval * 2, IModelConnection_1.IModelConnection.connectionTimeout - connectionTimeElapsed); if (request.retryInterval !== connectionRetryInterval) { request.retryInterval = connectionRetryInterval; core_bentley_1.Logger.logTrace(loggerCategory, `Adjusted open connection retry interval to ${request.retryInterval} milliseconds in IModelConnection.open`, iModelToken); } }); const openPromise = core_common_1.IModelReadRpcInterface.getClientForRouting(routingContext.token).getConnectionProps(iModelToken); let openResponse; try { openResponse = await openPromise; } finally { core_bentley_1.Logger.logTrace(loggerCategory, "Completed open request in IModelConnection.open", iModelToken); removeListener(); } return openResponse; } _reopenConnectionHandler = async (request, response, resubmit, reject) => { if (!response.hasOwnProperty("isIModelNotFoundResponse")) reject(); const iModelRpcProps = request.parameters[0]; if (this._fileKey !== iModelRpcProps.key) reject(); // The handler is called for a different connection than this core_bentley_1.Logger.logTrace(loggerCategory, "Attempting to reopen connection", () => iModelRpcProps); try { const openResponse = await CheckpointConnection.callOpen(iModelRpcProps, this.routingContext); // The new/reopened connection may have a new rpcKey and/or changesetId, but the other IModelRpcTokenProps should be the same this._fileKey = openResponse.key; this.changeset = openResponse.changeset; } catch (error) { reject(core_bentley_1.BentleyError.getErrorMessage(error)); } finally { } core_bentley_1.Logger.logTrace(loggerCategory, "Resubmitting original request after reopening connection", iModelRpcProps); request.parameters[0] = this.getRpcProps(); // Modify the token of the original request before resubmitting it. resubmit(); }; /** Close this CheckpointConnection */ async close() { if (this.isClosed) return; this.beforeClose(); if (this._fromIpc) await IpcApp_1.IpcApp.appFunctionIpc.closeIModel(this._fileKey); else core_common_1.RpcRequest.notFoundHandlers.removeListener(this._reopenConnectionHandler); this._isClosed = true; } } exports.CheckpointConnection = CheckpointConnection; //# sourceMappingURL=CheckpointConnection.js.map