@itwin/presentation-backend
Version:
Backend of iTwin.js Presentation library
487 lines • 22.7 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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
;