Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

presentation/default units #657

Merged
merged 14 commits into from
Feb 5, 2021
Merged
8 changes: 8 additions & 0 deletions common/api/presentation-backend.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { DistinctValuesRequestOptions } from '@bentley/presentation-common';
import { EventSink } from '@bentley/imodeljs-backend';
import { ExtendedContentRequestOptions } from '@bentley/presentation-common';
import { ExtendedHierarchyRequestOptions } from '@bentley/presentation-common';
import { FormatProps } from '@bentley/imodeljs-quantity';
import { HierarchyRequestOptions } from '@bentley/presentation-common';
import { Id64String } from '@bentley/bentleyjs-core';
import { IDisposable } from '@bentley/bentleyjs-core';
Expand Down Expand Up @@ -254,6 +255,13 @@ export interface PresentationManagerProps {
cacheConfig?: HierarchyCacheConfig;
// @alpha (undocumented)
contentCacheSize?: number;
// @alpha
defaultFormats?: {
[phenomenon: string]: {
unitSystems: string[];
grigasp marked this conversation as resolved.
Show resolved Hide resolved
format: FormatProps;
};
};
enableSchemasPreload?: boolean;
// @internal (undocumented)
eventSink?: EventSink;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@bentley/presentation-backend",
"comment": "Added a way to set default property formatting",
"type": "none"
}
],
"packageName": "@bentley/presentation-backend",
"email": "30312645+aurislt7@users.noreply.github.com"
}
2 changes: 1 addition & 1 deletion common/config/rush/browser-approved-packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
},
{
"name": "@bentley/imodeljs-quantity",
"allowedCategories": [ "common", "extensions", "frontend", "integration-testing", "internal" ]
"allowedCategories": [ "backend", "common", "extensions", "frontend", "integration-testing", "internal" ]
},
{
"name": "@bentley/itwin-client",
Expand Down
4 changes: 2 additions & 2 deletions full-stack-tests/presentation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"@bentley/imodeljs-backend": "2.12.0-dev.10",
"@bentley/imodeljs-frontend": "2.12.0-dev.10",
"@bentley/oidc-signin-tool": "2.12.0-dev.10",
"@bentley/presentation-common": "2.12.0-dev.10",
"@bentley/presentation-backend": "2.12.0-dev.10",
"@bentley/presentation-common": "2.12.0-dev.10",
"@bentley/presentation-frontend": "2.12.0-dev.10",
"@bentley/presentation-components": "2.12.0-dev.10",
"@bentley/presentation-testing": "2.12.0-dev.10",
Expand Down Expand Up @@ -90,4 +90,4 @@
],
"extends": "plugin:@bentley/imodeljs-recommended"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*---------------------------------------------------------------------------------------------
grigasp marked this conversation as resolved.
Show resolved Hide resolved
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { expect } from "chai";
import { ClientRequestContext, Guid } from "@bentley/bentleyjs-core";
import { IModelDb, SnapshotDb } from "@bentley/imodeljs-backend";
import { PresentationManager } from "@bentley/presentation-backend";
import {
ContentSpecificationTypes, DisplayValuesArray, DisplayValuesMap, KeySet, PresentationUnitSystem, Ruleset, RuleTypes,
} from "@bentley/presentation-common";
import { findFieldByLabel } from "../frontend/Content.test";
grigasp marked this conversation as resolved.
Show resolved Hide resolved
import { initialize, terminate } from "../IntegrationTests";

describe("PresentationManager", () => {

let imodel: IModelDb;
before(async () => {
await initialize();
imodel = SnapshotDb.openFile("assets/datasets/Properties_60InstancesWithUrl2.ibim");
expect(imodel).is.not.null;
});

after(async () => {
imodel.close();
await terminate();
});

describe("Property value formatting", () => {

const ruleset: Ruleset = {
id: Guid.createValue(),
rules: [{
ruleType: RuleTypes.Content,
specifications: [{ specType: ContentSpecificationTypes.SelectedNodeInstances }],
}],
};
const keys = KeySet.fromJSON({ instanceKeys: [["Generic:PhysicalObject", ["0x74"]]], nodeKeys: [] });

it("formats property with default kind of quantity format when it doesn't have format for requested unit system", async () => {
const manager: PresentationManager = new PresentationManager();
const descriptor = await manager.getContentDescriptor({
imodel,
rulesetOrId: ruleset,
keys,
displayType: "Grid",
requestContext: new ClientRequestContext(),
unitSystem: PresentationUnitSystem.BritishImperial,
});
expect(descriptor).to.not.be.undefined;
const field = findFieldByLabel(descriptor!.fields, "cm2")!;
expect(field).not.to.be.undefined;
const content = await manager.getContent({ imodel, rulesetOrId: ruleset, keys, descriptor: descriptor!, requestContext: new ClientRequestContext(), unitSystem: PresentationUnitSystem.BritishImperial });
const displayValues = content!.contentSet[0].displayValues.rc_generic_PhysicalObject_ncc_MyProp_areaElementAspect as DisplayValuesArray;
expect(displayValues.length).is.eq(1);
expect(((displayValues[0] as DisplayValuesMap).displayValues as DisplayValuesMap)[field.name]!).to.eq("150.1235 cm²");
});

it("formats property using default format when it doesn't have format for requested unit system", async () => {
const formatProps = {
composite:
{includeZero:true,
spacer:" ",
units:[{label:"ft²",name:"SQ_FT"}],
},
formatTraits:"KeepSingleZero|KeepDecimalPoint|ShowUnitLabel",
precision:4,
type:"Decimal",
uomSeparator:""};

const map = {
// eslint-disable-next-line @typescript-eslint/naming-convention
AREA: {unitSystems: [PresentationUnitSystem.BritishImperial], format: formatProps},
};

const manager: PresentationManager = new PresentationManager({defaultFormats: map});
const descriptor = await manager.getContentDescriptor({
imodel,
rulesetOrId: ruleset,
keys,
displayType: "Grid",
requestContext: new ClientRequestContext(),
unitSystem: PresentationUnitSystem.BritishImperial,
});
expect(descriptor).to.not.be.undefined;
const field = findFieldByLabel(descriptor!.fields, "cm2")!;
expect(field).not.to.be.undefined;
const content = await manager.getContent({ imodel, rulesetOrId: ruleset, keys, descriptor: descriptor!, requestContext: new ClientRequestContext(), unitSystem: PresentationUnitSystem.BritishImperial });
const displayValues = content!.contentSet[0].displayValues.rc_generic_PhysicalObject_ncc_MyProp_areaElementAspect as DisplayValuesArray;
expect(displayValues.length).is.eq(1);
expect(((displayValues[0] as DisplayValuesMap).displayValues as DisplayValuesMap)[field.name]!).to.eq("0.1616 ft²");
});

grigasp marked this conversation as resolved.
Show resolved Hide resolved
it("formats property using provided format when it has provided format and default format for requested unit system", async () => {
const formatProps = {
composite:
{includeZero:true,
spacer:" ",
units:[{label:"ft²",name:"SQ_FT"}],
},
formatTraits:"KeepSingleZero|KeepDecimalPoint|ShowUnitLabel",
precision:4,
type:"Decimal",
uomSeparator:""};

const map = {
// eslint-disable-next-line @typescript-eslint/naming-convention
AREA: {unitSystems: [PresentationUnitSystem.Metric], format: formatProps},
};

const manager: PresentationManager = new PresentationManager({defaultFormats: map});
const descriptor = await manager.getContentDescriptor({
imodel,
rulesetOrId: ruleset,
keys,
displayType: "Grid",
requestContext: new ClientRequestContext(),
unitSystem: PresentationUnitSystem.Metric,
});
expect(descriptor).to.not.be.undefined;
const field = findFieldByLabel(descriptor!.fields, "cm2")!;
expect(field).not.to.be.undefined;
const content = await manager.getContent({ imodel, rulesetOrId: ruleset, keys, descriptor: descriptor!, requestContext: new ClientRequestContext(), unitSystem: PresentationUnitSystem.Metric });
const displayValues = content!.contentSet[0].displayValues.rc_generic_PhysicalObject_ncc_MyProp_areaElementAspect as DisplayValuesArray;
expect(displayValues.length).is.eq(1);
expect(((displayValues[0] as DisplayValuesMap).displayValues as DisplayValuesMap)[field.name]!).to.eq("150.1235 cm²");
grigasp marked this conversation as resolved.
Show resolved Hide resolved
});
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ class ECClassHierarchy {
}
}

function findFieldByLabel(fields: Field[], label: string, allFields?: Field[]): Field | undefined {
export function findFieldByLabel(fields: Field[], label: string, allFields?: Field[]): Field | undefined {
const isTopLevel = (undefined === allFields);
if (!allFields)
allFields = new Array<Field>();
Expand Down
4 changes: 3 additions & 1 deletion presentation/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@bentley/bentleyjs-core": "^2.12.0-dev.10",
"@bentley/imodeljs-backend": "^2.12.0-dev.10",
"@bentley/imodeljs-common": "^2.12.0-dev.10",
"@bentley/imodeljs-quantity": "^2.12.0-dev.10",
"@bentley/presentation-common": "^2.12.0-dev.10"
},
"devDependencies": {
Expand All @@ -48,6 +49,7 @@
"@bentley/eslint-plugin": "2.12.0-dev.10",
"@bentley/imodeljs-backend": "2.12.0-dev.10",
"@bentley/imodeljs-common": "2.12.0-dev.10",
"@bentley/imodeljs-quantity": "2.12.0-dev.10",
"@bentley/presentation-common": "2.12.0-dev.10",
"@types/chai": "^4.1.4",
"@types/chai-as-promised": "^7",
Expand Down Expand Up @@ -96,4 +98,4 @@
],
"extends": "plugin:@bentley/imodeljs-recommended"
}
}
}
20 changes: 18 additions & 2 deletions presentation/backend/src/presentation-backend/NativePlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { IModelDb, IModelHost, IModelJsNative } from "@bentley/imodeljs-backend"
import {
DiagnosticsScopeLogs, PresentationError, PresentationStatus, UpdateInfoJSON, VariableValueJSON, VariableValueTypes,
} from "@bentley/presentation-common";
import { HierarchyCacheMode, PresentationManagerMode } from "./PresentationManager";
import { HierarchyCacheMode, PresentationManagerMode, UnitSystemFormat } from "./PresentationManager";

/** @internal */
export enum NativePlatformRequestTypes {
Expand Down Expand Up @@ -68,6 +68,7 @@ export interface DefaultNativePlatformProps {
isChangeTrackingEnabled: boolean;
cacheConfig?: IModelJsNative.ECPresentationHierarchyCacheConfig;
contentCacheSize?: number;
defaultFormats?: { [phenomenon: string]: UnitSystemFormat };
}

/** @internal */
Expand All @@ -79,7 +80,8 @@ export const createDefaultNativePlatform = (props: DefaultNativePlatformProps):
public constructor() {
const mode = (props.mode === PresentationManagerMode.ReadOnly) ? IModelJsNative.ECPresentationManagerMode.ReadOnly : IModelJsNative.ECPresentationManagerMode.ReadWrite;
const cacheConfig = props.cacheConfig ?? { mode: HierarchyCacheMode.Disk, directory: "" };
this._nativeAddon = new IModelHost.platform.ECPresentationManager({ ...props, mode, cacheConfig });
const defaultFormats = props.defaultFormats ? this.getSerializedDefaultFormatsMap(props.defaultFormats) : {};
this._nativeAddon = new IModelHost.platform.ECPresentationManager({ ...props, mode, cacheConfig, defaultFormats});
grigasp marked this conversation as resolved.
Show resolved Hide resolved
}
private getStatus(responseStatus: IModelJsNative.ECPresentationStatus): PresentationStatus {
switch (responseStatus) {
Expand All @@ -88,6 +90,20 @@ export const createDefaultNativePlatform = (props: DefaultNativePlatformProps):
default: return PresentationStatus.Error;
}
}
private getSerializedDefaultFormatsMap(defaultMap: { [phenomenon: string]: UnitSystemFormat }){
const res: {
[phenomenon: string]: {
unitSystems: string[];
serializedFormat: string;
};
} = {};
Object.keys(defaultMap).forEach((key) => {
const value = defaultMap[key];
res[key] = {unitSystems: value.unitSystems, serializedFormat: JSON.stringify(value.format)};
});

return res;
}
private createSuccessResponse<T>(response: IModelJsNative.ECPresentationManagerResponse<T>): NativePlatformResponse<T> {
const retValue: NativePlatformResponse<T> = { result: response.result! };
if (response.diagnostics)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as hash from "object-hash";
import * as path from "path";
import { ClientRequestContext, Id64String, Logger } from "@bentley/bentleyjs-core";
import { BriefcaseDb, EventSink, IModelDb, IModelHost, IModelJsNative } from "@bentley/imodeljs-backend";
import { FormatProps } from "@bentley/imodeljs-quantity";
import {
Content, ContentDescriptorRequestOptions, ContentFlags, ContentRequestOptions, DefaultContentDisplayTypes, Descriptor, DescriptorOverrides,
DisplayLabelRequestOptions, DisplayLabelsRequestOptions, DisplayValueGroup, DistinctValuesRequestOptions, ExtendedContentRequestOptions,
Expand Down Expand Up @@ -112,6 +113,17 @@ export interface HybridCacheConfig extends HierarchyCacheConfigBase {
disk?: DiskHierarchyCacheConfig;
}

/**
* Map for setting up format to a number of UnitSystems.
* It is used in [[PresentationManager]] to setup DefaultFormats.
* DefaultFormats are constructed by mapping a phenomenon with UnitSystemFormat.
grigasp marked this conversation as resolved.
Show resolved Hide resolved
* @alpha
*/
export interface UnitSystemFormat {
unitSystems: PresentationUnitSystem[];
format: FormatProps;
};

/**
* Properties that can be used to configure [[PresentationManager]]
* @public
Expand Down Expand Up @@ -246,6 +258,13 @@ export interface PresentationManagerProps {
/** @alpha */
contentCacheSize?: number;

/**
* A map for setting up default formats.
grigasp marked this conversation as resolved.
Show resolved Hide resolved
* @alpha */
defaultFormats?: {
[phenomenon: string]: UnitSystemFormat;
};

/**
* An identifier which helps separate multiple presentation managers. It's
* mostly useful in tests where multiple presentation managers can co-exist
Expand Down Expand Up @@ -307,6 +326,7 @@ export class PresentationManager {
isChangeTrackingEnabled,
cacheConfig: createCacheConfig(this._props.cacheConfig),
contentCacheSize: this._props.contentCacheSize,
defaultFormats: this._props.defaultFormats,
});
this._nativePlatform = new nativePlatformImpl();
}
Expand Down
6 changes: 6 additions & 0 deletions presentation/backend/src/test/PresentationManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ describe("PresentationManager", () => {
isChangeTrackingEnabled: false,
cacheConfig: { mode: HierarchyCacheMode.Disk, directory: "" },
contentCacheSize: undefined,
defaultFormats: {},
});
});
});
Expand Down Expand Up @@ -127,6 +128,7 @@ describe("PresentationManager", () => {
isChangeTrackingEnabled: true,
cacheConfig: expectedCacheConfig,
contentCacheSize: 999,
defaultFormats: {},
});
});
});
Expand All @@ -143,6 +145,7 @@ describe("PresentationManager", () => {
isChangeTrackingEnabled: false,
cacheConfig: { mode: HierarchyCacheMode.Disk, directory: "" },
contentCacheSize: undefined,
defaultFormats: {},
});
});
constructorSpy.resetHistory();
Expand All @@ -161,6 +164,7 @@ describe("PresentationManager", () => {
isChangeTrackingEnabled: false,
cacheConfig: expectedConfig,
contentCacheSize: undefined,
defaultFormats: {},
});
});
});
Expand All @@ -177,6 +181,7 @@ describe("PresentationManager", () => {
isChangeTrackingEnabled: false,
cacheConfig: { mode: HierarchyCacheMode.Hybrid, disk: undefined },
contentCacheSize: undefined,
defaultFormats: {},
});
});
constructorSpy.resetHistory();
Expand All @@ -200,6 +205,7 @@ describe("PresentationManager", () => {
isChangeTrackingEnabled: false,
cacheConfig: expectedConfig,
contentCacheSize: undefined,
defaultFormats: {},
});
});
});
Expand Down