@itwin/presentation-testing
Version:
Testing utilities for iTwin.js Presentation library
206 lines • 9.35 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Content
*/
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose, inner;
if (async) {
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose];
}
if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose];
if (async) inner = dispose;
}
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
env.stack.push({ value: value, dispose: dispose, async: async });
}
else if (async) {
env.stack.push({ async: true });
}
return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
return function (env) {
function fail(e) {
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
env.hasError = true;
}
var r, s = 0;
function next() {
while (r = env.stack.pop()) {
try {
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
if (r.dispose) {
var result = r.dispose.call(r.value);
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
}
else s |= 1;
}
catch (e) {
fail(e);
}
}
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
if (env.hasError) throw env.error;
}
return next();
};
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
import { QueryRowFormat } from "@itwin/core-common";
import { DefaultContentDisplayTypes, KeySet, traverseContent, Value, } from "@itwin/presentation-common";
import { ContentDataProvider, PropertyRecordsBuilder } from "@itwin/presentation-components";
import { Presentation } from "@itwin/presentation-frontend";
import { safeDispose } from "./Helpers.js";
/**
* A class that constructs content from specified imodel and ruleset.
* @public
*/
export class ContentBuilder {
_iModel;
_dataProvider;
_decimalPrecision;
/**
* Constructor
* @param iModel
* @param dataProvider
*/
constructor(props) {
this._iModel = props.imodel;
this._dataProvider = props.dataProvider;
this._decimalPrecision = props.decimalPrecision;
}
async doCreateContent(rulesetId, instanceKeys, displayType) {
const dataProvider = this._dataProvider ? this._dataProvider : new ContentDataProvider({ imodel: this._iModel, ruleset: rulesetId, displayType });
dataProvider.keys = new KeySet(instanceKeys);
const content = await dataProvider.getContent();
if (!content) {
return [];
}
const accumulator = new PropertyRecordsAccumulator(this._decimalPrecision);
traverseContent(accumulator, content);
return accumulator.records;
}
/**
* Create a list of property records using the supplied presentation ruleset.
* @param rulesetOrId Either a [Ruleset]($presentation-common) object or a ruleset id.
* @param instanceKeys Keys of instances that should be queried.
* @param displayType Type of content container display. For example:
* "PropertyPane", "Grid", "List" etc.
*/
async createContent(rulesetOrId, instanceKeys, displayType = DefaultContentDisplayTypes.PropertyPane) {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
if (typeof rulesetOrId === "string") {
return this.doCreateContent(rulesetOrId, instanceKeys, displayType);
}
const ruleset = await Presentation.presentation.rulesets().add(rulesetOrId);
const _ = __addDisposableResource(env_1, { [Symbol.dispose]: () => safeDispose(ruleset) }, false);
return await this.doCreateContent(ruleset.id, instanceKeys, displayType);
}
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
finally {
__disposeResources(env_1);
}
}
async getECClassNames() {
const reader = this._iModel.createQueryReader(`
SELECT s.Name schemaName, c.Name className FROM meta.ECClassDef c
INNER JOIN meta.ECSchemaDef s ON c.Schema.id = s.ECInstanceId
WHERE c.Modifier <> 1 AND c.Type = 0
ORDER BY s.Name, c.Name
`, undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames });
return reader.toArray();
}
async createContentForClasses(rulesetOrId, limitInstances, displayType) {
const classNameEntries = await this.getECClassNames();
const contents = [];
for (const nameEntry of classNameEntries) {
const reader = this._iModel.createQueryReader(`
SELECT ECInstanceId FROM ONLY "${nameEntry.schemaName}"."${nameEntry.className}"
ORDER BY ECInstanceId
`, undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames, limit: { count: limitInstances ? 1 : 4000 } });
const instanceIds = await reader.toArray();
if (!instanceIds.length) {
continue;
}
const instanceKeys = instanceIds.map((idEntry) => ({ className: `${nameEntry.schemaName}:${nameEntry.className}`, id: idEntry }));
contents.push({
className: `${nameEntry.schemaName}:${nameEntry.className}`,
records: await this.createContent(rulesetOrId, instanceKeys, displayType),
});
}
return contents;
}
/**
* Create a list of grouped property records using the supplied presentation ruleset.
* Each group includes all of the class instances.
* @param rulesetOrId Either a [Ruleset]($presentation-common) object or a ruleset id.
* @param displayType Type of content container display. For example:
* "PropertyPane", "Grid", "List" etc.
* @deprecated in 3.x. This method turned out to be useless as it creates content for too many instances. Should use [[createContent]] instead.
*/
async createContentForAllInstances(rulesetOrId, displayType = DefaultContentDisplayTypes.PropertyPane) {
return this.createContentForClasses(rulesetOrId, false, displayType);
}
/**
* Create a list of grouped property records using the supplied presentation ruleset.
* Each group includes at most one class instance.
* @param rulesetOrId Either a [Ruleset]($presentation-common) object or a ruleset id.
* @param displayType Type of content container display. For example:
* "PropertyPane", "Grid", "List" etc.
* @deprecated in 3.x. This method turned out to be useless as it creates content for too many instances. Should use [[createContent]] instead.
*/
async createContentForInstancePerClass(rulesetOrId, displayType = DefaultContentDisplayTypes.PropertyPane) {
return this.createContentForClasses(rulesetOrId, true, displayType);
}
}
class PropertyRecordsAccumulator extends PropertyRecordsBuilder {
_records = [];
_decimalPrecision;
constructor(decimalPrecision) {
super((record) => {
this._records.push(record);
});
this._decimalPrecision = decimalPrecision;
}
get records() {
return this._records;
}
processRawValue(value) {
if (this._decimalPrecision === undefined) {
return value;
}
if (typeof value === "number") {
return +Number(value).toFixed(this._decimalPrecision);
}
if (Value.isArray(value)) {
return value.map((item) => this.processRawValue(item));
}
if (Value.isMap(value)) {
const res = {};
Object.entries(value).forEach(([key, memberValue]) => {
res[key] = this.processRawValue(memberValue);
});
return res;
}
return value;
}
processPrimitiveValue(props) {
super.processPrimitiveValue({ ...props, rawValue: this.processRawValue(props.rawValue) });
}
}
//# sourceMappingURL=ContentBuilder.js.map