UNPKG

@itwin/presentation-backend

Version:

Backend of iTwin.js Presentation library

487 lines • 22.7 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. *--------------------------------------------------------------------------------------------*/ /* eslint-disable @typescript-eslint/ban-ts-comment */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PresentationManagerDetail = exports.DESCRIPTOR_ONLY_CONTENT_FLAG = void 0; exports.getRulesetIdObject = getRulesetIdObject; exports.getKeysForContentRequest = getKeysForContentRequest; exports.bisElementInstanceKeysProcessor = bisElementInstanceKeysProcessor; exports.ipcUpdatesHandler = ipcUpdatesHandler; exports.noopUpdatesHandler = noopUpdatesHandler; const hash = __importStar(require("object-hash")); const path = __importStar(require("path")); const core_backend_1 = require("@itwin/core-backend"); const core_bentley_1 = require("@itwin/core-bentley"); const presentation_common_1 = require("@itwin/presentation-common"); const internal_1 = require("@itwin/presentation-common/internal"); const presentation_shared_1 = require("@itwin/presentation-shared"); const BackendLoggerCategory_js_1 = require("./BackendLoggerCategory.js"); const NativePlatform_js_1 = require("./NativePlatform.js"); const PresentationManager_js_1 = require("./PresentationManager.js"); // @ts-ignore TS complains about `with` in CJS builds, but not ESM const ElementProperties_PresentationRuleSet_json_1 = __importDefault(require("./primary-presentation-rules/ElementProperties.PresentationRuleSet.json")); const RulesetManager_js_1 = require("./RulesetManager.js"); // @ts-ignore TS complains about `with` in CJS builds, but not ESM const BisCore_PresentationRuleSet_json_1 = __importDefault(require("./supplemental-presentation-rules/BisCore.PresentationRuleSet.json")); // @ts-ignore TS complains about `with` in CJS builds, but not ESM const Functional_PresentationRuleSet_json_1 = __importDefault(require("./supplemental-presentation-rules/Functional.PresentationRuleSet.json")); const Utils_js_1 = require("./Utils.js"); /** * Produce content descriptor that is not intended for querying content. Allows the implementation to omit certain * operations to make obtaining content descriptor faster. * @internal */ exports.DESCRIPTOR_ONLY_CONTENT_FLAG = 1 << 9; /** @internal */ class PresentationManagerDetail { _disposed; _nativePlatform; _diagnosticsOptions; rulesets; activeUnitSystem; onUsed; constructor(params) { this._disposed = false; this._nativePlatform = params.addon ?? createNativePlatform(params.id ?? "", params.workerThreadsCount ?? 2, core_backend_1.IpcHost.isValid ? ipcUpdatesHandler : noopUpdatesHandler, params.caching, // eslint-disable-next-line @typescript-eslint/no-deprecated params.defaultFormats, params.useMmap); setupRulesets(this._nativePlatform, params.supplementalRulesetDirectories ?? [], params.rulesetDirectories ?? []); this.activeUnitSystem = params.defaultUnitSystem; this.onUsed = new core_bentley_1.BeEvent(); this.rulesets = new RulesetManager_js_1.RulesetManagerImpl(() => this.getNativePlatform()); this._diagnosticsOptions = params.diagnostics; } [Symbol.dispose]() { if (this._disposed) { return; } this.getNativePlatform()[Symbol.dispose](); this._nativePlatform = undefined; this.onUsed.clear(); this._disposed = true; } getNativePlatform() { if (this._disposed) { throw new Error("Attempting to use Presentation manager after disposal"); } return this._nativePlatform; } async getNodes(requestOptions) { const { rulesetOrId, parentKey, ...strippedOptions } = requestOptions; const params = { requestId: parentKey ? NativePlatform_js_1.NativePlatformRequestTypes.GetChildren : NativePlatform_js_1.NativePlatformRequestTypes.GetRootNodes, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptions, nodeKey: parentKey, }; return this.request(params); } async getNodesCount(requestOptions) { const { rulesetOrId, parentKey, ...strippedOptions } = requestOptions; const params = { requestId: parentKey ? NativePlatform_js_1.NativePlatformRequestTypes.GetChildrenCount : NativePlatform_js_1.NativePlatformRequestTypes.GetRootNodesCount, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptions, nodeKey: parentKey, }; return JSON.parse(await this.request(params)); } async getNodesDescriptor(requestOptions) { const { rulesetOrId, parentKey, ...strippedOptions } = requestOptions; const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetNodesDescriptor, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptions, nodeKey: parentKey, }; return this.request(params); } async getNodePaths(requestOptions) { const { rulesetOrId, instancePaths, ...strippedOptions } = requestOptions; const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetNodePaths, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptions, paths: instancePaths, }; const paths = (0, internal_1.deepReplaceNullsToUndefined)(JSON.parse(await this.request(params))); return paths; } async getFilteredNodePaths(requestOptions) { const { rulesetOrId, ...strippedOptions } = requestOptions; const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetFilteredNodePaths, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptions, }; const paths = (0, internal_1.deepReplaceNullsToUndefined)(JSON.parse(await this.request(params))); return paths; } async getContentDescriptor(requestOptions) { const { rulesetOrId, contentFlags, ...strippedOptions } = requestOptions; const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetContentDescriptor, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptions, contentFlags: contentFlags ?? exports.DESCRIPTOR_ONLY_CONTENT_FLAG, // only set "descriptor only" flag if there are no flags provided keys: getKeysForContentRequest(requestOptions.keys, (map) => bisElementInstanceKeysProcessor(requestOptions.imodel, map)), }; return this.request(params); } async getContentSources(requestOptions) { const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetContentSources, rulesetId: "ElementProperties", ...requestOptions, }; const json = JSON.parse(await this.request(params)); return (0, internal_1.deepReplaceNullsToUndefined)(json.sources.map((sourceJson) => presentation_common_1.SelectClassInfo.fromCompressedJSON(sourceJson, json.classesMap))); } async getContentSetSize(requestOptions) { const { rulesetOrId, descriptor, ...strippedOptions } = requestOptions; const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetContentSetSize, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptions, keys: getKeysForContentRequest(requestOptions.keys, (map) => bisElementInstanceKeysProcessor(requestOptions.imodel, map)), descriptorOverrides: createContentDescriptorOverrides(descriptor), }; return JSON.parse(await this.request(params)); } async getContentSet(requestOptions) { const { rulesetOrId, descriptor, ...strippedOptions } = requestOptions; const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetContentSet, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptions, keys: getKeysForContentRequest(requestOptions.keys, (map) => bisElementInstanceKeysProcessor(requestOptions.imodel, map)), descriptorOverrides: createContentDescriptorOverrides(descriptor), }; return JSON.parse(await this.request(params)) .map((json) => presentation_common_1.Item.fromJSON((0, internal_1.deepReplaceNullsToUndefined)(json))) .filter((item) => !!item); } async getContent(requestOptions) { const { rulesetOrId, descriptor, ...strippedOptions } = requestOptions; const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetContent, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptions, keys: getKeysForContentRequest(requestOptions.keys, (map) => bisElementInstanceKeysProcessor(requestOptions.imodel, map)), descriptorOverrides: createContentDescriptorOverrides(descriptor), }; return presentation_common_1.Content.fromJSON((0, internal_1.deepReplaceNullsToUndefined)(JSON.parse(await this.request(params)))); } async getPagedDistinctValues(requestOptions) { const { rulesetOrId, ...strippedOptions } = requestOptions; const { descriptor, keys, ...strippedOptionsNoDescriptorAndKeys } = strippedOptions; const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetPagedDistinctValues, rulesetId: this.registerRuleset(rulesetOrId), ...strippedOptionsNoDescriptorAndKeys, keys: getKeysForContentRequest(keys, (map) => bisElementInstanceKeysProcessor(requestOptions.imodel, map)), descriptorOverrides: createContentDescriptorOverrides(descriptor), }; return (0, internal_1.deepReplaceNullsToUndefined)(JSON.parse(await this.request(params))); } async getDisplayLabelDefinition(requestOptions) { const params = { requestId: NativePlatform_js_1.NativePlatformRequestTypes.GetDisplayLabel, ...requestOptions, }; return (0, internal_1.deepReplaceNullsToUndefined)(JSON.parse(await this.request(params))); } async getDisplayLabelDefinitions(requestOptions) { const concreteKeys = requestOptions.keys .map((k) => { if ((0, presentation_shared_1.normalizeFullClassName)(k.className).toLowerCase() === "BisCore.Element".toLowerCase()) { return (0, Utils_js_1.getElementKey)(requestOptions.imodel, k.id); } return k; }) .filter((k) => !!k); const contentRequestOptions = { ...requestOptions, rulesetOrId: "RulesDrivenECPresentationManager_RulesetId_DisplayLabel", descriptor: { displayType: presentation_common_1.DefaultContentDisplayTypes.List, contentFlags: presentation_common_1.ContentFlags.ShowLabels | presentation_common_1.ContentFlags.NoFields, }, keys: new presentation_common_1.KeySet(concreteKeys), }; const content = await this.getContent(contentRequestOptions); return concreteKeys.map((key) => { const item = content ? content.contentSet.find((it) => it.primaryKeys.length > 0 && presentation_common_1.InstanceKey.compare(it.primaryKeys[0], key) === 0) : undefined; if (!item) { return { displayValue: "", rawValue: "", typeName: "" }; } return item.label; }); } /** Registers given ruleset and replaces the ruleset with its ID in the resulting object */ registerRuleset(rulesetOrId) { if (typeof rulesetOrId === "object") { const rulesetWithNativeId = { ...rulesetOrId, id: this.getRulesetId(rulesetOrId) }; return this.rulesets.add(rulesetWithNativeId).id; } return rulesetOrId; } /** @internal */ getRulesetId(rulesetOrId) { return getRulesetIdObject(rulesetOrId).uniqueId; } async request(params) { this.onUsed.raiseEvent(); const { requestId, imodel, unitSystem, diagnostics: requestDiagnostics, cancelEvent, ...strippedParams } = params; const imodelAddon = this.getNativePlatform().getImodelAddon(imodel); const response = await withOptionalDiagnostics([this._diagnosticsOptions, requestDiagnostics], async (diagnosticsOptions) => { const nativeRequestParams = { requestId, params: { unitSystem: toOptionalNativeUnitSystem(unitSystem ?? this.activeUnitSystem), ...strippedParams, ...(diagnosticsOptions ? { diagnostics: diagnosticsOptions } : undefined), }, }; return this.getNativePlatform().handleRequest(imodelAddon, JSON.stringify(nativeRequestParams), cancelEvent); }); return response.result; } } exports.PresentationManagerDetail = PresentationManagerDetail; async function withOptionalDiagnostics(diagnosticsOptions, nativePlatformRequestHandler) { const contexts = diagnosticsOptions.map((d) => d?.requestContextSupplier?.()); const combinedOptions = (0, Utils_js_1.combineDiagnosticsOptions)(...diagnosticsOptions); let responseDiagnostics; try { const response = await nativePlatformRequestHandler(combinedOptions); responseDiagnostics = response.diagnostics; return response; } catch (e) { if (e instanceof NativePlatform_js_1.PresentationNativePlatformResponseError) { responseDiagnostics = e.diagnostics; } throw e; /* c8 ignore next */ } finally { if (responseDiagnostics) { const diagnostics = { logs: [responseDiagnostics] }; diagnosticsOptions.forEach((options, i) => { options && (0, Utils_js_1.reportDiagnostics)(diagnostics, options, contexts[i]); }); } } } function setupRulesets(nativePlatform, supplementalRulesetDirectories, primaryRulesetDirectories) { nativePlatform.addRuleset(JSON.stringify(ElementProperties_PresentationRuleSet_json_1.default)); nativePlatform.registerSupplementalRuleset(JSON.stringify(BisCore_PresentationRuleSet_json_1.default)); nativePlatform.registerSupplementalRuleset(JSON.stringify(Functional_PresentationRuleSet_json_1.default)); nativePlatform.setupSupplementalRulesetDirectories(collateAssetDirectories(supplementalRulesetDirectories)); nativePlatform.setupRulesetDirectories(collateAssetDirectories(primaryRulesetDirectories)); } /** @internal */ function getRulesetIdObject(rulesetOrId) { if (typeof rulesetOrId === "object") { if (core_backend_1.IpcHost.isValid) { // in case of native apps we don't want to enforce ruleset id uniqueness as ruleset variables // are stored on a backend and creating new id will lose those variables return { uniqueId: rulesetOrId.id, parts: { id: rulesetOrId.id }, }; } const hashedId = hash.MD5(rulesetOrId); return { uniqueId: `${rulesetOrId.id}-${hashedId}`, parts: { id: rulesetOrId.id, hash: hashedId, }, }; } return { uniqueId: rulesetOrId, parts: { id: rulesetOrId } }; } /** @internal */ function getKeysForContentRequest(keys, classInstanceKeysProcessor) { const result = { instanceKeys: [], nodeKeys: [], }; const classInstancesMap = new Map(); keys.forEach((key) => { if (presentation_common_1.Key.isNodeKey(key)) { result.nodeKeys.push(key); } if (presentation_common_1.Key.isInstanceKey(key)) { addInstanceKey(classInstancesMap, key); } }); if (classInstanceKeysProcessor) { classInstanceKeysProcessor(classInstancesMap); } for (const entry of classInstancesMap) { if (entry[1].size > 0) { result.instanceKeys.push([entry["0"], [...entry[1]]]); } } return result; } /** @internal */ function bisElementInstanceKeysProcessor(imodel, classInstancesMap) { const elementClassName = "BisCore:Element"; const elementIds = classInstancesMap.get(elementClassName); if (elementIds) { const deleteElementIds = new Array(); elementIds.forEach((elementId) => { const concreteKey = (0, Utils_js_1.getElementKey)(imodel, elementId); if (concreteKey && concreteKey.className !== elementClassName) { deleteElementIds.push(elementId); addInstanceKey(classInstancesMap, { className: concreteKey.className, id: elementId }); } }); for (const id of deleteElementIds) { elementIds.delete(id); } } } function addInstanceKey(classInstancesMap, key) { let set = classInstancesMap.get(key.className); if (!set) { set = new Set(); classInstancesMap.set(key.className, set); } set.add(key.id); } function createNativePlatform(id, workerThreadsCount, updateCallback, caching, // eslint-disable-next-line @typescript-eslint/no-deprecated defaultFormats, useMmap) { return new ((0, NativePlatform_js_1.createDefaultNativePlatform)({ id, taskAllocationsMap: { [Number.MAX_SAFE_INTEGER]: workerThreadsCount }, updateCallback, cacheConfig: createCacheConfig(caching?.hierarchies), contentCacheSize: caching?.content?.size, workerConnectionCacheSize: caching?.workerConnectionCacheSize, defaultFormats: toNativeUnitFormatsMap(defaultFormats), useMmap, }))(); function createCacheConfig(config) { switch (config?.mode) { case PresentationManager_js_1.HierarchyCacheMode.Disk: return { ...config, directory: normalizeDirectory(config.directory) }; case PresentationManager_js_1.HierarchyCacheMode.Hybrid: return { ...config, disk: config.disk ? { ...config.disk, directory: normalizeDirectory(config.disk.directory) } : undefined, }; case PresentationManager_js_1.HierarchyCacheMode.Memory: return config; default: return { mode: PresentationManager_js_1.HierarchyCacheMode.Disk, directory: "" }; } } function normalizeDirectory(directory) { return directory ? path.resolve(directory) : ""; } // eslint-disable-next-line @typescript-eslint/no-deprecated function toNativeUnitFormatsMap(map) { if (!map) { return undefined; } const nativeFormatsMap = {}; Object.entries(map).forEach(([phenomenon, formats]) => { nativeFormatsMap[phenomenon] = (Array.isArray(formats) ? formats : [formats]).map((unitSystemsFormat) => ({ unitSystems: unitSystemsFormat.unitSystems.map(toNativeUnitSystem), format: unitSystemsFormat.format, })); }); return nativeFormatsMap; } } function toOptionalNativeUnitSystem(unitSystem) { return unitSystem ? toNativeUnitSystem(unitSystem) : undefined; } function toNativeUnitSystem(unitSystem) { switch (unitSystem) { case "imperial": return NativePlatform_js_1.NativePresentationUnitSystem.BritishImperial; case "metric": return NativePlatform_js_1.NativePresentationUnitSystem.Metric; case "usCustomary": return NativePlatform_js_1.NativePresentationUnitSystem.UsCustomary; case "usSurvey": return NativePlatform_js_1.NativePresentationUnitSystem.UsSurvey; } } function collateAssetDirectories(dirs) { return [...new Set(dirs)]; } const createContentDescriptorOverrides = (descriptorOrOverrides) => { if (descriptorOrOverrides instanceof presentation_common_1.Descriptor) { return descriptorOrOverrides.createDescriptorOverrides(); } return descriptorOrOverrides; }; function parseUpdateInfo(info) { if (info === undefined) { return undefined; } const parsedInfo = {}; for (const fileName in info) { /* c8 ignore next 3 */ if (!info.hasOwnProperty(fileName)) { continue; } const imodelDb = core_backend_1.IModelDb.findByFilename(fileName); if (!imodelDb) { core_bentley_1.Logger.logError(BackendLoggerCategory_js_1.PresentationBackendLoggerCategory.PresentationManager, `Update records IModelDb not found with path ${fileName}`); continue; } parsedInfo[imodelDb.getRpcProps().key] = info[fileName]; } return Object.keys(parsedInfo).length > 0 ? parsedInfo : undefined; } /** @internal */ function ipcUpdatesHandler(info) { const parsed = parseUpdateInfo(info); if (parsed) { core_backend_1.IpcHost.send(internal_1.PresentationIpcEvents.Update, parsed); } } /** @internal */ /* c8 ignore next */ function noopUpdatesHandler(_info) { } //# sourceMappingURL=PresentationManagerDetail.js.map