UNPKG

@itwin/core-backend

Version:
159 lines • 8.87 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 RpcInterface */ Object.defineProperty(exports, "__esModule", { value: true }); exports.RpcBriefcaseUtility = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const BackendLoggerCategory_1 = require("../BackendLoggerCategory"); const BriefcaseManager_1 = require("../BriefcaseManager"); const CheckpointManager_1 = require("../CheckpointManager"); const IModelDb_1 = require("../IModelDb"); const IModelHost_1 = require("../IModelHost"); const IModelJsFs_1 = require("../IModelJsFs"); const Symbols_1 = require("../internal/Symbols"); const loggerCategory = BackendLoggerCategory_1.BackendLoggerCategory.IModelDb; /** * Utility to open the iModel for RPC interfaces * @internal */ class RpcBriefcaseUtility { static async downloadAndOpen(args) { const { activity, tokenProps } = args; const accessToken = activity.accessToken; (0, core_bentley_1.assert)(undefined !== tokenProps.iModelId); const iModelId = tokenProps.iModelId; let myBriefcaseIds; if (args.syncMode === core_common_1.SyncMode.PullOnly) { myBriefcaseIds = [0]; // PullOnly means briefcaseId 0 } else { // check with iModelHub and see if we already have acquired any briefcaseIds myBriefcaseIds = await IModelHost_1.IModelHost[Symbols_1._hubAccess].getMyBriefcaseIds({ accessToken, iModelId }); } const resolvers = args.fileNameResolvers ?? [(arg) => BriefcaseManager_1.BriefcaseManager.getFileName(arg)]; // see if we can open any of the briefcaseIds we already acquired from iModelHub if (resolvers) { for (const resolver of resolvers) { for (const briefcaseId of myBriefcaseIds) { const fileName = resolver({ briefcaseId, iModelId }); if (IModelJsFs_1.IModelJsFs.existsSync(fileName)) { const briefcaseDb = IModelDb_1.BriefcaseDb.findByFilename(fileName); if (briefcaseDb !== undefined) { if (briefcaseDb.isBriefcaseDb()) { return briefcaseDb; } else { throw new core_common_1.IModelError(core_bentley_1.IModelStatus.AlreadyOpen, "iModel is already open as a SnapshotDb"); } } try { if (args.forceDownload) throw new Error(); // causes delete below const db = await IModelDb_1.BriefcaseDb.open({ fileName }); if (db.changeset.id !== tokenProps.changeset?.id) { (0, core_bentley_1.assert)(undefined !== tokenProps.changeset); const toIndex = tokenProps.changeset?.index ?? (await IModelHost_1.IModelHost[Symbols_1._hubAccess].getChangesetFromVersion({ accessToken, iModelId, version: core_common_1.IModelVersion.asOfChangeSet(tokenProps.changeset.id) })).index; await BriefcaseManager_1.BriefcaseManager.pullAndApplyChangesets(db, { accessToken, toIndex }); } return db; } catch (error) { if (!(error.errorNumber === core_bentley_1.IModelStatus.AlreadyOpen)) // somehow we have this briefcaseId and the file exists, but we can't open it. Delete it. await BriefcaseManager_1.BriefcaseManager.deleteBriefcaseFiles(fileName, accessToken); } } } } } // no local briefcase available. Download one and open it. (0, core_bentley_1.assert)(undefined !== tokenProps.iTwinId); const request = { accessToken, iTwinId: tokenProps.iTwinId, iModelId, briefcaseId: args.syncMode === core_common_1.SyncMode.PullOnly ? 0 : undefined, // if briefcaseId is undefined, we'll acquire a new one. }; const props = await BriefcaseManager_1.BriefcaseManager.downloadBriefcase(request); return IModelDb_1.BriefcaseDb.open(props); } static _briefcasePromises = new Map(); static async openBriefcase(args) { const key = `${args.tokenProps.iModelId}:${args.tokenProps.changeset?.id}:${args.tokenProps.changeset?.index}:${args.syncMode}`; const cachedPromise = this._briefcasePromises.get(key); if (cachedPromise) return cachedPromise; try { const briefcasePromise = this.downloadAndOpen(args); // save the fact that we're working on downloading so if we timeout, we'll reuse this request. this._briefcasePromises.set(key, briefcasePromise); return await briefcasePromise; } finally { this._briefcasePromises.delete(key); // the download and open is now done } } /** find a previously opened iModel for RPC. * @param accessToken necessary (only) for V2 checkpoints to refresh access token in daemon if it has expired. We use the accessToken of the current RPC request * to refresh the daemon, even though it will be used for all authorized users. * @param the IModelRpcProps to locate the opened iModel. */ static async findOpenIModel(accessToken, iModel) { const iModelDb = IModelDb_1.IModelDb.findByKey(iModel.key); // call refreshContainer, just in case this is a V2 checkpoint whose sasToken is about to expire, or its default transaction is about to be restarted. await iModelDb.refreshContainerForRpc(accessToken); return iModelDb; } /** * Download and open a checkpoint or briefcase, ensuring the operation completes within a default timeout. If the time to open exceeds the timeout period, * a RpcPendingResponse exception is thrown */ static async open(args) { const { activity, tokenProps, syncMode } = args; core_bentley_1.Logger.logTrace(loggerCategory, "RpcBriefcaseUtility.open", tokenProps); const timeout = args.timeout ?? 1000; if (syncMode === core_common_1.SyncMode.PullOnly || syncMode === core_common_1.SyncMode.PullAndPush) { const briefcaseDb = await core_bentley_1.BeDuration.race(timeout, this.openBriefcase(args)); if (briefcaseDb === undefined) { core_bentley_1.Logger.logTrace(loggerCategory, "Open briefcase - pending", tokenProps); throw new core_common_1.RpcPendingResponse(); // eslint-disable-line @typescript-eslint/only-throw-error } // note: usage is logged in the function BriefcaseManager.downloadNewBriefcaseAndOpen return briefcaseDb; } if (!tokenProps.iModelId || !tokenProps.iTwinId || !tokenProps.changeset) throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadArg, "invalid arguments"); const checkpoint = { iModelId: tokenProps.iModelId, iTwinId: tokenProps.iTwinId, changeset: tokenProps.changeset, accessToken: activity.accessToken, }; // opening a checkpoint. let db; // first check if it's already open db = IModelDb_1.SnapshotDb.tryFindByKey(CheckpointManager_1.CheckpointManager.getKey(checkpoint)); if (db) { core_bentley_1.Logger.logTrace(loggerCategory, "Checkpoint was already open", tokenProps); return db; } // now try V2 checkpoint db = await IModelDb_1.SnapshotDb.openCheckpointFromRpc(checkpoint); core_bentley_1.Logger.logTrace(loggerCategory, "using V2 checkpoint", tokenProps); return db; } static async openWithTimeout(activity, tokenProps, syncMode, timeout = 1000) { if (tokenProps.iModelId) await IModelHost_1.IModelHost.tileStorage?.initialize(tokenProps.iModelId); // eslint-disable-next-line @typescript-eslint/no-deprecated return (await this.open({ activity, tokenProps, syncMode, timeout })).toJSON(); } } exports.RpcBriefcaseUtility = RpcBriefcaseUtility; //# sourceMappingURL=RpcBriefcaseUtility.js.map