@itwin/core-backend
Version:
iTwin.js backend components
318 lines • 16.1 kB
JavaScript
"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