UNPKG

@itwin/core-backend

Version:
318 lines • 16.1 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.IModelReadRpcImpl = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const BackendLoggerCategory_1 = require("../BackendLoggerCategory"); const Category_1 = require("../Category"); const ConcurrentQuery_1 = require("../ConcurrentQuery"); const CustomViewState3dCreator_1 = require("../CustomViewState3dCreator"); const GeometrySummary_1 = require("../GeometrySummary"); const PromiseMemoizer_1 = require("../PromiseMemoizer"); const tracing_1 = require("../rpc/tracing"); const ViewStateHydrator_1 = require("../ViewStateHydrator"); const RpcBriefcaseUtility_1 = require("./RpcBriefcaseUtility"); const Symbols_1 = require("../internal/Symbols"); class ViewStateRequestMemoizer extends PromiseMemoizer_1.PromiseMemoizer { _timeoutMs; static _instance; static async perform(props) { if (!this._instance) this._instance = new ViewStateRequestMemoizer(); return this._instance.perform(props); } constructor() { const memoize = async (props) => { const db = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(props.accessToken, props.tokenProps); const viewCreator = new CustomViewState3dCreator_1.CustomViewState3dCreator(db); return viewCreator.getCustomViewState3dData(props.options); }; const stringify = (props) => { const token = props.tokenProps; const modelIds = props.options.modelIds; return `${token.key}-${token.iTwinId}-${token.iModelId}-${token.changeset?.id}:${modelIds}`; }; super(memoize, stringify); this._timeoutMs = 20 * 1000; } async perform(props) { const memo = this.memoize(props); // Rejections must be caught so that the memoization entry is deleted. await core_bentley_1.BeDuration.race(this._timeoutMs, memo.promise).catch(() => undefined); if (memo.isPending) throw new core_common_1.RpcPendingResponse(); // eslint-disable-line @typescript-eslint/only-throw-error this.deleteMemoized(props); if (memo.isFulfilled) { (0, core_bentley_1.assert)(undefined !== memo.result); return memo.result; } (0, core_bentley_1.assert)(memo.isRejected); throw memo.error; } } function currentActivity() { return tracing_1.RpcTrace.expectCurrentActivity; } async function getIModelForRpc(tokenProps) { return RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(tracing_1.RpcTrace.expectCurrentActivity.accessToken, tokenProps); } /** The backend implementation of IModelReadRpcInterface. * @internal */ class IModelReadRpcImpl extends core_common_1.RpcInterface { static register() { core_common_1.RpcManager.registerImpl(core_common_1.IModelReadRpcInterface, IModelReadRpcImpl); } async getConnectionProps(tokenProps) { return RpcBriefcaseUtility_1.RpcBriefcaseUtility.openWithTimeout(currentActivity(), tokenProps, core_common_1.SyncMode.FixedVersion); } async getCustomViewState3dData(tokenProps, options) { const accessToken = currentActivity().accessToken; return ViewStateRequestMemoizer.perform({ accessToken, tokenProps, options }); } async hydrateViewState(tokenProps, options) { const iModelDb = await getIModelForRpc(tokenProps); const viewHydrater = new ViewStateHydrator_1.ViewStateHydrator(iModelDb); return viewHydrater.getHydrateResponseProps(options); } async queryAllUsedSpatialSubCategories(tokenProps) { const iModelDb = await getIModelForRpc(tokenProps); return iModelDb.queryAllUsedSpatialSubCategories(); } async querySubCategories(tokenProps, compressedCategoryIds) { const iModelDb = await getIModelForRpc(tokenProps); const decompressedIds = core_bentley_1.CompressedId64Set.decompressArray(compressedCategoryIds); return iModelDb.querySubCategories(decompressedIds); } async queryRows(tokenProps, request) { const iModelDb = await getIModelForRpc(tokenProps); if (iModelDb.isReadonly && request.usePrimaryConn === true) { core_bentley_1.Logger.logWarning(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, "usePrimaryConn is only supported on imodel that is opened in read/write mode. The option will be ignored.", request); request.usePrimaryConn = false; } return ConcurrentQuery_1.ConcurrentQuery.executeQueryRequest(iModelDb[Symbols_1._nativeDb], request); } async queryBlob(tokenProps, request) { const iModelDb = await getIModelForRpc(tokenProps); if (iModelDb.isReadonly && request.usePrimaryConn === true) { core_bentley_1.Logger.logWarning(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, "usePrimaryConn is only supported on imodel that is opened in read/write mode. The option will be ignored.", request); request.usePrimaryConn = false; } return ConcurrentQuery_1.ConcurrentQuery.executeBlobRequest(iModelDb[Symbols_1._nativeDb], request); } async queryModelRanges(tokenProps, modelIds) { const results = await this.queryModelExtents(tokenProps, modelIds); if (results.length === 1 && results[0].status !== core_bentley_1.IModelStatus.Success) throw new core_common_1.IModelError(results[0].status, "error querying model range"); return results.filter((x) => x.status === core_bentley_1.IModelStatus.Success).map((x) => x.extents); } async queryModelExtents(tokenProps, modelIds) { const iModel = await getIModelForRpc(tokenProps); return iModel.models.queryExtents(modelIds); } async getModelProps(tokenProps, modelIdsList) { const modelIds = new Set(modelIdsList); const iModelDb = await getIModelForRpc(tokenProps); const modelJsonArray = []; for (const id of modelIds) { try { const modelProps = iModelDb.models.getModelProps(id); modelJsonArray.push(modelProps); } catch (error) { if (modelIds.size === 1) throw error; // if they're asking for more than one model, don't throw on error. } } return modelJsonArray; } async queryModelProps(tokenProps, params) { const ids = await this.queryEntityIds(tokenProps, params); return this.getModelProps(tokenProps, [...ids]); } async getElementProps(tokenProps, elementIdsList) { const elementIds = new Set(elementIdsList); const iModelDb = await getIModelForRpc(tokenProps); const elementProps = []; for (const id of elementIds) { try { elementProps.push(iModelDb.elements.getElementProps({ id })); } catch (error) { if (elementIds.size === 1) throw error; // if they're asking for more than one element, don't throw on error. } } return elementProps; } async loadElementProps(tokenProps, identifier, options) { const props = options ? { ...options } : {}; if (typeof identifier === "string") { if (core_bentley_1.Id64.isId64(identifier)) props.id = identifier; else props.federationGuid = identifier; } else { props.code = core_common_1.Code.fromJSON(identifier); } const iModelDb = await getIModelForRpc(tokenProps); return iModelDb.elements.tryGetElementProps(props); } async getGeometrySummary(tokenProps, request) { const iModel = await getIModelForRpc(tokenProps); return (0, GeometrySummary_1.generateGeometrySummaries)(request, iModel); } async queryElementProps(tokenProps, params) { const ids = await this.queryEntityIds(tokenProps, params); const res = this.getElementProps(tokenProps, [...ids]); return res; } async queryEntityIds(tokenProps, params) { const iModelDb = await getIModelForRpc(tokenProps); const res = iModelDb.queryEntityIds(params); return [...res]; } async getClassHierarchy(tokenProps, classFullName) { const iModelDb = await getIModelForRpc(tokenProps); const classArray = []; while (true) { // eslint-disable-next-line @typescript-eslint/no-deprecated const classMetaData = iModelDb.getMetaData(classFullName); classArray.push(classFullName); if (!classMetaData.baseClasses || classMetaData.baseClasses.length === 0) break; classFullName = classMetaData.baseClasses[0]; } return classArray; } async getAllCodeSpecs(tokenProps) { const codeSpecs = []; const iModelDb = await getIModelForRpc(tokenProps); // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement("SELECT ECInstanceId AS id, name, jsonProperties FROM BisCore.CodeSpec", (statement) => { for (const row of statement) codeSpecs.push({ id: row.id, name: row.name, jsonProperties: JSON.parse(row.jsonProperties) }); }); return codeSpecs; } async getViewStateData(tokenProps, viewDefinitionId, options) { const iModelDb = await getIModelForRpc(tokenProps); return iModelDb.views.getViewStateProps(viewDefinitionId, options); } async readFontJson(tokenProps) { const iModelDb = await getIModelForRpc(tokenProps); return iModelDb[Symbols_1._nativeDb].readFontMap(); } async requestSnap(tokenProps, sessionId, props) { const iModelDb = await getIModelForRpc(tokenProps); return iModelDb.requestSnap(sessionId, props); } async cancelSnap(tokenProps, sessionId) { const iModelDb = await getIModelForRpc(tokenProps); return iModelDb.cancelSnap(sessionId); } async getGeometryContainment(tokenProps, props) { const iModelDb = await getIModelForRpc(tokenProps); return iModelDb.getGeometryContainment(props); } async getMassProperties(tokenProps, props) { const iModelDb = await getIModelForRpc(tokenProps); return iModelDb.getMassProperties(props); } async getMassPropertiesPerCandidate(tokenProps, props) { const iModelDb = await getIModelForRpc(tokenProps); const getSingleCandidateMassProperties = async (candidate) => { try { const massPropResults = []; for (const op of props.operations) { const massProperties = await iModelDb.getMassProperties({ operation: op, candidates: [candidate] }); massPropResults.push(massProperties); } let singleCandidateResult = { status: core_bentley_1.BentleyStatus.ERROR, candidate }; // eslint-disable-line @typescript-eslint/no-deprecated if (massPropResults.some((r) => r.status !== core_bentley_1.BentleyStatus.ERROR)) { singleCandidateResult.status = core_bentley_1.BentleyStatus.SUCCESS; for (const r of massPropResults.filter((mpr) => mpr.status !== core_bentley_1.BentleyStatus.ERROR)) { singleCandidateResult = { ...singleCandidateResult, ...r }; } } return singleCandidateResult; } catch { return { status: core_bentley_1.BentleyStatus.ERROR, candidate }; } }; const promises = []; // eslint-disable-line @typescript-eslint/no-deprecated for (const candidate of core_bentley_1.CompressedId64Set.iterable(props.candidates)) { promises.push(getSingleCandidateMassProperties(candidate)); } return Promise.all(promises); } async getToolTipMessage(tokenProps, id) { const iModelDb = await getIModelForRpc(tokenProps); const el = iModelDb.elements.getElement(id); return (el === undefined) ? [] : el.getToolTipMessage(); } /** Send a view thumbnail to the frontend. This is a binary transfer with the metadata in a 16-byte prefix header. * @deprecated in 3.6.0 - might be removed in next major version. Use queryViewThumbnail instead */ async getViewThumbnail(tokenProps, viewId) { const iModelDb = await getIModelForRpc(tokenProps); const thumbnail = iModelDb.views.getThumbnail(viewId); if (undefined === thumbnail || 0 === thumbnail.image.length) throw new core_common_1.NoContentError(); const val = new Uint8Array(thumbnail.image.length + 16); // allocate a new buffer 16 bytes larger than the image size new Uint32Array(val.buffer, 0, 4).set([thumbnail.image.length, thumbnail.format === "jpeg" ? core_common_1.ImageSourceFormat.Jpeg : core_common_1.ImageSourceFormat.Png, thumbnail.width, thumbnail.height]); // Put the metadata in the first 16 bytes. val.set(thumbnail.image, 16); // put the image data at offset 16 after metadata return val; } async getDefaultViewId(tokenProps) { const iModelDb = await getIModelForRpc(tokenProps); const spec = { namespace: "dgn_View", name: "DefaultView" }; const blob = iModelDb.queryFilePropertyBlob(spec); if (undefined === blob || 8 !== blob.length) return core_bentley_1.Id64.invalid; const view = new Uint32Array(blob.buffer); return core_bentley_1.Id64.fromUint32Pair(view[0], view[1]); } async getSpatialCategoryId(tokenProps, categoryName) { const iModelDb = await getIModelForRpc(tokenProps); const dictionary = iModelDb.models.getModel(core_common_1.IModel.dictionaryId); return Category_1.SpatialCategory.queryCategoryIdByName(iModelDb, dictionary.id, categoryName); } async getIModelCoordinatesFromGeoCoordinates(tokenProps, props) { const iModelDb = await getIModelForRpc(tokenProps); return iModelDb.getIModelCoordinatesFromGeoCoordinates(props); } async getGeoCoordinatesFromIModelCoordinates(tokenProps, props) { const iModelDb = await getIModelForRpc(tokenProps); return iModelDb.getGeoCoordinatesFromIModelCoordinates(props); } async queryTextureData(tokenProps, textureLoadProps) { const db = await getIModelForRpc(tokenProps); return db.queryTextureData(textureLoadProps); } async generateElementMeshes(tokenProps, props) { const db = await getIModelForRpc(tokenProps); return db[Symbols_1._nativeDb].generateElementMeshes(props); } /** @internal */ async callViewStore(tokenProps, version, forWrite, methodName, ...args) { if (!core_common_1.RpcInterface.isVersionCompatible(core_common_1.ViewStoreRpc.version, version)) throw new Error("ViewStoreRpc version mismatch"); const db = await getIModelForRpc(tokenProps); const viewStore = await db.views.accessViewStore({ accessLevel: forWrite ? "write" : "read" }); const access = viewStore[forWrite ? "writeLocker" : "reader"]; const func = access[methodName]; if (typeof func !== "function") throw new core_common_1.IModelError(core_bentley_1.IModelStatus.FunctionNotFound, `Illegal ViewStore RPC call "${methodName}"`); return func.call(access, ...args); } } exports.IModelReadRpcImpl = IModelReadRpcImpl; //# sourceMappingURL=IModelReadRpcImpl.js.map