@itwin/core-frontend
Version:
iTwin.js frontend components
136 lines • 7.97 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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
;